Fix up ARTV import so that imports actually work.
[citadel.git] / citadel / threads.c
index fdbf5dced068a5fa3be3daeaf5a367709e078dd4..a24e03289e74121b826d57856c09d63a2d17649c 100644 (file)
@@ -13,6 +13,7 @@
 #include <sys/socket.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <signal.h>
 
 #if TIME_WITH_SYS_TIME
 # include <sys/time.h>
@@ -57,7 +58,7 @@ static int num_workers = 0;                   /* Current number of worker threads */
 CtdlThreadNode *CtdlThreadList = NULL;
 CtdlThreadNode *CtdlThreadSchedList = NULL;
 
-static citthread_t GC_thread;
+static CtdlThreadNode *GC_thread = NULL;
 static char *CtdlThreadStates[CTDL_THREAD_LAST_STATE];
 double CtdlThreadLoadAvg = 0;
 double CtdlThreadWorkerAvg = 0;
@@ -153,7 +154,7 @@ void ctdl_thread_internal_init_tsd(void)
        int ret;
        
        if ((ret = citthread_key_create(&ThreadKey, ctdl_thread_internal_dest_tsd))) {
-               lprintf(CTDL_EMERG, "citthread_key_create: %s\n", strerror(ret));
+               CtdlLogPrintf(CTDL_EMERG, "citthread_key_create: %s\n", strerror(ret));
                exit(CTDLEXIT_DB);
        }
 }
@@ -220,7 +221,6 @@ void ctdl_thread_internal_init(void)
        CtdlThreadNode *this_thread;
        int ret = 0;
        
-       GC_thread = citthread_self();
        CtdlThreadStates[CTDL_THREAD_INVALID] = strdup ("Invalid Thread");
        CtdlThreadStates[CTDL_THREAD_VALID] = strdup("Valid Thread");
        CtdlThreadStates[CTDL_THREAD_CREATE] = strdup("Thread being Created");
@@ -257,7 +257,8 @@ void ctdl_thread_internal_init(void)
 
        this_thread->name = "Garbage Collection Thread";
        
-       this_thread->tid = GC_thread;
+       this_thread->tid = citthread_self();
+       GC_thread = this_thread;
        CT = this_thread;
        
        num_threads++;  // Increase the count of threads in the system.
@@ -329,14 +330,18 @@ void CtdlThreadStopAll(void)
        
        begin_critical_section(S_THREAD_LIST);
        this_thread = CtdlThreadList;
+       // Ask the GC thread to stop first so everything knows we are shutting down.
+       GC_thread->state = CTDL_THREAD_STOP_REQ;
        while(this_thread)
        {
 #ifdef THREADS_USESIGNALS
-               citthread_killl(this_thread->tid, SIGHUP);
+               if (!citthread_equal(this_thread->tid, GC_thread->tid))
+                       citthread_kill(this_thread->tid, SIGHUP);
 #endif
                ctdl_thread_internal_change_state (this_thread, CTDL_THREAD_STOP_REQ);
                citthread_cond_signal(&this_thread->ThreadCond);
                citthread_cond_signal(&this_thread->SleepCond);
+               this_thread->stop_ticker = time(NULL);
                CtdlLogPrintf(CTDL_DEBUG, "Thread system stopping thread \"%s\" (0x%08lx).\n",
                        this_thread->name, this_thread->tid);
                this_thread = this_thread->next;
@@ -471,9 +476,12 @@ int CtdlThreadCheckStop(void)
        
        state = CT->state;
 
-#ifdef THREADS_USERSIGNALS
+#ifdef THREADS_USESIGNALS
        if (CT->signal)
+       {
                CtdlLogPrintf(CTDL_DEBUG, "Thread \"%s\" caught signal %d.\n", CT->name, CT->signal);
+               CT->signal = 0;
+       }
 #endif
        if(state == CTDL_THREAD_STOP_REQ)
        {
@@ -505,11 +513,13 @@ void CtdlThreadStop(CtdlThreadNode *thread)
        if (!(this_thread->thread_func))
                return;         // Don't stop garbage collector
 #ifdef THREADS_USESIGNALS
-       citthread_kill(this_thread->tid, SIGHUP);       
+       if (!citthread_equal(this_thread->tid, GC_thread->tid))
+               citthread_kill(this_thread->tid, SIGHUP);
 #endif
        ctdl_thread_internal_change_state (this_thread, CTDL_THREAD_STOP_REQ);
        citthread_cond_signal(&this_thread->ThreadCond);
        citthread_cond_signal(&this_thread->SleepCond);
+       this_thread->stop_ticker = time(NULL);
 }
 
 /*
@@ -640,11 +650,19 @@ void CtdlThreadGC (void)
                
                if ((that_thread->state == CTDL_THREAD_STOP_REQ || that_thread->state == CTDL_THREAD_STOPPING)
                        && (!citthread_equal(that_thread->tid, citthread_self())))
-                               that_thread->stop_ticker++;
+                               CtdlLogPrintf(CTDL_DEBUG, "Waiting for thread %s (0x%08lx) to exit.\n", that_thread->name, that_thread->tid);
+               else
+               {
+                       /**
+                        * Catch the situation where a worker was asked to stop but couldn't and we are not
+                        * shutting down.
+                        */
+                       that_thread->stop_ticker = 0;
+               }
                
