initial load of upstream version 1.06.32
[xmlrpc-c] / lib / abyss / src / thread_fork.c
diff --git a/lib/abyss/src/thread_fork.c b/lib/abyss/src/thread_fork.c
new file mode 100644 (file)
index 0000000..b1727c7
--- /dev/null
@@ -0,0 +1,323 @@
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <wait.h>
+#include <signal.h>
+
+#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) {
+
+}