Begun implimentation of a really good thread control interface.
authorDave West <davew@uncensored.citadel.org>
Wed, 21 Nov 2007 02:48:48 +0000 (02:48 +0000)
committerDave West <davew@uncensored.citadel.org>
Wed, 21 Nov 2007 02:48:48 +0000 (02:48 +0000)
As it turns out it impliments a lot of what eCrash does to track threads
though I didn't know it at the time. To check it out look in sysdep.c
and search on CtdlThread. That will get you close.
At the moment nothing makes use of this code.
A handy side effect is a thread safe sleep mechanism that will sleep
the current thread untill some other thread sleeps or it times out, needs a
bit more work to make it sleep any thread without waking when another
thread sleeps.
Also fixed a potential memory leak or two in the old thread code.

citadel/housekeeping.c
citadel/parsedate.c
citadel/server.h
citadel/sysdep.c
citadel/sysdep_decls.h

index 3a02ef569a028d66c007e7aa115e4cb8dbaf8174..cbc8a1c42c5c70506e2f79bad4b2d75c93d45fff 100644 (file)
@@ -168,6 +168,8 @@ void do_housekeeping(void) {
        JournalRunQueue();
 
        PerformSessionHooks(EVT_HOUSE); /* perform as needed housekeeping */
+       
+       ctdl_internal_thread_gc(0);
 
        /* Then, do the "once per minute" stuff... */
        if (do_perminute_housekeeping_now) {
index 39a3651a8e359348b2085b63e535b02670911506..5a355fd15fe1e97ca7163962209c28b61bc58d56 100644 (file)
@@ -1,7 +1,7 @@
-/* A Bison parser, made by GNU Bison 1.875c.  */
+/* A Bison parser, made by GNU Bison 1.875.  */
 
 /* Skeleton parser for Yacc-like parsing with Bison,
-   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -223,7 +223,7 @@ typedef union YYSTYPE {
     enum _MERIDIAN     Meridian;
 } YYSTYPE;
 /* Line 191 of yacc.c.  */
-#line 227 "y.tab.c"
+#line 226 "y.tab.c"
 # define yystype YYSTYPE /* obsolescent; will be withdrawn */
 # define YYSTYPE_IS_DECLARED 1
 # define YYSTYPE_IS_TRIVIAL 1
@@ -235,29 +235,22 @@ typedef union YYSTYPE {
 
 
 /* Line 214 of yacc.c.  */
-#line 239 "y.tab.c"
+#line 238 "y.tab.c"
 
 #if ! defined (yyoverflow) || YYERROR_VERBOSE
 
-# ifndef YYFREE
-#  define YYFREE free
-# endif
-# ifndef YYMALLOC
-#  define YYMALLOC malloc
-# endif
-
 /* The parser invokes alloca or malloc; define the necessary symbols.  */
 
-# ifdef YYSTACK_USE_ALLOCA
-#  if YYSTACK_USE_ALLOCA
-#   define YYSTACK_ALLOC alloca
-#  endif
+# if YYSTACK_USE_ALLOCA
+#  define YYSTACK_ALLOC alloca
 # else
-#  if defined (alloca) || defined (_ALLOCA_H)
-#   define YYSTACK_ALLOC alloca
-#  else
-#   ifdef __GNUC__
-#    define YYSTACK_ALLOC __builtin_alloca
+#  ifndef YYSTACK_USE_ALLOCA
+#   if defined (alloca) || defined (_ALLOCA_H)
+#    define YYSTACK_ALLOC alloca
+#   else
+#    ifdef __GNUC__
+#     define YYSTACK_ALLOC __builtin_alloca
+#    endif
 #   endif
 #  endif
 # endif
@@ -270,15 +263,15 @@ typedef union YYSTYPE {
 #   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
 #   define YYSIZE_T size_t
 #  endif
-#  define YYSTACK_ALLOC YYMALLOC
-#  define YYSTACK_FREE YYFREE
+#  define YYSTACK_ALLOC malloc
+#  define YYSTACK_FREE free
 # endif
 #endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
 
 
 #if (! defined (yyoverflow) \
      && (! defined (__cplusplus) \
-        || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+        || (YYSTYPE_IS_TRIVIAL)))
 
 /* A type that is properly aligned for any stack member.  */
 union yyalloc
@@ -299,7 +292,7 @@ union yyalloc
 /* Copy COUNT objects from FROM to TO.  The source and destination do
    not overlap.  */
 # ifndef YYCOPY
-#  if defined (__GNUC__) && 1 < __GNUC__
+#  if 1 < __GNUC__
 #   define YYCOPY(To, From, Count) \
       __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
 #  else
@@ -432,10 +425,10 @@ static const unsigned short yyrline[] =
    First, the terminals, then, starting at YYNTOKENS, nonterminals. */
 static const char *const yytname[] =
 {
-  "$end", "error", "$undefined", "tDAY", "tDAYZONE", "tMERIDIAN",
-  "tMONTH", "tMONTH_UNIT", "tSEC_UNIT", "tSNUMBER", "tUNUMBER", "tZONE",
-  "':'", "'/'", "','", "$accept", "spec", "item", "time", "zone",
-  "numzone", "date", "rel", "o_merid", 0
+  "$end", "error", "$undefined", "tDAY", "tDAYZONE", "tMERIDIAN", "tMONTH", 
+  "tMONTH_UNIT", "tSEC_UNIT", "tSNUMBER", "tUNUMBER", "tZONE", "':'", 
+  "'/'", "','", "$accept", "spec", "item", "time", "zone", "numzone", 
+  "date", "rel", "o_merid", 0
 };
 #endif
 
@@ -560,8 +553,7 @@ static const unsigned char yystos[] =
 
 #define YYACCEPT       goto yyacceptlab
 #define YYABORT                goto yyabortlab
-#define YYERROR                goto yyerrorlab
-
+#define YYERROR                goto yyerrlab1
 
 /* Like YYERROR except do call yyerror.  This remains here temporarily
    to ease the transition to the new meaning of YYERROR, for GCC.
@@ -595,11 +587,11 @@ while (0)
    are run).  */
 
 #ifndef YYLLOC_DEFAULT
-# define YYLLOC_DEFAULT(Current, Rhs, N)               \
-   ((Current).first_line   = (Rhs)[1].first_line,      \
-    (Current).first_column = (Rhs)[1].first_column,    \
-    (Current).last_line    = (Rhs)[N].last_line,       \
-    (Current).last_column  = (Rhs)[N].last_column)
+# define YYLLOC_DEFAULT(Current, Rhs, N)         \
+  Current.first_line   = Rhs[1].first_line;      \
+  Current.first_column = Rhs[1].first_column;    \
+  Current.last_line    = Rhs[N].last_line;       \
+  Current.last_column  = Rhs[N].last_column;
 #endif
 
 /* YYLEX -- calling `yylex' with the right arguments.  */
@@ -643,7 +635,7 @@ do {                                                                \
 
 /*------------------------------------------------------------------.
 | yy_stack_print -- Print the state stack from its BOTTOM up to its |
-| TOP (included).                                                   |
+| TOP (cinluded).                                                   |
 `------------------------------------------------------------------*/
 
 #if defined (__STDC__) || defined (__cplusplus)
@@ -683,9 +675,9 @@ yy_reduce_print (yyrule)
 #endif
 {
   int yyi;
-  unsigned int yylno = yyrline[yyrule];
+  unsigned int yylineno = yyrline[yyrule];
   YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
-             yyrule - 1, yylno);
+             yyrule - 1, yylineno);
   /* Print the symbols being reduced, and their result.  */
   for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
     YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
@@ -722,7 +714,7 @@ int yydebug;
    SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
    evaluated with infinite-precision integer arithmetic.  */
 
-#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0
+#if YYMAXDEPTH == 0
 # undef YYMAXDEPTH
 #endif
 
@@ -1409,8 +1401,8 @@ yyreduce:
 
     }
 
-/* Line 1000 of yacc.c.  */
-#line 1414 "y.tab.c"
+/* Line 991 of yacc.c.  */
+#line 1405 "y.tab.c"
 \f
   yyvsp -= yylen;
   yyssp -= yylen;
@@ -1451,33 +1443,18 @@ yyerrlab:
        {
          YYSIZE_T yysize = 0;
          int yytype = YYTRANSLATE (yychar);
-         const char* yyprefix;
          char *yymsg;
-         int yyx;
+         int yyx, yycount;
 
+         yycount = 0;
          /* Start YYX at -YYN if negative to avoid negative indexes in
             YYCHECK.  */
-         int yyxbegin = yyn < 0 ? -yyn : 0;
-
-         /* Stay within bounds of both yycheck and yytname.  */
-         int yychecklim = YYLAST - yyn;
-         int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
-         int yycount = 0;
-
-         yyprefix = ", expecting ";
-         for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+         for (yyx = yyn < 0 ? -yyn : 0;
+              yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++)
            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
-             {
-               yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]);
-               yycount += 1;
-               if (yycount == 5)
-                 {
-                   yysize = 0;
-                   break;
-                 }
-             }
-         yysize += (sizeof ("syntax error, unexpected ")
-                    + yystrlen (yytname[yytype]));
+             yysize += yystrlen (yytname[yyx]) + 15, yycount++;
+         yysize += yystrlen ("syntax error, unexpected ") + 1;
+         yysize += yystrlen (yytname[yytype]);
          yymsg = (char *) YYSTACK_ALLOC (yysize);
          if (yymsg != 0)
            {
@@ -1486,13 +1463,16 @@ yyerrlab:
 
              if (yycount < 5)
                {
-                 yyprefix = ", expecting ";
-                 for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+                 yycount = 0;
+                 for (yyx = yyn < 0 ? -yyn : 0;
+                      yyx < (int) (sizeof (yytname) / sizeof (char *));
+                      yyx++)
                    if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
                      {
-                       yyp = yystpcpy (yyp, yyprefix);
+                       const char *yyq = ! yycount ? ", expecting " : " or ";
+                       yyp = yystpcpy (yyp, yyq);
                        yyp = yystpcpy (yyp, yytname[yyx]);
-                       yyprefix = " or ";
+                       yycount++;
                      }
                }
              yyerror (yymsg);
@@ -1513,56 +1493,52 @@ yyerrlab:
       /* If just tried and failed to reuse lookahead token after an
         error, discard it.  */
 
-      if (yychar <= YYEOF)
+      /* Return failure if at end of input.  */
+      if (yychar == YYEOF)
         {
-          /* If at end of input, pop the error token,
-            then the rest of the stack, then return failure.  */
-         if (yychar == YYEOF)
-            for (;;)
-              {
-                YYPOPSTACK;
-                if (yyssp == yyss)
-                  YYABORT;
-                YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
-                yydestruct (yystos[*yyssp], yyvsp);
-              }
+         /* Pop the error token.  */
+          YYPOPSTACK;
+         /* Pop the rest of the stack.  */
+         while (yyss < yyssp)
+           {
+             YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+             yydestruct (yystos[*yyssp], yyvsp);
+             YYPOPSTACK;
+           }
+         YYABORT;
         }
-      else
-       {
-         YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
-         yydestruct (yytoken, &yylval);
-         yychar = YYEMPTY;
 
-       }
+      YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+      yydestruct (yytoken, &yylval);
+      yychar = YYEMPTY;
+
     }
 
   /* Else will try to reuse lookahead token after shifting the error
      token.  */
-  goto yyerrlab1;
+  goto yyerrlab2;
 
 
-/*---------------------------------------------------.
-| yyerrorlab -- error raised explicitly by YYERROR.  |
-`---------------------------------------------------*/
-yyerrorlab:
+/*----------------------------------------------------.
+| yyerrlab1 -- error raised explicitly by an action.  |
+`----------------------------------------------------*/
+yyerrlab1:
 
-#ifdef __GNUC__
-  /* Pacify GCC when the user code never invokes YYERROR and the label
-     yyerrorlab therefore never appears in user code.  */
-  if (0)
-     goto yyerrorlab;
+  /* Suppress GCC warning that yyerrlab1 is unused when no action
+     invokes YYERROR.  */
+#if defined (__GNUC_MINOR__) && 2093 <= (__GNUC__ * 1000 + __GNUC_MINOR__) \
+    && !defined __cplusplus
+  __attribute__ ((__unused__))
 #endif
 
-  yyvsp -= yylen;
-  yyssp -= yylen;
-  yystate = *yyssp;
-  goto yyerrlab1;
 
+  goto yyerrlab2;
 
-/*-------------------------------------------------------------.
-| yyerrlab1 -- common code for both syntax error and YYERROR.  |
-`-------------------------------------------------------------*/
-yyerrlab1:
+
+/*---------------------------------------------------------------.
+| yyerrlab2 -- pop states until the error token can be shifted.  |
+`---------------------------------------------------------------*/
+yyerrlab2:
   yyerrstatus = 3;     /* Each real token shifted decrements this.  */
 
   for (;;)
@@ -1585,8 +1561,9 @@ yyerrlab1:
 
       YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
       yydestruct (yystos[yystate], yyvsp);
-      YYPOPSTACK;
-      yystate = *yyssp;
+      yyvsp--;
+      yystate = *--yyssp;
+
       YY_STACK_PRINT (yyss, yyssp);
     }
 
