9 #include "xmlrpc_config.h"
10 #include "xmlrpc-c/string_int.h"
11 #include "xmlrpc-c/abyss.h"
13 #include "mallocvar.h"
18 blockSignalClass(int const signalClass,
19 sigset_t * const oldBlockedSetP) {
21 sigset_t newBlockedSet;
23 sigemptyset(&newBlockedSet);
24 sigaddset(&newBlockedSet, signalClass);
26 sigprocmask(SIG_BLOCK, &newBlockedSet, oldBlockedSetP);
32 struct abyss_thread * nextInPoolP;
33 TThreadDoneFn * threadDone;
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.
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.
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.
53 But large scale systems probably aren't using fork threads anyway.
57 struct abyss_thread * firstP;
63 ThreadPoolInit(void) {
65 ThreadPool.firstP = NULL;
70 static struct abyss_thread *
71 findThread(pid_t const pid) {
73 struct abyss_thread * p;
75 for (p = ThreadPool.firstP; p && p->pid != pid; p = p->nextInPoolP);
83 addToPool(struct abyss_thread * const threadP) {
85 if (ThreadPool.firstP == NULL)
86 ThreadPool.firstP = threadP;
88 struct abyss_thread * p;
90 for (p = ThreadPool.firstP; p->nextInPoolP; p = p->nextInPoolP);
92 /* p points to the last thread in the list */
94 p->nextInPoolP = threadP;
101 removeFromPool(struct abyss_thread * const threadP) {
103 if (threadP == ThreadPool.firstP)
104 ThreadPool.firstP = threadP->nextInPoolP;
106 struct abyss_thread * p;
108 for (p = ThreadPool.firstP;
109 p && p->nextInPoolP != threadP;
113 /* p points to thread right before the one we want to remove */
114 p->nextInPoolP = threadP->nextInPoolP;
121 ThreadHandleSigchld(pid_t const pid) {
122 /*----------------------------------------------------------------------------
123 Handle a death of a child signal for process 'pid', which may be one
125 -----------------------------------------------------------------------------*/
126 struct abyss_thread * const threadP = findThread(pid);
129 if (threadP->threadDone)
130 threadP->threadDone(threadP->userHandle);
133 /* Note that threadDone might free *threadP */
139 ThreadUpdateStatus(TThread * const threadP) {
141 if (!threadP->useSigchld) {
143 if (kill(threadP->pid, 0) != 0) {
144 if (threadP->threadDone)
145 threadP->threadDone(threadP->userHandle);
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) {
166 xmlrpc_asprintf(errorP,
167 "Can't allocate memory for thread descriptor.");
169 sigset_t oldBlockedSet;
172 threadP->nextInPoolP = NULL;
173 threadP->threadDone = threadDone;
174 threadP->userHandle = userHandle;
175 threadP->useSigchld = useSigchld;
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.
182 blockSignalClass(SIGCHLD, &oldBlockedSet);
187 xmlrpc_asprintf(errorP, "fork() failed, errno=%d (%s)",
188 errno, strerror(errno));
190 /* This is the child */
194 /* This is the parent */
199 sigprocmask(SIG_SETMASK, &oldBlockedSet, NULL); /* restore */
205 removeFromPool(threadP);
214 ThreadRun(TThread * const threadP ATTR_UNUSED) {
221 ThreadStop(TThread * const threadP ATTR_UNUSED) {
228 ThreadKill(TThread * const threadP ATTR_UNUSED) {
235 ThreadWaitAndRelease(TThread * const threadP) {
240 waitpid(threadP->pid, &exitStatus, 0);
242 threadP->threadDone(threadP->userHandle);
246 ThreadRelease(threadP);
252 ThreadExit(int const retValue) {
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
268 ThreadRelease(TThread * const threadP) {
270 removeFromPool(threadP);
284 /*********************************************************************
286 *********************************************************************/
288 /* As two processes don't share memory, there is nothing to synchronize,
289 so locking is a no-op.
293 MutexCreate(TMutex * const mutexP ATTR_UNUSED) {
300 MutexLock(TMutex * const mutexP ATTR_UNUSED) {
307 MutexUnlock(TMutex * const mutexP ATTR_UNUSED) {
314 MutexTryLock(TMutex * const mutexP ATTR_UNUSED) {
321 MutexFree(TMutex * const mutexP ATTR_UNUSED) {