X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=lib%2Fabyss%2Fsrc%2Fthread_fork.c;fp=lib%2Fabyss%2Fsrc%2Fthread_fork.c;h=b1727c71b82c4663a43ac1be7a28ea55c2401227;hb=ce67d0cdeaa37c3e856e23ae4010480887165630;hp=0000000000000000000000000000000000000000;hpb=e355d4e7962400470f467b88f5568de9c8324475;p=xmlrpc-c diff --git a/lib/abyss/src/thread_fork.c b/lib/abyss/src/thread_fork.c new file mode 100644 index 0000000..b1727c7 --- /dev/null +++ b/lib/abyss/src/thread_fork.c @@ -0,0 +1,323 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "xmlrpc_config.h" +#include "xmlrpc-c/string_int.h" +#include "xmlrpc-c/abyss.h" + +#include "mallocvar.h" +#include "thread.h" + + +static void +blockSignalClass(int const signalClass, + sigset_t * const oldBlockedSetP) { + + sigset_t newBlockedSet; + + sigemptyset(&newBlockedSet); + sigaddset(&newBlockedSet, signalClass); + + sigprocmask(SIG_BLOCK, &newBlockedSet, oldBlockedSetP); +} + + + +struct abyss_thread { + struct abyss_thread * nextInPoolP; + TThreadDoneFn * threadDone; + void * userHandle; + pid_t pid; + abyss_bool useSigchld; + /* This means that user is going to call ThreadHandleSigchld() + when it gets a death of a child signal for this process. If + false, he's going to leave us in the dark, so we'll have to + poll to know if the process is dead or not. + */ +}; + + +/* Because signals are global, we need this global variable in order for + the signal handler to figure out to what thread the signal belongs. +*/ + +/* We use a singly linked list. Every time we access it, we have to run + the whole chain. To make this scale up, we should replace it with + a doubly linked list and some kind of index by PID. + + But large scale systems probably aren't using fork threads anyway. +*/ + +static struct { + struct abyss_thread * firstP; +} ThreadPool; + + + +void +ThreadPoolInit(void) { + + ThreadPool.firstP = NULL; +} + + + +static struct abyss_thread * +findThread(pid_t const pid) { + + struct abyss_thread * p; + + for (p = ThreadPool.firstP; p && p->pid != pid; p = p->nextInPoolP); + + return p; +} + + + +static void +addToPool(struct abyss_thread * const threadP) { + + if (ThreadPool.firstP == NULL) + ThreadPool.firstP = threadP; + else { + struct abyss_thread * p; + + for (p = ThreadPool.firstP; p->nextInPoolP; p = p->nextInPoolP); + + /* p points to the last thread in the list */ + + p->nextInPoolP = threadP; + } +} + + + +static void +removeFromPool(struct abyss_thread * const threadP) { + + if (threadP == ThreadPool.firstP) + ThreadPool.firstP = threadP->nextInPoolP; + else { + struct abyss_thread * p; + + for (p = ThreadPool.firstP; + p && p->nextInPoolP != threadP; + p = p->nextInPoolP); + + if (p) + /* p points to thread right before the one we want to remove */ + p->nextInPoolP = threadP->nextInPoolP; + } +} + + + +void +ThreadHandleSigchld(pid_t const pid) { +/*---------------------------------------------------------------------------- + Handle a death of a child signal for process 'pid', which may be one + of our threads. +-----------------------------------------------------------------------------*/ + struct abyss_thread * const threadP = findThread(pid); + + if (threadP) { + if (threadP->threadDone) + threadP->threadDone(threadP->userHandle); + threadP->pid = 0; + } + /* Note that threadDone might free *threadP */ +} + + + +void +ThreadUpdateStatus(TThread * const threadP) { + + if (!threadP->useSigchld) { + if (threadP->pid) { + if (kill(threadP->pid, 0) != 0) { + if (threadP->threadDone) + threadP->threadDone(threadP->userHandle); + threadP->pid = 0; + } + } + } +} + + + +void +ThreadCreate(TThread ** const threadPP, + void * const userHandle, + TThreadProc * const func, + TThreadDoneFn * const threadDone, + abyss_bool const useSigchld, + const char ** const errorP) { + + TThread * threadP; + + MALLOCVAR(threadP); + if (threadP == NULL) + xmlrpc_asprintf(errorP, + "Can't allocate memory for thread descriptor."); + else { + sigset_t oldBlockedSet; + pid_t rc; + + threadP->nextInPoolP = NULL; + threadP->threadDone = threadDone; + threadP->userHandle = userHandle; + threadP->useSigchld = useSigchld; + threadP->pid = 0; + + /* We have to be sure we don't get the SIGCHLD for this child's + death until the child is properly registered in the thread pool + so that the handler will know who he is. + */ + blockSignalClass(SIGCHLD, &oldBlockedSet); + + rc = fork(); + + if (rc < 0) + xmlrpc_asprintf(errorP, "fork() failed, errno=%d (%s)", + errno, strerror(errno)); + else if (rc == 0) { + /* This is the child */ + (*func)(userHandle); + exit(0); + } else { + /* This is the parent */ + threadP->pid = rc; + + addToPool(threadP); + + sigprocmask(SIG_SETMASK, &oldBlockedSet, NULL); /* restore */ + + *errorP = NULL; + *threadPP = threadP; + } + if (*errorP) { + removeFromPool(threadP); + free(threadP); + } + } +} + + + +abyss_bool +ThreadRun(TThread * const threadP ATTR_UNUSED) { + return TRUE; +} + + + +abyss_bool +ThreadStop(TThread * const threadP ATTR_UNUSED) { + return TRUE; +} + + + +abyss_bool +ThreadKill(TThread * const threadP ATTR_UNUSED) { + return TRUE; +} + + + +void +ThreadWaitAndRelease(TThread * const threadP) { + + if (threadP->pid) { + int exitStatus; + + waitpid(threadP->pid, &exitStatus, 0); + + threadP->threadDone(threadP->userHandle); + + threadP->pid = 0; + } + ThreadRelease(threadP); +} + + + +void +ThreadExit(int const retValue) { + + /* Note that the OS will automatically send a SIGCHLD signal to + the parent process after we exit. The handler for that signal + will run threadDone in parent's context. Alternatively, if + the parent is set up for signals, the parent will eventually + poll for the existence of our PID and call threadDone when he + sees we've gone. + */ + + exit(retValue); +} + + + +void +ThreadRelease(TThread * const threadP) { + + removeFromPool(threadP); + free(threadP); +} + + + +abyss_bool +ThreadForks(void) { + + return TRUE; +} + + + +/********************************************************************* +** Mutex +*********************************************************************/ + +/* As two processes don't share memory, there is nothing to synchronize, + so locking is a no-op. +*/ + +abyss_bool +MutexCreate(TMutex * const mutexP ATTR_UNUSED) { + return TRUE; +} + + + +abyss_bool +MutexLock(TMutex * const mutexP ATTR_UNUSED) { + return TRUE; +} + + + +abyss_bool +MutexUnlock(TMutex * const mutexP ATTR_UNUSED) { + return TRUE; +} + + + +abyss_bool +MutexTryLock(TMutex * const mutexP ATTR_UNUSED) { + return TRUE; +} + + + +void +MutexFree(TMutex * const mutexP ATTR_UNUSED) { + +}