index 02b0de9c71b50e443490773ff541d1fa3d93222c..95c820ef8c34dc3fe74b2430510993c043b782c6 100644 (file)
@@ -242,6 +242,7 @@ enum {
        S_CHKPWD,
        S_LOG,
        S_NETSPOOL,
+       S_THREAD_LIST,
        MAX_SEMAPHORES
 };
 
index 14676acfb552e038528181142399497c25121de7..43a4bdec27ebc165d277027cabd405736d4161f0 100644 (file)
@@ -786,7 +786,7 @@ void sysdep_master_cleanup(void) {
 #ifdef HAVE_OPENSSL
        destruct_ssl();
 #endif
-       serv_calendar_destroy();
+       serv_calendar_destroy();        // FIXME: Shouldn't be here, should be by a cleanup hook surely.
        CtdlDestroyProtoHooks();
        CtdlDestroyDeleteHooks();
        CtdlDestroyXmsgHooks();
@@ -944,6 +944,292 @@ int convert_login(char NameToConvert[]) {
        }
 }
 
+
+
+/*
+ * New thread interface.
+ * To create a thread you must call one of the create thread functions.
+ * You must pass it the address of (a pointer to a CtdlThreadNode initialised to NULL) like this
+ * struct CtdlThreadNode *node = NULL;
+ * pass in &node
+ * If the thread is created *node will point to the thread control structure for the created thread.
+ * If the thread creation fails *node remains NULL
+ * Do not free the memory pointed to by *node, it doesn't belong to you.
+ * If your thread function returns it will be started again without creating a new thread.
+ * If your thread function wants to exit it should call CtdlThreadExit(ret_code);
+ * This new interface duplicates much of the eCrash stuff. We should go for closer integration since that would
+ * remove the need for the calls to eCrashRegisterThread and friends
+ */
+
+// FIXME: these defines should be else where
+#define CTDLTHREAD_BIGSTACK    0x0001
+
+struct CtdlThreadNode *CtdlThreadList = NULL;
+static pthread_mutex_t ThreadWaiterMutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t ThreadWaiterCond = PTHREAD_COND_INITIALIZER;
+
+
+/*
+ * So we now have a sleep command that works with threads but it is in seconds
+ */
+void CtdlThreadSleep(int secs)
+{
+
+       struct timespec wake_time;
+       struct timeval time_now;
+       
+       
+       memset (&wake_time, 0, sizeof(struct timespec));
+       gettimeofday(&time_now, NULL);
+       wake_time.tv_sec = time_now.tv_sec + secs;
+       pthread_cond_timedwait(&ThreadWaiterCond, &ThreadWaiterMutex, &wake_time);
+}
+
+
+/*
+ * Routine to clean up our thread function on exit
+ */
+void ctdl_internal_thread_cleanup(void *arg)
+{
+       struct CtdlThreadNode *this_thread;
+       // arg is a pointer to our thread structure
+       this_thread = (struct CtdlThreadNode *) arg;
+       if (this_thread->valid)
+       {
+               /*
+                * In here we were called by the current thread because it is exiting
+                * NB. WE ARE THE CURRENT THREAD
+                */
+               CtdlLogPrintf(CTDL_NOTICE, "Thread \"%s\" (%ld) exited.\n", this_thread->name, this_thread->tid);
+               this_thread->running = FALSE;
+               this_thread->valid = FALSE;     // needs to be last thing else house keeping will unlink us too early
+               /*
+                * Our thread is exiting either because it wanted to end or because the server is stopping
+                * We need to clean up
+                */
+               #ifdef HAVE_BACKTRACE
+               eCrash_UnregisterThread();
+               #endif
+       }
+       else
+       {
+               if (this_thread->tid == pthread_self())
+               {
+                       CtdlLogPrintf(CTDL_EMERG, "Thread system PANIC a thread is trying to clean up after itself.\n");
+                       time_to_die = -1;
+                       return;
+               }
+               /*
+                * In here we were called by some other thread that wants to clean up any dead threads
+                * NB. WE ARE NOT THE THREAD BEING CLEANED
+                */
+               // We probably got called by house keeping or master shutdown so we unlink the dead threads here
+               num_threads--;
+               
+               begin_critical_section(S_THREAD_LIST);
+               if(this_thread->name)
+                       free(this_thread->name);
+               if(this_thread->prev)
+                       this_thread->prev->next = this_thread->next;
+               if(this_thread->next)
+                       this_thread->next->prev = this_thread->next;
+               end_critical_section(S_THREAD_LIST);
+               free(this_thread);
+       }
+}
+
+
+/*
+ * Garbage collection routine.
+ * Gets called by do_housekeeping() and in master_cleanup() to clean up the thread list
+ */
+void ctdl_internal_thread_gc (int shutdown)
+{
+       struct CtdlThreadNode *this_thread, *that_thread;
+       
+       this_thread = CtdlThreadList;
+       while(this_thread)
+       {
+               that_thread = this_thread;
+               this_thread = this_thread->next;
+               
+               if(shutdown && that_thread->valid)
+               {       // We want the threads to shutdown so first ask it nicely
+                       that_thread->running = FALSE;
+                       // Wait for it to exit
+                       CtdlThreadSleep(1);
+                       
+                       if(that_thread->valid)  // Be more brutal about it
+                               pthread_cancel (that_thread->tid);
+                       // Wait for it to exit
+                       CtdlThreadSleep(1);
+               }
+               
+               if (that_thread->valid == FALSE)
+               {
+                       CtdlLogPrintf(CTDL_NOTICE, "Joining thread \"%s\" (%ld)\n", that_thread->name, that_thread->tid);
+                       pthread_join(that_thread->tid, NULL);
+                       ctdl_internal_thread_cleanup(that_thread);
+               }
+       }
+}
+/*
+ * Runtime function for a Citadel Thread.
+ * This initialises the threads environment and then calls the user supplied thread function
+ * Note that this is the REAL thread function and wraps the users thread function.
+ */ 
+void *ctdl_internal_thread_func (void *arg)
+{
+       struct CtdlThreadNode *this_thread;
+       void *ret = NULL;
+
+       // Get our thread data structure
+       this_thread = (struct CtdlThreadNode *) arg;
+       // Tell the world we are here
+       CtdlLogPrintf(CTDL_NOTICE, "Spawned a new thread \"%s\" (%ld). \n", this_thread->name, this_thread->tid);
+
+       num_threads++;  // Increase the count of threads in the system.
+       
+       // Register for tracing
+       #ifdef HAVE_BACKTRACE
+       eCrash_RegisterThread(this_thread->name, 0);
+       #endif
+
+       // Register the cleanup function to take care of when we exit.
+       pthread_cleanup_push(ctdl_internal_thread_cleanup, arg);
+       
+       this_thread->running = TRUE;
+       
+       while ((!time_to_die) && (this_thread->running))
+       {       // Call the users thread function
+               ret = (this_thread->thread_func)(this_thread->user_args);
+       }
+       
+       /*
+        * Our thread is exiting either because it wanted to end or because the server is stopping
+        * We need to clean up
+        */
+       #ifdef HAVE_BACKTRACE
+       eCrash_UnregisterThread();
+       #endif
+       
+       pthread_cleanup_pop(1); // Execute our cleanup routine and remove it
+       
+       return(ret);
+}
+
+
+/*
+ * Internal function to create a thread.
+ * Must be called from within a S_THREAD_LIST critical section
+ */ 
+int ctdl_internal_create_thread(char *name, int flags, void *(*thread_func) (void *arg), void *arg, struct CtdlThreadNode **new_thread)
+{
+       int ret = 0;
+       pthread_attr_t attr;
+       struct CtdlThreadNode *this_thread;
+       
+       if (*new_thread)
+       {
+               lprintf(CTDL_EMERG, "Possible attempt to overwrite an existing thread!!!\n");
+               return -1;
+       }
+       
+       this_thread = malloc(sizeof(struct CtdlThreadNode));
+       if (this_thread == NULL) {
+               lprintf(CTDL_EMERG, "can't allocate CtdlThreadNode, exiting\n");
+               return ret;
+       }
+       // Ensuring this is zero'd means we make sure the thread doesn't start doing its thing until we are ready.
+       memset (this_thread, 0, sizeof(struct CtdlThreadNode));
+       
+       if ((ret = pthread_attr_init(&attr))) {
+               lprintf(CTDL_EMERG, "pthread_attr_init: %s\n", strerror(ret));
+               free(this_thread);
+               return ret;
+       }
+
+       /* Our per-thread stacks need to be bigger than the default size,
+        * otherwise the MIME parser crashes on FreeBSD, and the IMAP service
+        * crashes on 64-bit Linux.
+        */
+       if (flags & CTDLTHREAD_BIGSTACK)
+       {
+               if ((ret = pthread_attr_setstacksize(&attr, THREADSTACKSIZE))) {
+                       lprintf(CTDL_EMERG, "pthread_attr_setstacksize: %s\n",
+                               strerror(ret));
+                       pthread_attr_destroy(&attr);
+                       free(this_thread);
+                       return ret;
+               }
+       }
+
+       /*
+        * If we got here we are going to create the thread so we must initilise the structure
+        * first because most implimentations of threading can't create it in a stopped state
+        * and it might want to do things with its structure that aren't initialised otherwise.
+        */
+       if(name)
+       {
+               this_thread->name = strdup(name);
+       }
+       else
+       {
+               this_thread->name = strdup("Unknown Thread");
+       }
+       this_thread->flags = flags;
+       this_thread->thread_func = thread_func;
+       this_thread->user_args = arg;
+       this_thread->valid = 1; // Need this to prevent house keeping unlinking us from the list
+       /*
+        * We pass this_thread into the thread as its args so that it can find out information
+        * about itself and it has a bit of storage space for itself, not to mention that the REAL
+        * thread function needs to finish off the setup of the structure
+        */
+       if ((ret = pthread_create(&this_thread->tid, &attr, ctdl_internal_thread_func, this_thread) != 0))
+       {
+
+               lprintf(CTDL_ALERT, "Can't create thread: %s\n",
+                       strerror(ret));
+               if (this_thread->name)
+                       free (this_thread->name);
+               free(this_thread);
+               pthread_attr_destroy(&attr);
+               return ret;
+       }
+       
+       this_thread->next = CtdlThreadList;
+       CtdlThreadList = this_thread;
+       *new_thread = this_thread;
+       pthread_attr_destroy(&attr);
+       return 0;
+}
+
+/*
+ * Wrapper function to create a thread
+ * ensures the critical section and other protections are in place.
+ * char *name = name to give to thread, if NULL, use generic name
+ * int flags = flags to determine type of thread and standard facilities
+ */
+int CtdlCreateThread(char *name, int flags, void *(*thread_func) (void *arg), void *arg,  struct CtdlThreadNode **new_thread)
+{
+       int ret;
+       
+       begin_critical_section(S_THREAD_LIST);
+       ret = ctdl_internal_create_thread(name, flags, thread_func, arg, new_thread);
+       end_critical_section(S_THREAD_LIST);
+       return ret;
+}
+
+
+       
+/*
+ * Old thread interface.
+ */
+
+
 struct worker_node *worker_list = NULL;
 
 
