-
Notifications
You must be signed in to change notification settings - Fork 63
/
Copy pathatfork.c
130 lines (118 loc) · 3.34 KB
/
atfork.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
* atfork.c
*
* Demonstrate the use of "fork handlers" to protect data
* invariants across a fork.
*/
#include <sys/types.h>
#include <pthread.h>
#include <sys/wait.h>
#include "errors.h"
pid_t self_pid; /* pid of current process */
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
/*
* This routine will be called prior to executing the fork,
* within the parent process.
*/
void fork_prepare (void)
{
int status;
/*
* Lock the mutex in the parent before creating the child,
* to ensure that no other thread can lock it (or change any
* associated shared state) until after the fork completes.
*/
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock in prepare handler");
}
/*
* This routine will be called after executing the fork, within
* the parent process
*/
void fork_parent (void)
{
int status;
/*
* Unlock the mutex in the parent after the child has been created.
*/
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock in parent handler");
}
/*
* This routine will be called after executing the fork, within
* the child process
*/
void fork_child (void)
{
int status;
/*
* Update the file scope "self_pid" within the child process, and unlock
* the mutex.
*/
self_pid = getpid ();
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock in child handler");
}
/*
* Thread start routine, which will fork a new child process.
*/
void *thread_routine (void *arg)
{
pid_t child_pid;
int status;
child_pid = fork ();
if (child_pid == (pid_t)-1)
errno_abort ("Fork");
/*
* Lock the mutex -- without the atfork handlers, the mutex will remain
* locked in the child process and this lock attempt will hang (or fail
* with EDEADLK) in the child.
*/
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock in child");
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock in child");
printf ("After fork: %d (%d)\n", child_pid, self_pid);
if (child_pid != 0) {
if ((pid_t)-1 == waitpid (child_pid, (int*)0, 0))
errno_abort ("Wait for child");
}
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t fork_thread;
int atfork_flag = 1;
int status;
if (argc > 1)
atfork_flag = atoi (argv[1]);
if (atfork_flag) {
status = pthread_atfork (fork_prepare, fork_parent, fork_child);
if (status != 0)
err_abort (status, "Register fork handlers");
}
self_pid = getpid ();
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
/*
* Create a thread while the mutex is locked. It will fork a process,
* which (without atfork handlers) will run with the mutex locked.
*/
status = pthread_create (&fork_thread, NULL, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
sleep (5);
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
status = pthread_join (fork_thread, NULL);
if (status != 0)
err_abort (status, "Join thread");
return 0;
}