initial load of upstream version 1.06.32
[xmlrpc-c] / lib / abyss / src / thread_fork.c
1 #include <sys/types.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <wait.h>
7 #include <signal.h>
8
9 #include "xmlrpc_config.h"
10 #include "xmlrpc-c/string_int.h"
11 #include "xmlrpc-c/abyss.h"
12
13 #include "mallocvar.h"
14 #include "thread.h"
15
16
17 static void
18 blockSignalClass(int        const signalClass,
19                  sigset_t * const oldBlockedSetP) {
20
21     sigset_t newBlockedSet;
22
23     sigemptyset(&newBlockedSet);
24     sigaddset(&newBlockedSet, signalClass);
25
26     sigprocmask(SIG_BLOCK, &newBlockedSet, oldBlockedSetP);
27 }
28
29
30
31 struct abyss_thread {
32     struct abyss_thread * nextInPoolP;
33     TThreadDoneFn * threadDone;
34     void * userHandle;
35     pid_t pid;
36     abyss_bool useSigchld;
37         /* This means that user is going to call ThreadHandleSigchld()
38            when it gets a death of a child signal for this process.  If
39            false, he's going to leave us in the dark, so we'll have to
40            poll to know if the process is dead or not.
41         */
42 };
43
44
45 /* Because signals are global, we need this global variable in order for
46    the signal handler to figure out to what thread the signal belongs.
47 */
48
49 /* We use a singly linked list.  Every time we access it, we have to run
50    the whole chain.  To make this scale up, we should replace it with
51    a doubly linked list and some kind of index by PID.
52
53    But large scale systems probably aren't using fork threads anyway.
54 */
55
56 static struct {
57     struct abyss_thread * firstP;
58 } ThreadPool;
59    
60
61
62 void
63 ThreadPoolInit(void) {
64
65     ThreadPool.firstP = NULL;
66 }
67
68
69
70 static struct abyss_thread *
71 findThread(pid_t const pid) {
72
73     struct abyss_thread * p;
74
75     for (p = ThreadPool.firstP; p && p->pid != pid; p = p->nextInPoolP);
76     
77     return p;
78 }
79
80
81
82 static void
83 addToPool(struct abyss_thread * const threadP) {
84
85     if (ThreadPool.firstP == NULL)
86         ThreadPool.firstP = threadP;
87     else {
88         struct abyss_thread * p;
89
90         for (p = ThreadPool.firstP; p->nextInPoolP; p = p->nextInPoolP);
91
92         /* p points to the last thread in the list */
93
94         p->nextInPoolP = threadP;
95     }
96 }
97
98
99
100 static void
101 removeFromPool(struct abyss_thread * const threadP) {
102
103     if (threadP == ThreadPool.firstP)
104         ThreadPool.firstP = threadP->nextInPoolP;
105     else {
106         struct abyss_thread * p;
107
108         for (p = ThreadPool.firstP;
109              p && p->nextInPoolP != threadP;
110              p = p->nextInPoolP);
111         
112         if (p)
113             /* p points to thread right before the one we want to remove */
114             p->nextInPoolP = threadP->nextInPoolP;
115     }
116 }
117
118
119
120 void
121 ThreadHandleSigchld(pid_t const pid) {
122 /*----------------------------------------------------------------------------
123    Handle a death of a child signal for process 'pid', which may be one
124    of our threads.
125 -----------------------------------------------------------------------------*/
126     struct abyss_thread * const threadP = findThread(pid);
127
128     if (threadP) {
129         if (threadP->threadDone)
130             threadP->threadDone(threadP->userHandle);
131         threadP->pid = 0;
132     }
133     /* Note that threadDone might free *threadP */
134 }
135
136
137
138 void
139 ThreadUpdateStatus(TThread * const threadP) {
140
141     if (!threadP->useSigchld) {
142         if (threadP->pid) {
143             if (kill(threadP->pid, 0) != 0) {
144                 if (threadP->threadDone)
145                     threadP->threadDone(threadP->userHandle);
146                 threadP->pid = 0;
147             }
148         }
149     }
150 }
151
152
153
154 void
155 ThreadCreate(TThread **      const threadPP,
156              void *          const userHandle,
157              TThreadProc   * const func,
158              TThreadDoneFn * const threadDone,
159              abyss_bool      const useSigchld,
160              const char **   const errorP) {
161     
162     TThread * threadP;
163
164     MALLOCVAR(threadP);
165     if (threadP == NULL)
166         xmlrpc_asprintf(errorP,
167                         "Can't allocate memory for thread descriptor.");
168     else {
169         sigset_t oldBlockedSet;
170         pid_t rc;
171
172         threadP->nextInPoolP = NULL;
173         threadP->threadDone  = threadDone;
174         threadP->userHandle  = userHandle;
175         threadP->useSigchld  = useSigchld;
176         threadP->pid         = 0;
177
178         /* We have to be sure we don't get the SIGCHLD for this child's
179            death until the child is properly registered in the thread pool
180            so that the handler will know who he is.
181         */
182         blockSignalClass(SIGCHLD, &oldBlockedSet);
183
184         rc = fork();
185         
186         if (rc < 0)
187             xmlrpc_asprintf(errorP, "fork() failed, errno=%d (%s)",
188                             errno, strerror(errno));
189         else if (rc == 0) {
190             /* This is the child */
191             (*func)(userHandle);
192             exit(0);
193         } else {
194             /* This is the parent */
195             threadP->pid = rc;
196
197             addToPool(threadP);
198
199             sigprocmask(SIG_SETMASK, &oldBlockedSet, NULL);  /* restore */
200
201             *errorP = NULL;
202             *threadPP = threadP;
203         }
204         if (*errorP) {
205             removeFromPool(threadP);
206             free(threadP);
207         }
208     }
209 }
210
211
212
213 abyss_bool
214 ThreadRun(TThread * const threadP ATTR_UNUSED) {
215     return TRUE;    
216 }
217
218
219
220 abyss_bool
221 ThreadStop(TThread * const threadP ATTR_UNUSED) {
222     return TRUE;
223 }
224
225
226
227 abyss_bool
228 ThreadKill(TThread * const threadP ATTR_UNUSED) {
229     return TRUE;
230 }
231
232
233
234 void
235 ThreadWaitAndRelease(TThread * const threadP) {
236
237     if (threadP->pid) {
238         int exitStatus;
239
240         waitpid(threadP->pid, &exitStatus, 0);
241
242         threadP->threadDone(threadP->userHandle);
243
244         threadP->pid = 0;
245     }
246     ThreadRelease(threadP);
247 }
248
249
250
251 void
252 ThreadExit(int const retValue) {
253
254     /* Note that the OS will automatically send a SIGCHLD signal to
255        the parent process after we exit.  The handler for that signal
256        will run threadDone in parent's context.  Alternatively, if
257        the parent is set up for signals, the parent will eventually
258        poll for the existence of our PID and call threadDone when he
259        sees we've gone.
260     */
261
262     exit(retValue);
263 }
264
265
266
267 void
268 ThreadRelease(TThread * const threadP) {
269
270     removeFromPool(threadP);
271     free(threadP);
272 }
273
274
275
276 abyss_bool
277 ThreadForks(void) {
278
279     return TRUE;
280 }
281
282
283
284 /*********************************************************************
285 ** Mutex
286 *********************************************************************/
287
288 /* As two processes don't share memory, there is nothing to synchronize,
289    so locking is a no-op.
290 */
291
292 abyss_bool
293 MutexCreate(TMutex * const mutexP ATTR_UNUSED) {
294     return TRUE;
295 }
296
297
298
299 abyss_bool
300 MutexLock(TMutex * const mutexP ATTR_UNUSED) {
301     return TRUE;
302 }
303
304
305
306 abyss_bool
307 MutexUnlock(TMutex * const mutexP ATTR_UNUSED) {
308     return TRUE;
309 }
310
311
312
313 abyss_bool
314 MutexTryLock(TMutex * const mutexP ATTR_UNUSED) {
315     return TRUE;
316 }
317
318
319
320 void
321 MutexFree(TMutex * const mutexP ATTR_UNUSED) {
322
323 }