-               if (that_thread->stop_ticker == 5)
+               if (that_thread->stop_ticker + 5 == time(NULL))
                {
-                       CtdlLogPrintf(CTDL_DEBUG, "Thread System: The thread \"%s\" (0x%08lx) failed to self terminate withing 5 ticks. It would be cancelled now.\n", that_thread->name, that_thread->tid);
+                       CtdlLogPrintf(CTDL_DEBUG, "Thread System: The thread \"%s\" (0x%08lx) failed to self terminate within 5 ticks. It would be cancelled now.\n", that_thread->name, that_thread->tid);
                        if ((that_thread->flags & CTDLTHREAD_WORKER) == 0)
                                CtdlLogPrintf(CTDL_INFO, "Thread System: A non worker thread would have been canceled this may cause message loss.\n");
 //                     that_thread->state = CTDL_THREAD_CANCELLED;
@@ -1112,7 +1130,7 @@ void ctdl_thread_internal_check_scheduled(void)
                                if (ctdl_thread_internal_start_scheduled (that_thread))
                                {
 #ifdef WITH_THREADLOG
-                                       CtdlLogPrintf(CTDL_INFO, "Thread system, Started a scheduled thread \"%s\" (%ud).\n",
+                                       CtdlLogPrintf(CTDL_INFO, "Thread system, Started a scheduled thread \"%s\" (0x%08lx).\n",
                                                that_thread->name, that_thread->tid);
 #endif
                                }
@@ -1135,11 +1153,47 @@ void ctdl_thread_internal_check_scheduled(void)
  */
 int CtdlThreadSelect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
 {
-       int ret;
+       int ret = 0;
        
        ctdl_thread_internal_change_state(CT, CTDL_THREAD_BLOCKED);
-       ret = select(n, readfds, writefds, exceptfds, timeout);
-       ctdl_thread_internal_change_state(CT, CTDL_THREAD_RUNNING);
+       if (!CtdlThreadCheckStop())
+               ret = select(n, readfds, writefds, exceptfds, timeout);
+       /**
+        * If the select returned <= 0 then it failed due to an error
+        * or timeout so this thread could stop if asked to do so.
+        * Anything else means it needs to continue unless the system is shutting down
+        */
+       if (ret <= 0)
+       {
+               /**
+                * select says nothing to do so we can change to running if we haven't been asked to stop.
+                */
+               ctdl_thread_internal_change_state(CT, CTDL_THREAD_RUNNING);
+       }
+       else
+       {
+               /**
+                * The select says this thread needs to do something useful.
+                * This thread was in an idle state so it may have been asked to stop
+                * but if the system isn't shutting down this thread is no longer
+                * idle and select has given it a task to do so it must not stop
+                * In this condition we need to force it into the running state.
+                * CtdlThreadGC will clear its ticker for us.
+                *
+                * FIXME: there is still a small hole here. It is possible for the sequence of locking
+                * to allow the state to get changed to STOP_REQ just after this code if the other thread
+                * has decided to change the state before this lock, it there fore has to wait till the lock
+                * completes but it will continue to change the state. We need something a bit better here.
+                */
+               citthread_mutex_lock(&CT->ThreadMutex); /* To prevent race condition of a sleeping thread */
+               if (GC_thread->state > CTDL_THREAD_STOP_REQ && CT->state <= CTDL_THREAD_STOP_REQ)
+               {
+                       CtdlLogPrintf(CTDL_DEBUG, "Thread %s (0x%08lx) refused stop request.\n", CT->name, CT->tid);
+                       CT->state = CTDL_THREAD_RUNNING;
+               }
+               citthread_mutex_unlock(&CT->ThreadMutex);
+       }
+
        return ret;
 }
 
@@ -1262,7 +1316,11 @@ void go_threading(void)
                        CtdlThreadGC();
                }
                
+#ifdef THREADS_USESIGNALS
+               if (CtdlThreadGetCount() && CT->state > CTDL_THREAD_STOP_REQ)
+#else
                if (CtdlThreadGetCount())
+#endif
                        CtdlThreadSleep(1);
        }
        /*