@@ -966,6 +1252,7 @@ void create_worker(void) {
        if ((ret = pthread_attr_init(&attr))) {
                lprintf(CTDL_EMERG, "pthread_attr_init: %s\n", strerror(ret));
                time_to_die = -1;
+               free(n);
                return;
        }
 
@@ -978,6 +1265,7 @@ void create_worker(void) {
                        strerror(ret));
                time_to_die = -1;
                pthread_attr_destroy(&attr);
+               free(n);
                return;
        }
 
@@ -986,6 +1274,10 @@ void create_worker(void) {
 
                lprintf(CTDL_ALERT, "Can't create worker thread: %s\n",
                        strerror(ret));
+               time_to_die = -1;
+               pthread_attr_destroy(&attr);
+               free(n);
+               return;
        }
 
        n->next = worker_list;
index 630d117e3385eaf34ee0f6bb838f1ad10bc5949e..ab8745c790c5e0ee1073ddc3b0190c287d441bc9 100644 (file)
@@ -84,6 +84,8 @@ void InitializeMasterCC(void);
 void init_master_fdset(void);
 void create_worker(void);
 void InitialiseSemaphores(void);
+void ctdl_internal_thread_gc (int shutdown);
+
 
 extern int num_sessions;
 extern volatile int time_to_die;
@@ -99,6 +101,19 @@ extern struct worker_node {
         struct worker_node *next;
 } *worker_list;
 
+
+extern struct CtdlThreadNode {
+       pthread_t tid;
+       char *name;
+       void *(*thread_func) (void *arg);
+       void *user_args;
+       int flags;
+       int running;
+       int valid;
+       struct CtdlThreadNode *prev;
+       struct CtdlThreadNode *next;
+} *CtdlThreadList;
+
 extern int SyslogFacility(char *name);
 extern int syslog_facility;