From 84a0666848ccbea920f0bd398baf149c1a12ea74 Mon Sep 17 00:00:00 2001 From: Dave West Date: Sun, 25 Nov 2007 22:15:18 +0000 Subject: [PATCH] Here it is, the new thread interface. The code to handle threads is in sysdep.c This new thread interface has caused a change to the way modules are initialised. A modules init function is now called twice. First time threading==0, second time threading==1. threading is a parameter passed to the init function. You should not create a thread until your init function has been called with threading==1. See serv_fulltext.c as an example. All of the modules init functions have been updated for this change. The old RegisterThread stuff has gone away. time_to_die has gone as it was used to tell threads to exit, we now use CtdlThreadCheckStop() instead which return non zero if the thread should exit. The server will exit when all threads have stopped. CtdlThreadCreate() handles creation of threads in a safe manner. CtdlThreadSleep() is used to do a sleep in a thread. CtdlThreadStop() is used to stop a thread. CtdlThreadStopAll() stops all threads and thus causes the server to exit. Threads are cleanup when they die by the main server process (every 10 seconds) which originally did nothing useful. CtdlThreadGC() will force a garbage collection now. CtdlThreadGetCount() return the number of threads in the system not just the number of workers. CtdlThreadSelf() returns a pointer to this threads thread structure. CtdlThreadName() returns a strdup'd string that is the threads name, you need to free this. It also allows you to change a threads name. CtdlThreadCancel() does the same as pthread_cancel except it also informs our thread system. Don't use this if you can help it. There are a lot of debug messages at the moment, they will go away once stability is proven. --- citadel/citserver.c | 14 +- citadel/database.h | 1 + citadel/database_sleepycat.c | 32 +- citadel/housekeeping.c | 8 +- citadel/include/ctdl_module.h | 19 +- citadel/mk_module_init.sh | 9 +- .../autocompletion/serv_autocompletion.c | 6 +- citadel/modules/bio/serv_bio.c | 10 +- citadel/modules/calendar/serv_calendar.c | 19 +- citadel/modules/chat/serv_chat.c | 23 +- citadel/modules/checkpoint/serv_checkpoint.c | 73 ++ citadel/modules/expire/serv_expire.c | 9 +- citadel/modules/fulltext/serv_fulltext.c | 29 +- citadel/modules/funambol/serv_funambol.c | 8 +- citadel/modules/imap/serv_imap.c | 15 +- citadel/modules/inetcfg/serv_inetcfg.c | 9 +- citadel/modules/ldap/serv_ldap.c | 51 +- citadel/modules/listsub/serv_listsub.c | 7 +- .../modules/managesieve/serv_managesieve.c | 22 +- citadel/modules/mrtg/serv_mrtg.c | 7 +- citadel/modules/netfilter/serv_netfilter.c | 7 +- citadel/modules/network/serv_network.c | 20 +- citadel/modules/newuser/serv_newuser.c | 11 +- citadel/modules/notes/serv_notes.c | 7 +- citadel/modules/pager/serv_pager.c | 9 +- citadel/modules/pas2/serv_pas2.c | 7 +- citadel/modules/pop3/serv_pop3.c | 31 +- citadel/modules/pop3client/serv_pop3client.c | 7 +- citadel/modules/rssclient/serv_rssclient.c | 8 +- citadel/modules/rwho/serv_rwho.c | 15 +- citadel/modules/sieve/serv_sieve.c | 16 +- citadel/modules/smtp/serv_smtp.c | 79 +- citadel/modules/spam/serv_spam.c | 7 +- citadel/modules/test/serv_test.c | 13 +- citadel/modules/upgrade/serv_upgrade.c | 7 +- citadel/modules/vandelay/serv_vandelay.c | 7 +- citadel/modules/vcard/serv_vcard.c | 93 +- citadel/serv_extensions.c | 14 - citadel/serv_extensions.h | 8 - citadel/server_main.c | 39 +- citadel/sysconfig.h | 2 +- citadel/sysdep.c | 809 +++++++++++------- citadel/sysdep_decls.h | 52 +- 43 files changed, 1008 insertions(+), 631 deletions(-) create mode 100644 citadel/modules/checkpoint/serv_checkpoint.c diff --git a/citadel/citserver.c b/citadel/citserver.c index 6a85744a5..cc27e4363 100644 --- a/citadel/citserver.c +++ b/citadel/citserver.c @@ -62,6 +62,9 @@ #include "snprintf.h" #endif +#include "ctdl_module.h" + + struct CitContext *ContextList = NULL; struct CitContext* next_session = NULL; char *unique_session_numbers; @@ -176,7 +179,6 @@ void master_startup(void) { */ void master_cleanup(int exitcode) { struct CleanupFunctionHook *fcn; - struct MaintenanceThreadHook *m_fcn; static int already_cleaning_up = 0; if (already_cleaning_up) while(1) sleep(1); @@ -190,12 +192,6 @@ void master_cleanup(int exitcode) { /* Close the AdjRefCount queue file */ AdjRefCount(-1, 0); - for (m_fcn = MaintenanceThreadHookTable; m_fcn != NULL; m_fcn = m_fcn->next) { - lprintf(CTDL_INFO, "Waiting for maintenance thread \"%s\" to shut down\n", m_fcn->name); - pthread_join(m_fcn->MaintenanceThread_tid, NULL); - } - - /* Close databases */ lprintf(CTDL_INFO, "Closing databases\n"); close_databases(); @@ -826,7 +822,7 @@ void cmd_down(char *argbuf) { { cprintf(Reply, CIT_OK + SERVER_SHUTTING_DOWN); } - time_to_die = 1; + CtdlThreadStopAll(); } /* @@ -837,7 +833,7 @@ void cmd_halt(void) { if (CtdlAccessCheck(ac_aide)) return; cprintf("%d Halting server. Goodbye.\n", CIT_OK); - time_to_die = 1; + CtdlThreadStopAll(); shutdown_and_halt = 1; } diff --git a/citadel/database.h b/citadel/database.h index b913faa87..c7fe3833e 100644 --- a/citadel/database.h +++ b/citadel/database.h @@ -17,6 +17,7 @@ void cdb_check_handles(void); void cdb_trunc(int cdb); void *checkpoint_thread(void *arg); void cdb_chmod_data(void); +void cdb_checkpoint(void); /* * Database records beginning with this magic number are assumed to diff --git a/citadel/database_sleepycat.c b/citadel/database_sleepycat.c index 2ca3bb922..ddd8edb12 100644 --- a/citadel/database_sleepycat.c +++ b/citadel/database_sleepycat.c @@ -280,16 +280,20 @@ void cmd_cull(char *argbuf) { /* * Request a checkpoint of the database. */ -static void cdb_checkpoint(void) +void cdb_checkpoint(void) { int ret; - static time_t last_run = 0L; +// static time_t last_run = 0L; /* Only do a checkpoint once per minute. */ +/* + * Don't need this any more, since the thread that calls us sleeps for 60 seconds between calls + if ((time(NULL) - last_run) < 60L) { return; } last_run = time(NULL); +*/ lprintf(CTDL_DEBUG, "-- db checkpoint --\n"); ret = dbenv->txn_checkpoint(dbenv, @@ -309,29 +313,6 @@ static void cdb_checkpoint(void) } -/* - * Main loop for the checkpoint thread. - */ -void *checkpoint_thread(void *arg) { - struct CitContext checkpointCC; - - lprintf(CTDL_DEBUG, "checkpoint_thread() initializing\n"); - - memset(&checkpointCC, 0, sizeof(struct CitContext)); - checkpointCC.internal_pgm = 1; - checkpointCC.cs_pid = 0; - pthread_setspecific(MyConKey, (void *)&checkpointCC ); - - cdb_allocate_tsd(); - - while (!time_to_die) { - cdb_checkpoint(); - sleep(1); - } - - lprintf(CTDL_DEBUG, "checkpoint_thread() exiting\n"); - pthread_exit(NULL); -} /* * Open the various databases we'll be using. Any database which @@ -461,7 +442,6 @@ void open_databases(void) cdb_allocate_tsd(); - CtdlRegisterMaintenanceThread ("checkpoint", checkpoint_thread); } diff --git a/citadel/housekeeping.c b/citadel/housekeeping.c index cbc8a1c42..1dd18d61e 100644 --- a/citadel/housekeeping.c +++ b/citadel/housekeeping.c @@ -43,6 +43,9 @@ #include "msgbase.h" #include "journaling.h" +#include "ctdl_module.h" + + /* * Terminate idle sessions. This function pounds through the session table * comparing the current time to each session's time-of-last-command. If an @@ -76,8 +79,7 @@ void terminate_idle_sessions(void) { void check_sched_shutdown(void) { if ((ScheduledShutdown == 1) && (ContextList == NULL)) { lprintf(CTDL_NOTICE, "Scheduled shutdown initiating.\n"); - time_to_die = 1; - master_cleanup(0); + CtdlThreadStopAll(); } } @@ -168,8 +170,6 @@ 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) { diff --git a/citadel/include/ctdl_module.h b/citadel/include/ctdl_module.h index 51d5c9c7f..587adb4a8 100644 --- a/citadel/include/ctdl_module.h +++ b/citadel/include/ctdl_module.h @@ -11,9 +11,9 @@ * define macros for module init stuff */ -#define CTDL_MODULE_INIT(module_name) char *ctdl_module_##module_name##_init (void) +#define CTDL_MODULE_INIT(module_name) char *ctdl_module_##module_name##_init (int threading) -#define CTDL_INIT_CALL(module_name) ctdl_module_##module_name##_init () +#define CTDL_INIT_CALL(module_name) ctdl_module_##module_name##_init (threading) /* @@ -104,4 +104,19 @@ int CtdlDoDirectoryServiceFunc(char *cn, char *ou, void **object, char *module, */ void CtdlModuleStartCryptoMsgs(char *ok_response, char *nosup_response, char *error_response); + +/* + * Citadel Threads API + */ +struct CtdlThreadNode *CtdlThreadCreate(char *name, long flags, void *(*thread_func) (void *arg), void *args); +void CtdlThreadSleep(int secs); +void CtdlThreadStop(struct CtdlThreadNode *thread); +int CtdlThreadCheckStop(void); +void CtdlThreadCancel(struct CtdlThreadNode *thread); +char *CtdlThreadName(struct CtdlThreadNode *thread, char *name); +struct CtdlThreadNode *CtdlThreadSelf(void); +int CtdlThreadGetCount(void); +void CtdlThreadGC(void); +void CtdlThreadStopAll(void); + #endif /* CTDL_MODULE_H */ diff --git a/citadel/mk_module_init.sh b/citadel/mk_module_init.sh index b75365be3..aaa13f145 100755 --- a/citadel/mk_module_init.sh +++ b/citadel/mk_module_init.sh @@ -79,12 +79,15 @@ extern long DetailErrorFlags; -void initialise_modules (void) +void initialise_modules (int threading) { long filter; nSizErrmsg = 0; - lprintf (CTDL_INFO, "New citadel module init proceedure.\n"); + if (threading) + CtdlLogPrintf (CTDL_INFO, "Initialise modules, CtdlThreads enabled.\n"); + else + CtdlLogPrintf (CTDL_INFO, "Initialise modules, CtdlThreads not yet enabled.\n"); EOF @@ -100,7 +103,7 @@ cat < $H_FILE #define MODULES_INIT_H #include "ctdl_module.h" extern size_t nSizErrmsg; -void initialise_modules (void); +void initialise_modules (int threading); EOF diff --git a/citadel/modules/autocompletion/serv_autocompletion.c b/citadel/modules/autocompletion/serv_autocompletion.c index 02f09969c..9faebfe9d 100644 --- a/citadel/modules/autocompletion/serv_autocompletion.c +++ b/citadel/modules/autocompletion/serv_autocompletion.c @@ -249,8 +249,10 @@ void cmd_auto(char *argbuf) { CTDL_MODULE_INIT(autocompletion) { - CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion"); - + if (!threading) + { + CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion"); + } /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/bio/serv_bio.c b/citadel/modules/bio/serv_bio.c index 8f875a435..276e7d981 100644 --- a/citadel/modules/bio/serv_bio.c +++ b/citadel/modules/bio/serv_bio.c @@ -134,10 +134,12 @@ void cmd_lbio(char *cmdbuf) { CTDL_MODULE_INIT(bio) { - CtdlRegisterProtoHook(cmd_ebio, "EBIO", "Enter your bio"); - CtdlRegisterProtoHook(cmd_rbio, "RBIO", "Read a user's bio"); - CtdlRegisterProtoHook(cmd_lbio, "LBIO", "List users with bios"); - + if (!threading) + { + CtdlRegisterProtoHook(cmd_ebio, "EBIO", "Enter your bio"); + CtdlRegisterProtoHook(cmd_rbio, "RBIO", "Read a user's bio"); + CtdlRegisterProtoHook(cmd_lbio, "LBIO", "List users with bios"); + } /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/calendar/serv_calendar.c b/citadel/modules/calendar/serv_calendar.c index cd55a1339..a56099337 100644 --- a/citadel/modules/calendar/serv_calendar.c +++ b/citadel/modules/calendar/serv_calendar.c @@ -2167,16 +2167,19 @@ void ical_fixed_output(char *ptr, int len) { */ CTDL_MODULE_INIT(calendar) { + if (!threading) + { #ifdef CITADEL_WITH_CALENDAR_SERVICE - CtdlRegisterMessageHook(ical_obj_beforesave, EVT_BEFORESAVE); - CtdlRegisterMessageHook(ical_obj_aftersave, EVT_AFTERSAVE); - CtdlRegisterSessionHook(ical_create_room, EVT_LOGIN); - CtdlRegisterProtoHook(cmd_ical, "ICAL", "Citadel iCal commands"); - CtdlRegisterSessionHook(ical_session_startup, EVT_START); - CtdlRegisterSessionHook(ical_session_shutdown, EVT_STOP); - CtdlRegisterFixedOutputHook("text/calendar", ical_fixed_output); + CtdlRegisterMessageHook(ical_obj_beforesave, EVT_BEFORESAVE); + CtdlRegisterMessageHook(ical_obj_aftersave, EVT_AFTERSAVE); + CtdlRegisterSessionHook(ical_create_room, EVT_LOGIN); + CtdlRegisterProtoHook(cmd_ical, "ICAL", "Citadel iCal commands"); + CtdlRegisterSessionHook(ical_session_startup, EVT_START); + CtdlRegisterSessionHook(ical_session_shutdown, EVT_STOP); + CtdlRegisterFixedOutputHook("text/calendar", ical_fixed_output); #endif - + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/chat/serv_chat.c b/citadel/modules/chat/serv_chat.c index b62f48524..c77b36ab9 100644 --- a/citadel/modules/chat/serv_chat.c +++ b/citadel/modules/chat/serv_chat.c @@ -815,16 +815,19 @@ void cmd_reqt(char *argbuf) { CTDL_MODULE_INIT(chat) { - CtdlRegisterProtoHook(cmd_chat, "CHAT", "Begin real-time chat"); - CtdlRegisterProtoHook(cmd_pexp, "PEXP", "Poll for instant messages"); - CtdlRegisterProtoHook(cmd_gexp, "GEXP", "Get instant messages"); - CtdlRegisterProtoHook(cmd_sexp, "SEXP", "Send an instant message"); - CtdlRegisterProtoHook(cmd_dexp, "DEXP", "Disable instant messages"); - CtdlRegisterProtoHook(cmd_reqt, "REQT", "Request client termination"); - CtdlRegisterSessionHook(cmd_gexp_async, EVT_ASYNC); - CtdlRegisterSessionHook(delete_instant_messages, EVT_STOP); - CtdlRegisterXmsgHook(send_instant_message, XMSG_PRI_LOCAL); - + if (!threading) + { + CtdlRegisterProtoHook(cmd_chat, "CHAT", "Begin real-time chat"); + CtdlRegisterProtoHook(cmd_pexp, "PEXP", "Poll for instant messages"); + CtdlRegisterProtoHook(cmd_gexp, "GEXP", "Get instant messages"); + CtdlRegisterProtoHook(cmd_sexp, "SEXP", "Send an instant message"); + CtdlRegisterProtoHook(cmd_dexp, "DEXP", "Disable instant messages"); + CtdlRegisterProtoHook(cmd_reqt, "REQT", "Request client termination"); + CtdlRegisterSessionHook(cmd_gexp_async, EVT_ASYNC); + CtdlRegisterSessionHook(delete_instant_messages, EVT_STOP); + CtdlRegisterXmsgHook(send_instant_message, XMSG_PRI_LOCAL); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/checkpoint/serv_checkpoint.c b/citadel/modules/checkpoint/serv_checkpoint.c new file mode 100644 index 000000000..819f1989b --- /dev/null +++ b/citadel/modules/checkpoint/serv_checkpoint.c @@ -0,0 +1,73 @@ +/* + * $Id: serv_checkpoint.c 5756 2007-11-16 17:15:22Z ajc $ + * + * checkpointing module for the database + */ + +#include "sysdep.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DB_H +#include +#elif defined(HAVE_DB4_DB_H) +#include +#else +#error Neither nor was found by configure. Install db4-devel. +#endif + + +#if DB_VERSION_MAJOR < 4 || DB_VERSION_MINOR < 1 +#error Citadel requires Berkeley DB v4.1 or newer. Please upgrade. +#endif + +#include "citadel.h" +#include "server.h" +#include "citserver.h" +#include "database.h" +#include "msgbase.h" +#include "sysdep_decls.h" +#include "config.h" + +#include "ctdl_module.h" + +/* + * Main loop for the checkpoint thread. + */ +void *checkpoint_thread(void *arg) { + struct CitContext checkpointCC; + + CtdlLogPrintf(CTDL_DEBUG, "checkpoint_thread() initializing\n"); + + memset(&checkpointCC, 0, sizeof(struct CitContext)); + checkpointCC.internal_pgm = 1; + checkpointCC.cs_pid = 0; + pthread_setspecific(MyConKey, (void *)&checkpointCC ); + + cdb_allocate_tsd(); + + while (!CtdlThreadCheckStop()) { + cdb_checkpoint(); + CtdlThreadSleep(60); + } + + CtdlLogPrintf(CTDL_DEBUG, "checkpoint_thread() exiting\n"); + return NULL; +} + + +CTDL_MODULE_INIT(checkpoint) { + if (threading) + { + CtdlThreadCreate ("checkpoint", CTDLTHREAD_BIGSTACK, checkpoint_thread, NULL); + } + /* return our Subversion id for the Log */ + return "$Id: serv_autocompletion.c 5756 2007-11-16 17:15:22Z ajc $"; +} diff --git a/citadel/modules/expire/serv_expire.c b/citadel/modules/expire/serv_expire.c index 254ee5a10..4902af16b 100644 --- a/citadel/modules/expire/serv_expire.c +++ b/citadel/modules/expire/serv_expire.c @@ -844,9 +844,12 @@ void cmd_fsck(char *argbuf) { CTDL_MODULE_INIT(expire) { - CtdlRegisterSessionHook(purge_databases, EVT_TIMER); - CtdlRegisterProtoHook(cmd_fsck, "FSCK", "Check message ref counts"); - + if (!threading) + { + CtdlRegisterSessionHook(purge_databases, EVT_TIMER); + CtdlRegisterProtoHook(cmd_fsck, "FSCK", "Check message ref counts"); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/fulltext/serv_fulltext.c b/citadel/modules/fulltext/serv_fulltext.c index 5ba8b5a37..4a8bdc644 100644 --- a/citadel/modules/fulltext/serv_fulltext.c +++ b/citadel/modules/fulltext/serv_fulltext.c @@ -233,9 +233,13 @@ void do_fulltext_indexing(void) { * Make sure we don't run the indexer too frequently. * FIXME move the setting into config */ +/* + * The thread sleeps for 300 seconds so we don't need this here any more + if ( (time(NULL) - last_index) < 300L) { return; } +*/ /* * Check to see whether the fulltext index is up to date; if there @@ -293,7 +297,7 @@ void do_fulltext_indexing(void) { ft_index_message(ft_newmsgs[i], 1); /* Check to see if we need to quit early */ - if (time_to_die) { + if (CtdlThreadCheckStop()) { lprintf(CTDL_DEBUG, "Indexer quitting early\n"); ft_newhighest = ft_newmsgs[i]; break; @@ -344,13 +348,13 @@ void *indexer_thread(void *arg) { cdb_allocate_tsd(); - while (!time_to_die) { + while (!CtdlThreadCheckStop()) { do_fulltext_indexing(); - sleep(1); + CtdlThreadSleep(300); } lprintf(CTDL_DEBUG, "indexer_thread() exiting\n"); - pthread_exit(NULL); + return NULL; } @@ -486,12 +490,17 @@ void ft_delete_remove(char *room, long msgnum) CTDL_MODULE_INIT(fulltext) { - initialize_ft_cache(); - CtdlRegisterProtoHook(cmd_srch, "SRCH", "Full text search"); - CtdlRegisterDeleteHook(ft_delete_remove); - CtdlRegisterSearchFuncHook(ft_search, "fulltext"); - CtdlRegisterMaintenanceThread ("indexer", indexer_thread); - + if (!threading) + { + initialize_ft_cache(); + CtdlRegisterProtoHook(cmd_srch, "SRCH", "Full text search"); + CtdlRegisterDeleteHook(ft_delete_remove); + CtdlRegisterSearchFuncHook(ft_search, "fulltext"); + } + else + { + CtdlThreadCreate("indexer", CTDLTHREAD_BIGSTACK, indexer_thread, NULL); + } /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/funambol/serv_funambol.c b/citadel/modules/funambol/serv_funambol.c index 23b17b470..e60c64a69 100644 --- a/citadel/modules/funambol/serv_funambol.c +++ b/citadel/modules/funambol/serv_funambol.c @@ -249,9 +249,11 @@ int funambol_isAllowedByPrefs(long configMsgNum) { CTDL_MODULE_INIT(funambol) { - create_notify_queue(); - CtdlRegisterSessionHook(do_notify_queue, EVT_TIMER); - + if (!threading) + { + create_notify_queue(); + CtdlRegisterSessionHook(do_notify_queue, EVT_TIMER); + } /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/imap/serv_imap.c b/citadel/modules/imap/serv_imap.c index cd138b86d..31b3e5c96 100644 --- a/citadel/modules/imap/serv_imap.c +++ b/citadel/modules/imap/serv_imap.c @@ -1596,14 +1596,17 @@ const char *CitadelServiceIMAPS="IMAPS"; */ CTDL_MODULE_INIT(imap) { - CtdlRegisterServiceHook(config.c_imap_port, - NULL, imap_greeting, imap_command_loop, NULL, CitadelServiceIMAP); + if (!threading) + { + CtdlRegisterServiceHook(config.c_imap_port, + NULL, imap_greeting, imap_command_loop, NULL, CitadelServiceIMAP); #ifdef HAVE_OPENSSL - CtdlRegisterServiceHook(config.c_imaps_port, - NULL, imaps_greeting, imap_command_loop, NULL, CitadelServiceIMAPS); + CtdlRegisterServiceHook(config.c_imaps_port, + NULL, imaps_greeting, imap_command_loop, NULL, CitadelServiceIMAPS); #endif - CtdlRegisterSessionHook(imap_cleanup_function, EVT_STOP); - + CtdlRegisterSessionHook(imap_cleanup_function, EVT_STOP); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/inetcfg/serv_inetcfg.c b/citadel/modules/inetcfg/serv_inetcfg.c index 8fb48f19a..06e61c88c 100644 --- a/citadel/modules/inetcfg/serv_inetcfg.c +++ b/citadel/modules/inetcfg/serv_inetcfg.c @@ -178,9 +178,12 @@ void inetcfg_init(void) { CTDL_MODULE_INIT(inetcfg) { - CtdlRegisterMessageHook(inetcfg_aftersave, EVT_AFTERSAVE); - inetcfg_init(); - + if (!threading) + { + CtdlRegisterMessageHook(inetcfg_aftersave, EVT_AFTERSAVE); + inetcfg_init(); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/ldap/serv_ldap.c b/citadel/modules/ldap/serv_ldap.c index c2b786529..ea84e6cdd 100644 --- a/citadel/modules/ldap/serv_ldap.c +++ b/citadel/modules/ldap/serv_ldap.c @@ -539,32 +539,35 @@ void ldap_disconnect_timer(void) */ CTDL_MODULE_INIT(ldap) { + if (!threading) + { #ifdef HAVE_LDAP - if (!IsEmptyStr(config.c_ldap_base_dn)) { - CtdlRegisterCleanupHook(serv_ldap_cleanup); - CtdlRegisterSessionHook(ldap_disconnect_timer, EVT_TIMER); - CtdlRegisterDirectoryServiceFunc(delete_from_ldap, - DIRECTORY_USER_DEL, - "ldap"); - CtdlRegisterDirectoryServiceFunc(create_ldap_host_OU, - DIRECTORY_CREATE_HOST, - "ldap"); - CtdlRegisterDirectoryServiceFunc(create_ldap_object, - DIRECTORY_CREATE_OBJECT, - "ldap"); - CtdlRegisterDirectoryServiceFunc(add_ldap_object, - DIRECTORY_ATTRIB_ADD, - "ldap"); - CtdlRegisterDirectoryServiceFunc(save_ldap_object, - DIRECTORY_SAVE_OBJECT, - "ldap"); - CtdlRegisterDirectoryServiceFunc(free_ldap_object, - DIRECTORY_FREE_OBJECT, - "ldap"); - create_ldap_root(); - } + if (!IsEmptyStr(config.c_ldap_base_dn)) { + CtdlRegisterCleanupHook(serv_ldap_cleanup); + CtdlRegisterSessionHook(ldap_disconnect_timer, EVT_TIMER); + CtdlRegisterDirectoryServiceFunc(delete_from_ldap, + DIRECTORY_USER_DEL, + "ldap"); + CtdlRegisterDirectoryServiceFunc(create_ldap_host_OU, + DIRECTORY_CREATE_HOST, + "ldap"); + CtdlRegisterDirectoryServiceFunc(create_ldap_object, + DIRECTORY_CREATE_OBJECT, + "ldap"); + CtdlRegisterDirectoryServiceFunc(add_ldap_object, + DIRECTORY_ATTRIB_ADD, + "ldap"); + CtdlRegisterDirectoryServiceFunc(save_ldap_object, + DIRECTORY_SAVE_OBJECT, + "ldap"); + CtdlRegisterDirectoryServiceFunc(free_ldap_object, + DIRECTORY_FREE_OBJECT, + "ldap"); + create_ldap_root(); + } #endif /* HAVE_LDAP */ - + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/listsub/serv_listsub.c b/citadel/modules/listsub/serv_listsub.c index 0eb2300f2..615bcc5b5 100644 --- a/citadel/modules/listsub/serv_listsub.c +++ b/citadel/modules/listsub/serv_listsub.c @@ -570,8 +570,11 @@ void cmd_subs(char *cmdbuf) { */ CTDL_MODULE_INIT(listsub) { - CtdlRegisterProtoHook(cmd_subs, "SUBS", "List subscribe/unsubscribe"); - + if (!threading) + { + CtdlRegisterProtoHook(cmd_subs, "SUBS", "List subscribe/unsubscribe"); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/managesieve/serv_managesieve.c b/citadel/modules/managesieve/serv_managesieve.c index e40007d3f..f05966c82 100644 --- a/citadel/modules/managesieve/serv_managesieve.c +++ b/citadel/modules/managesieve/serv_managesieve.c @@ -585,22 +585,24 @@ void managesieve_cleanup_function(void) { const char* CitadelServiceManageSieve = "ManageSieve"; CTDL_MODULE_INIT(managesieve) { - + if (!threading) + { #ifdef HAVE_LIBSIEVE - CtdlRegisterServiceHook(config.c_managesieve_port, - NULL, - managesieve_greeting, - managesieve_command_loop, - NULL, - CitadelServiceManageSieve); - CtdlRegisterSessionHook(managesieve_cleanup_function, EVT_STOP); + CtdlRegisterServiceHook(config.c_managesieve_port, + NULL, + managesieve_greeting, + managesieve_command_loop, + NULL, + CitadelServiceManageSieve); + CtdlRegisterSessionHook(managesieve_cleanup_function, EVT_STOP); #else /* HAVE_LIBSIEVE */ - lprintf(CTDL_INFO, "This server is missing libsieve. Managesieve protocol is disabled..\n"); + lprintf(CTDL_INFO, "This server is missing libsieve. Managesieve protocol is disabled..\n"); #endif /* HAVE_LIBSIEVE */ - + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/mrtg/serv_mrtg.c b/citadel/modules/mrtg/serv_mrtg.c index dab0ab7b8..dc3adfc30 100644 --- a/citadel/modules/mrtg/serv_mrtg.c +++ b/citadel/modules/mrtg/serv_mrtg.c @@ -130,8 +130,11 @@ void cmd_mrtg(char *argbuf) { CTDL_MODULE_INIT(mrtg) { - CtdlRegisterProtoHook(cmd_mrtg, "MRTG", "Supply stats to MRTG"); - + if (!threading) + { + CtdlRegisterProtoHook(cmd_mrtg, "MRTG", "Supply stats to MRTG"); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/netfilter/serv_netfilter.c b/citadel/modules/netfilter/serv_netfilter.c index 09fe8c0f9..026df8ffc 100644 --- a/citadel/modules/netfilter/serv_netfilter.c +++ b/citadel/modules/netfilter/serv_netfilter.c @@ -107,8 +107,11 @@ int filter_the_idiots(struct CtdlMessage *msg, char *target_room) { CTDL_MODULE_INIT(netfilter) { - CtdlRegisterNetprocHook(filter_the_idiots); - + if (!threading) + { + CtdlRegisterNetprocHook(filter_the_idiots); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/network/serv_network.c b/citadel/modules/network/serv_network.c index 06363b92d..ad8618406 100644 --- a/citadel/modules/network/serv_network.c +++ b/citadel/modules/network/serv_network.c @@ -2112,15 +2112,17 @@ int network_room_handler (struct ctdlroom *room) */ CTDL_MODULE_INIT(network) { - create_spool_dirs(); - CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config"); - CtdlRegisterProtoHook(cmd_snet, "SNET", "Set network config"); - CtdlRegisterProtoHook(cmd_netp, "NETP", "Identify as network poller"); - CtdlRegisterProtoHook(cmd_nsyn, "NSYN", "Synchronize room to node"); - CtdlRegisterSessionHook(network_do_queue, EVT_TIMER); - CtdlRegisterRoomHook(network_room_handler); - CtdlRegisterCleanupHook(destroy_network_queue_room); - + if (!threading) + { + create_spool_dirs(); + CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config"); + CtdlRegisterProtoHook(cmd_snet, "SNET", "Set network config"); + CtdlRegisterProtoHook(cmd_netp, "NETP", "Identify as network poller"); + CtdlRegisterProtoHook(cmd_nsyn, "NSYN", "Synchronize room to node"); + CtdlRegisterSessionHook(network_do_queue, EVT_TIMER); + CtdlRegisterRoomHook(network_room_handler); + CtdlRegisterCleanupHook(destroy_network_queue_room); + } /* return our Subversion id for the Log */ return "$Id$"; diff --git a/citadel/modules/newuser/serv_newuser.c b/citadel/modules/newuser/serv_newuser.c index ac47feb10..afcea7e54 100644 --- a/citadel/modules/newuser/serv_newuser.c +++ b/citadel/modules/newuser/serv_newuser.c @@ -97,8 +97,11 @@ void CopyNewUserGreetings(void) { CTDL_MODULE_INIT(newuser) { - CtdlRegisterSessionHook(CopyNewUserGreetings, EVT_LOGIN); - - /* return our Subversion id for the Log */ - return "$Id$"; + if (!threading) + { + CtdlRegisterSessionHook(CopyNewUserGreetings, EVT_LOGIN); + } + + /* return our Subversion id for the Log */ + return "$Id$"; } diff --git a/citadel/modules/notes/serv_notes.c b/citadel/modules/notes/serv_notes.c index 0dedda9e3..c7a613c04 100644 --- a/citadel/modules/notes/serv_notes.c +++ b/citadel/modules/notes/serv_notes.c @@ -106,8 +106,11 @@ int serv_notes_beforesave(struct CtdlMessage *msg) CTDL_MODULE_INIT(notes) { - CtdlRegisterMessageHook(serv_notes_beforesave, EVT_BEFORESAVE); - + if (!threading) + { + CtdlRegisterMessageHook(serv_notes_beforesave, EVT_BEFORESAVE); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/pager/serv_pager.c b/citadel/modules/pager/serv_pager.c index bdafbc85f..3570856e4 100644 --- a/citadel/modules/pager/serv_pager.c +++ b/citadel/modules/pager/serv_pager.c @@ -228,9 +228,12 @@ char *pager_getUserPhoneNumber(long configMsgNum) { } CTDL_MODULE_INIT(pager) { - create_pager_queue(); - CtdlRegisterSessionHook(do_pager_queue, EVT_TIMER); - + if (!threading) + { + create_pager_queue(); + CtdlRegisterSessionHook(do_pager_queue, EVT_TIMER); + } + /* return our Subversion id for the Log */ return "$Id: serv_pager.c $"; } diff --git a/citadel/modules/pas2/serv_pas2.c b/citadel/modules/pas2/serv_pas2.c index 7df51d60d..63bd213ae 100644 --- a/citadel/modules/pas2/serv_pas2.c +++ b/citadel/modules/pas2/serv_pas2.c @@ -88,8 +88,11 @@ void cmd_pas2(char *argbuf) CTDL_MODULE_INIT(pas2) { - CtdlRegisterProtoHook(cmd_pas2, "PAS2", "APOP-based login"); - + if (!threading) + { + CtdlRegisterProtoHook(cmd_pas2, "PAS2", "APOP-based login"); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/pop3/serv_pop3.c b/citadel/modules/pop3/serv_pop3.c index dcdbb8ec6..336926c58 100644 --- a/citadel/modules/pop3/serv_pop3.c +++ b/citadel/modules/pop3/serv_pop3.c @@ -716,22 +716,25 @@ const char *CitadelServicePop3S="POP3S"; CTDL_MODULE_INIT(pop3) { - CtdlRegisterServiceHook(config.c_pop3_port, - NULL, - pop3_greeting, - pop3_command_loop, - NULL, - CitadelServicePop3); + if(!threading) + { + CtdlRegisterServiceHook(config.c_pop3_port, + NULL, + pop3_greeting, + pop3_command_loop, + NULL, + CitadelServicePop3); #ifdef HAVE_OPENSSL - CtdlRegisterServiceHook(config.c_pop3s_port, - NULL, - pop3s_greeting, - pop3_command_loop, - NULL, - CitadelServicePop3S); + CtdlRegisterServiceHook(config.c_pop3s_port, + NULL, + pop3s_greeting, + pop3_command_loop, + NULL, + CitadelServicePop3S); #endif - CtdlRegisterSessionHook(pop3_cleanup_function, EVT_STOP); - + CtdlRegisterSessionHook(pop3_cleanup_function, EVT_STOP); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/pop3client/serv_pop3client.c b/citadel/modules/pop3client/serv_pop3client.c index 5046a985d..b3227707b 100644 --- a/citadel/modules/pop3client/serv_pop3client.c +++ b/citadel/modules/pop3client/serv_pop3client.c @@ -287,8 +287,11 @@ void pop3client_scan(void) { CTDL_MODULE_INIT(pop3client) { - CtdlRegisterSessionHook(pop3client_scan, EVT_TIMER); - + if (!threading) + { + CtdlRegisterSessionHook(pop3client_scan, EVT_TIMER); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/rssclient/serv_rssclient.c b/citadel/modules/rssclient/serv_rssclient.c index 868516907..c9461a673 100644 --- a/citadel/modules/rssclient/serv_rssclient.c +++ b/citadel/modules/rssclient/serv_rssclient.c @@ -622,11 +622,15 @@ void rssclient_scan(void) { CTDL_MODULE_INIT(rssclient) { + if (!threading) + { #ifdef HAVE_EXPAT - CtdlRegisterSessionHook(rssclient_scan, EVT_TIMER); + CtdlRegisterSessionHook(rssclient_scan, EVT_TIMER); #else - lprintf(CTDL_INFO, "This server is missing the Expat XML parser. RSS client will be disabled.\n"); + lprintf(CTDL_INFO, "This server is missing the Expat XML parser. RSS client will be disabled.\n"); #endif + } + /* return our Subversion id for the Log */ return "$Id: serv_rssclient.c 5652 2007-10-29 20:14:48Z ajc $"; } diff --git a/citadel/modules/rwho/serv_rwho.c b/citadel/modules/rwho/serv_rwho.c index 1c9f7a7a4..16746dfe9 100644 --- a/citadel/modules/rwho/serv_rwho.c +++ b/citadel/modules/rwho/serv_rwho.c @@ -255,12 +255,15 @@ void cmd_stel(char *cmdbuf) CTDL_MODULE_INIT(rwho) { - CtdlRegisterProtoHook(cmd_rwho, "RWHO", "Display who is online"); - CtdlRegisterProtoHook(cmd_hchg, "HCHG", "Masquerade hostname"); - CtdlRegisterProtoHook(cmd_rchg, "RCHG", "Masquerade roomname"); - CtdlRegisterProtoHook(cmd_uchg, "UCHG", "Masquerade username"); - CtdlRegisterProtoHook(cmd_stel, "STEL", "Enter/exit stealth mode"); - + if(!threading) + { + CtdlRegisterProtoHook(cmd_rwho, "RWHO", "Display who is online"); + CtdlRegisterProtoHook(cmd_hchg, "HCHG", "Masquerade hostname"); + CtdlRegisterProtoHook(cmd_rchg, "RCHG", "Masquerade roomname"); + CtdlRegisterProtoHook(cmd_uchg, "UCHG", "Masquerade username"); + CtdlRegisterProtoHook(cmd_stel, "STEL", "Enter/exit stealth mode"); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/sieve/serv_sieve.c b/citadel/modules/sieve/serv_sieve.c index e92ebdc8f..e6cf9aebf 100644 --- a/citadel/modules/sieve/serv_sieve.c +++ b/citadel/modules/sieve/serv_sieve.c @@ -1292,22 +1292,24 @@ int serv_sieve_room(struct ctdlroom *room) CTDL_MODULE_INIT(sieve) { - + if (!threading) + { #ifdef HAVE_LIBSIEVE - ctdl_sieve_init(); - CtdlRegisterProtoHook(cmd_msiv, "MSIV", "Manage Sieve scripts"); + ctdl_sieve_init(); + CtdlRegisterProtoHook(cmd_msiv, "MSIV", "Manage Sieve scripts"); - CtdlRegisterRoomHook(serv_sieve_room); + CtdlRegisterRoomHook(serv_sieve_room); - CtdlRegisterSessionHook(perform_sieve_processing, EVT_HOUSE); + CtdlRegisterSessionHook(perform_sieve_processing, EVT_HOUSE); #else /* HAVE_LIBSIEVE */ - lprintf(CTDL_INFO, "This server is missing libsieve. Mailbox filtering will be disabled.\n"); + lprintf(CTDL_INFO, "This server is missing libsieve. Mailbox filtering will be disabled.\n"); #endif /* HAVE_LIBSIEVE */ - + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/smtp/serv_smtp.c b/citadel/modules/smtp/serv_smtp.c index 77b2618ea..0376c1e47 100644 --- a/citadel/modules/smtp/serv_smtp.c +++ b/citadel/modules/smtp/serv_smtp.c @@ -1814,48 +1814,51 @@ const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF"; CTDL_MODULE_INIT(smtp) { - CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */ - NULL, - smtp_mta_greeting, - smtp_command_loop, - NULL, - CitadelServiceSMTP_MTA); + if (!threading) + { + CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */ + NULL, + smtp_mta_greeting, + smtp_command_loop, + NULL, + CitadelServiceSMTP_MTA); #ifdef HAVE_OPENSSL - CtdlRegisterServiceHook(config.c_smtps_port, - NULL, - smtps_greeting, - smtp_command_loop, - NULL, - CitadelServiceSMTPS_MTA); + CtdlRegisterServiceHook(config.c_smtps_port, + NULL, + smtps_greeting, + smtp_command_loop, + NULL, + CitadelServiceSMTPS_MTA); #endif - CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */ - NULL, - smtp_msa_greeting, - smtp_command_loop, - NULL, - CitadelServiceSMTP_MSA); - - CtdlRegisterServiceHook(0, /* local LMTP */ - file_lmtp_socket, - lmtp_greeting, - smtp_command_loop, - NULL, - CitadelServiceSMTP_LMTP); - - CtdlRegisterServiceHook(0, /* local LMTP */ - file_lmtp_unfiltered_socket, - lmtp_unfiltered_greeting, - smtp_command_loop, - NULL, - CitadelServiceSMTP_LMTP_UNF); - - smtp_init_spoolout(); - CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER); - CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP); - CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands"); - + CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */ + NULL, + smtp_msa_greeting, + smtp_command_loop, + NULL, + CitadelServiceSMTP_MSA); + + CtdlRegisterServiceHook(0, /* local LMTP */ + file_lmtp_socket, + lmtp_greeting, + smtp_command_loop, + NULL, + CitadelServiceSMTP_LMTP); + + CtdlRegisterServiceHook(0, /* local LMTP */ + file_lmtp_unfiltered_socket, + lmtp_unfiltered_greeting, + smtp_command_loop, + NULL, + CitadelServiceSMTP_LMTP_UNF); + + smtp_init_spoolout(); + CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER); + CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP); + CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands"); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/spam/serv_spam.c b/citadel/modules/spam/serv_spam.c index 415f03cae..6ad702944 100644 --- a/citadel/modules/spam/serv_spam.c +++ b/citadel/modules/spam/serv_spam.c @@ -148,8 +148,11 @@ bail: close(sock); CTDL_MODULE_INIT(spam) { - CtdlRegisterMessageHook(spam_assassin, EVT_SMTPSCAN); - + if (!threading) + { + CtdlRegisterMessageHook(spam_assassin, EVT_SMTPSCAN); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/test/serv_test.c b/citadel/modules/test/serv_test.c index 356b167b5..af49e8d32 100644 --- a/citadel/modules/test/serv_test.c +++ b/citadel/modules/test/serv_test.c @@ -59,11 +59,14 @@ void LoginTest(void) { CTDL_MODULE_INIT(test) { #if 0 - CtdlRegisterCleanupHook(CleanupTest); - CtdlRegisterSessionHook(NewRoomTest, EVT_NEWROOM); - CtdlRegisterSessionHook(SessionStartTest, EVT_START); - CtdlRegisterSessionHook(SessionStopTest, EVT_STOP); - CtdlRegisterSessionHook(LoginTest, EVT_LOGIN); + if (!threading) + { + CtdlRegisterCleanupHook(CleanupTest); + CtdlRegisterSessionHook(NewRoomTest, EVT_NEWROOM); + CtdlRegisterSessionHook(SessionStartTest, EVT_START); + CtdlRegisterSessionHook(SessionStopTest, EVT_STOP); + CtdlRegisterSessionHook(LoginTest, EVT_LOGIN); + } #endif /* return our Subversion id for the Log */ diff --git a/citadel/modules/upgrade/serv_upgrade.c b/citadel/modules/upgrade/serv_upgrade.c index 8676ab9ae..718836556 100644 --- a/citadel/modules/upgrade/serv_upgrade.c +++ b/citadel/modules/upgrade/serv_upgrade.c @@ -228,8 +228,11 @@ void check_server_upgrades(void) { CTDL_MODULE_INIT(upgrade) { - check_server_upgrades(); - + if (!threading) + { + check_server_upgrades(); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/vandelay/serv_vandelay.c b/citadel/modules/vandelay/serv_vandelay.c index 576089f3c..2464d9e39 100644 --- a/citadel/modules/vandelay/serv_vandelay.c +++ b/citadel/modules/vandelay/serv_vandelay.c @@ -806,8 +806,11 @@ void cmd_artv(char *cmdbuf) { CTDL_MODULE_INIT(vandelay) { - CtdlRegisterProtoHook(cmd_artv, "ARTV", "import/export data store"); - + if (!threading) + { + CtdlRegisterProtoHook(cmd_artv, "ARTV", "import/export data store"); + } + /* return our Subversion id for the Log */ return "$Id$"; } diff --git a/citadel/modules/vcard/serv_vcard.c b/citadel/modules/vcard/serv_vcard.c index 7cb8d6d53..7580c0d2a 100644 --- a/citadel/modules/vcard/serv_vcard.c +++ b/citadel/modules/vcard/serv_vcard.c @@ -1527,53 +1527,56 @@ CTDL_MODULE_INIT(vcard) char filename[256]; FILE *fp; - CtdlRegisterSessionHook(vcard_session_login_hook, EVT_LOGIN); - CtdlRegisterMessageHook(vcard_upload_beforesave, EVT_BEFORESAVE); - CtdlRegisterMessageHook(vcard_upload_aftersave, EVT_AFTERSAVE); - CtdlRegisterDeleteHook(vcard_delete_remove); - CtdlRegisterProtoHook(cmd_regi, "REGI", "Enter registration info"); - CtdlRegisterProtoHook(cmd_greg, "GREG", "Get registration info"); - CtdlRegisterProtoHook(cmd_igab, "IGAB", - "Initialize Global Address Book"); - CtdlRegisterProtoHook(cmd_qdir, "QDIR", "Query Directory"); - CtdlRegisterProtoHook(cmd_gvsn, "GVSN", "Get Valid Screen Names"); - CtdlRegisterProtoHook(cmd_gvea, "GVEA", "Get Valid Email Addresses"); - CtdlRegisterProtoHook(cmd_dvca, "DVCA", "Dump VCard Addresses"); - CtdlRegisterUserHook(vcard_newuser, EVT_NEWUSER); - CtdlRegisterUserHook(vcard_purge, EVT_PURGEUSER); - CtdlRegisterNetprocHook(vcard_extract_from_network); - CtdlRegisterSessionHook(store_harvested_addresses, EVT_TIMER); - CtdlRegisterFixedOutputHook("text/x-vcard", vcard_fixed_output); - CtdlRegisterFixedOutputHook("text/vcard", vcard_fixed_output); - - /* Create the Global ADdress Book room if necessary */ - create_room(ADDRESS_BOOK_ROOM, 3, "", 0, 1, 0, VIEW_ADDRESSBOOK); + if (!threading) + { + CtdlRegisterSessionHook(vcard_session_login_hook, EVT_LOGIN); + CtdlRegisterMessageHook(vcard_upload_beforesave, EVT_BEFORESAVE); + CtdlRegisterMessageHook(vcard_upload_aftersave, EVT_AFTERSAVE); + CtdlRegisterDeleteHook(vcard_delete_remove); + CtdlRegisterProtoHook(cmd_regi, "REGI", "Enter registration info"); + CtdlRegisterProtoHook(cmd_greg, "GREG", "Get registration info"); + CtdlRegisterProtoHook(cmd_igab, "IGAB", + "Initialize Global Address Book"); + CtdlRegisterProtoHook(cmd_qdir, "QDIR", "Query Directory"); + CtdlRegisterProtoHook(cmd_gvsn, "GVSN", "Get Valid Screen Names"); + CtdlRegisterProtoHook(cmd_gvea, "GVEA", "Get Valid Email Addresses"); + CtdlRegisterProtoHook(cmd_dvca, "DVCA", "Dump VCard Addresses"); + CtdlRegisterUserHook(vcard_newuser, EVT_NEWUSER); + CtdlRegisterUserHook(vcard_purge, EVT_PURGEUSER); + CtdlRegisterNetprocHook(vcard_extract_from_network); + CtdlRegisterSessionHook(store_harvested_addresses, EVT_TIMER); + CtdlRegisterFixedOutputHook("text/x-vcard", vcard_fixed_output); + CtdlRegisterFixedOutputHook("text/vcard", vcard_fixed_output); + + /* Create the Global ADdress Book room if necessary */ + create_room(ADDRESS_BOOK_ROOM, 3, "", 0, 1, 0, VIEW_ADDRESSBOOK); + + /* Set expiration policy to manual; otherwise objects will be lost! */ + if (!lgetroom(&qr, ADDRESS_BOOK_ROOM)) { + qr.QRep.expire_mode = EXPIRE_MANUAL; + qr.QRdefaultview = VIEW_ADDRESSBOOK; /* 2 = address book view */ + lputroom(&qr); - /* Set expiration policy to manual; otherwise objects will be lost! */ - if (!lgetroom(&qr, ADDRESS_BOOK_ROOM)) { - qr.QRep.expire_mode = EXPIRE_MANUAL; - qr.QRdefaultview = VIEW_ADDRESSBOOK; /* 2 = address book view */ - lputroom(&qr); - - /* - * Also make sure it has a netconfig file, so the networker runs - * on this room even if we don't share it with any other nodes. - * This allows the CANCEL messages (i.e. "Purge this vCard") to be - * purged. - */ - assoc_file_name(filename, sizeof filename, &qr, ctdl_netcfg_dir); - fp = fopen(filename, "a"); - if (fp != NULL) fclose(fp); - chown(filename, CTDLUID, (-1)); - } + /* + * Also make sure it has a netconfig file, so the networker runs + * on this room even if we don't share it with any other nodes. + * This allows the CANCEL messages (i.e. "Purge this vCard") to be + * purged. + */ + assoc_file_name(filename, sizeof filename, &qr, ctdl_netcfg_dir); + fp = fopen(filename, "a"); + if (fp != NULL) fclose(fp); + chown(filename, CTDLUID, (-1)); + } - /* for postfix tcpdict */ - CtdlRegisterServiceHook(config.c_pftcpdict_port, /* Postfix */ - NULL, - check_get_greeting, - check_get, - NULL, - CitadelServiceDICT_TCP); + /* for postfix tcpdict */ + CtdlRegisterServiceHook(config.c_pftcpdict_port, /* Postfix */ + NULL, + check_get_greeting, + check_get, + NULL, + CitadelServiceDICT_TCP); + } /* return our Subversion id for the Log */ return "$Id$"; diff --git a/citadel/serv_extensions.c b/citadel/serv_extensions.c index 5f65dbc7b..18e425830 100644 --- a/citadel/serv_extensions.c +++ b/citadel/serv_extensions.c @@ -42,7 +42,6 @@ struct DeleteFunctionHook *DeleteHookTable = NULL; struct ServiceFunctionHook *ServiceHookTable = NULL; struct FixedOutputHook *FixedOutputTable = NULL; struct RoomFunctionHook *RoomHookTable = NULL; -struct MaintenanceThreadHook *MaintenanceThreadHookTable = NULL; struct SearchFunctionHook *SearchFunctionHookTable = NULL; struct ProtoFunctionHook { @@ -1044,19 +1043,6 @@ int PerformXmsgHooks(char *sender, char *recp, char *msg) return total_sent; } -void CtdlRegisterMaintenanceThread(char *name, void *(*thread_proc)(void *arg)) -{ - struct MaintenanceThreadHook *newfcn; - - newfcn = (struct MaintenanceThreadHook *) - malloc(sizeof(struct MaintenanceThreadHook)); - newfcn->name = name; - newfcn->next = MaintenanceThreadHookTable; - newfcn->fcn_ptr = thread_proc; - MaintenanceThreadHookTable = newfcn; - - lprintf(CTDL_INFO, "Registered a new maintenance thread function\n"); -} diff --git a/citadel/serv_extensions.h b/citadel/serv_extensions.h index a9e150b97..39cb0c92f 100644 --- a/citadel/serv_extensions.h +++ b/citadel/serv_extensions.h @@ -149,14 +149,6 @@ struct RoomFunctionHook { extern struct RoomFunctionHook *RoomHookTable; -struct MaintenanceThreadHook { - struct MaintenanceThreadHook *next; - char *name; - void *(*fcn_ptr) (void *arg); - pthread_t MaintenanceThread_tid; -}; -extern struct MaintenanceThreadHook *MaintenanceThreadHookTable; - struct SearchFunctionHook { struct SearchFunctionHook *next; diff --git a/citadel/server_main.c b/citadel/server_main.c index 7edc6ad16..b553904f0 100644 --- a/citadel/server_main.c +++ b/citadel/server_main.c @@ -276,11 +276,7 @@ int main(int argc, char **argv) lprintf(CTDL_INFO, "Initializing server extensions\n"); size = strlen(ctdl_home_directory) + 9; -/* - initialize_server_extensions(); -*/ - - initialise_modules(); + initialise_modules(0); @@ -325,26 +321,35 @@ int main(int argc, char **argv) /* We want to check for idle sessions once per minute */ CtdlRegisterSessionHook(terminate_idle_sessions, EVT_TIMER); + /* + * Initialise the thread system + */ + ctdl_thread_internal_init(); + /* * Now create a bunch of worker threads. */ - lprintf(CTDL_DEBUG, "Starting %d worker threads\n", + CtdlLogPrintf(CTDL_DEBUG, "Starting %d worker threads\n", config.c_min_workers-1); - begin_critical_section(S_WORKER_LIST); + begin_critical_section(S_THREAD_LIST); for (i=0; i<(config.c_min_workers-1); ++i) { - create_worker(); + ctdl_internal_create_thread("Worker Thread", CTDLTHREAD_BIGSTACK + CTDLTHREAD_WORKER, worker_thread, NULL); } - end_critical_section(S_WORKER_LIST); + end_critical_section(S_THREAD_LIST); - /* Create the maintenance threads. */ - create_maintenance_threads(); + /* Second call to module init functions now that threading is up */ + initialise_modules(1); - /* This thread is now useless. It can't be turned into a worker - * thread because its stack is too small, but it can't be killed - * either because the whole server process would exit. So we just - * join to the first worker thread and exit when it exits. + /* + * This thread is now used for garbage collection of other threads in the thread list + */ + CtdlLogPrintf(CTDL_INFO, "Startup thread %d becoming garbage collector,\n", pthread_self()); + while (CtdlThreadGetCount()) + ctdl_internal_thread_gc(); + /* + * If the above loop exits we must be shutting down since we obviously have no threads */ - pthread_join(worker_list->tid, NULL); - master_cleanup(0); + + master_cleanup(exit_signal); return(0); } diff --git a/citadel/sysconfig.h b/citadel/sysconfig.h index 62a167570..98dc149cf 100644 --- a/citadel/sysconfig.h +++ b/citadel/sysconfig.h @@ -113,7 +113,7 @@ /* * The size of per-thread stacks. If set too low, citserver will randomly crash. */ -#define THREADSTACKSIZE 1048576 +#define THREADSTACKSIZE 0x100000 /* * How many messages may the full text indexer scan before flushing its diff --git a/citadel/sysdep.c b/citadel/sysdep.c index 43a4bdec2..87c7a9e8d 100644 --- a/citadel/sysdep.c +++ b/citadel/sysdep.c @@ -72,6 +72,7 @@ #include "snprintf.h" #endif +#include "ctdl_module.h" #ifdef DEBUG_MEMORY_LEAKS struct igheap { @@ -93,13 +94,12 @@ int verbosity = DEFAULT_VERBOSITY; /* Logging level */ struct CitContext masterCC; time_t last_purge = 0; /* Last dead session purge */ static int num_threads = 0; /* Current number of threads */ +static int num_workers = 0; /* Current number of worker threads */ int num_sessions = 0; /* Current number of sessions */ int syslog_facility = LOG_DAEMON; int enable_syslog = 0; -void DestroyWorkerList(void); - /* * Create an interface to lprintf that follows the coding convention. @@ -173,15 +173,15 @@ void vlprintf(enum LogLevel loglevel, const char *format, va_list arg_ptr) * Signal handler to shut down the server. */ -volatile int time_to_die = 0; +volatile int exit_signal = 0; volatile int shutdown_and_halt = 0; volatile int restart_server = 0; volatile int running_as_daemon = 0; static RETSIGTYPE signal_cleanup(int signum) { - lprintf(CTDL_DEBUG, "Caught signal %d; shutting down.\n", signum); - time_to_die = 1; - master_cleanup(signum); + CtdlLogPrintf(CTDL_DEBUG, "Caught signal %d; shutting down.\n", signum); + CtdlThreadStopAll(); + exit_signal = signum; } @@ -228,7 +228,7 @@ void init_sysdep(void) { * session to which the calling thread is currently bound. */ if (pthread_key_create(&MyConKey, NULL) != 0) { - lprintf(CTDL_CRIT, "Can't create TSD key: %s\n", + CtdlLogPrintf(CTDL_CRIT, "Can't create TSD key: %s\n", strerror(errno)); } @@ -268,7 +268,7 @@ void init_sysdep(void) { */ void begin_critical_section(int which_one) { - /* lprintf(CTDL_DEBUG, "begin_critical_section(%d)\n", which_one); */ + /* CtdlLogPrintf(CTDL_DEBUG, "begin_critical_section(%d)\n", which_one); */ /* For all types of critical sections except those listed here, * ensure nobody ever tries to do a critical section within a @@ -330,7 +330,7 @@ int ig_tcp_server(char *ip_addr, int port_number, int queue_len, char **errormes snprintf(*errormessage, SIZ, "citserver: Can't create a socket: %s", strerror(errno)); - lprintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); return(-1); } @@ -342,7 +342,7 @@ int ig_tcp_server(char *ip_addr, int port_number, int queue_len, char **errormes snprintf(*errormessage, SIZ, "citserver: Can't bind: %s", strerror(errno)); - lprintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); close(s); return(-1); } @@ -353,7 +353,7 @@ int ig_tcp_server(char *ip_addr, int port_number, int queue_len, char **errormes snprintf(*errormessage, SIZ, "citserver: Can't set socket to non-blocking: %s", strerror(errno)); - lprintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); close(s); return(-1); } @@ -363,7 +363,7 @@ int ig_tcp_server(char *ip_addr, int port_number, int queue_len, char **errormes snprintf(*errormessage, SIZ, "citserver: Can't listen: %s", strerror(errno)); - lprintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); close(s); return(-1); } @@ -391,7 +391,7 @@ int ig_uds_server(char *sockpath, int queue_len, char **errormessage) *errormessage = (char*) malloc(SIZ + 1); snprintf(*errormessage, SIZ, "citserver: can't unlink %s: %s", sockpath, strerror(errno)); - lprintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); return(-1); } @@ -405,7 +405,7 @@ int ig_uds_server(char *sockpath, int queue_len, char **errormessage) snprintf(*errormessage, SIZ, "citserver: Can't create a socket: %s", strerror(errno)); - lprintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); return(-1); } @@ -414,7 +414,7 @@ int ig_uds_server(char *sockpath, int queue_len, char **errormessage) snprintf(*errormessage, SIZ, "citserver: Can't bind: %s", strerror(errno)); - lprintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); return(-1); } @@ -424,7 +424,7 @@ int ig_uds_server(char *sockpath, int queue_len, char **errormessage) snprintf(*errormessage, SIZ, "citserver: Can't set socket to non-blocking: %s", strerror(errno)); - lprintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); close(s); return(-1); } @@ -434,7 +434,7 @@ int ig_uds_server(char *sockpath, int queue_len, char **errormessage) snprintf(*errormessage, SIZ, "citserver: Can't listen: %s", strerror(errno)); - lprintf(CTDL_EMERG, "%s\n", *errormessage); + CtdlLogPrintf(CTDL_EMERG, "%s\n", *errormessage); return(-1); } @@ -473,7 +473,7 @@ struct CitContext *CreateNewContext(void) { me = (struct CitContext *) malloc(sizeof(struct CitContext)); if (me == NULL) { - lprintf(CTDL_ALERT, "citserver: can't allocate memory!!\n"); + CtdlLogPrintf(CTDL_ALERT, "citserver: can't allocate memory!!\n"); return NULL; } memset(me, 0, sizeof(struct CitContext)); @@ -628,12 +628,12 @@ void client_write(char *buf, int nbytes) retval = write(Ctx->client_socket, &buf[bytes_written], nbytes - bytes_written); if (retval < 1) { - lprintf(CTDL_ERR, + CtdlLogPrintf(CTDL_ERR, "client_write(%d bytes) failed: %s (%d)\n", nbytes - bytes_written, strerror(errno), errno); cit_backtrace(); - // lprintf(CTDL_DEBUG, "Tried to send: %s", &buf[bytes_written]); + // CtdlLogPrintf(CTDL_DEBUG, "Tried to send: %s", &buf[bytes_written]); Ctx->kill_me = 1; return; } @@ -761,7 +761,6 @@ int client_getln(char *buf, int bufsize) void sysdep_master_cleanup(void) { struct ServiceFunctionHook *serviceptr; -///// DestroyWorkerList(); /* * close all protocol master sockets */ @@ -769,11 +768,11 @@ void sysdep_master_cleanup(void) { serviceptr = serviceptr->next ) { if (serviceptr->tcp_port > 0) - lprintf(CTDL_INFO, "Closing listener on port %d\n", + CtdlLogPrintf(CTDL_INFO, "Closing listener on port %d\n", serviceptr->tcp_port); if (serviceptr->sockpath != NULL) - lprintf(CTDL_INFO, "Closing listener on '%s'\n", + CtdlLogPrintf(CTDL_INFO, "Closing listener on '%s'\n", serviceptr->sockpath); close(serviceptr->msock); @@ -961,159 +960,470 @@ int convert_login(char NameToConvert[]) { * 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; + +/* + * Condition variable and Mutex for thread garbage collection + */ +static pthread_mutex_t thread_gc_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t thread_gc_cond = PTHREAD_COND_INITIALIZER; +static pthread_t GC_thread; +static char *CtdlThreadStates[CTDL_THREAD_LAST_STATE]; +/* + * Pinched the following bits regarding signals from Kannel.org + */ + +/* + * Change this thread's signal mask to block user-visible signals + * (HUP, TERM, QUIT, INT), and store the old signal mask in + * *old_set_storage. + * Return 0 for success, or -1 if an error occurred. + */ + + /* + * This does not work in Darwin alias MacOS X alias Mach kernel, + * however. So we define a dummy function doing nothing. + */ +#if defined(DARWIN_OLD) + static int pthread_sigmask(); +#endif + +static int ctdl_thread_internal_block_signals(sigset_t *old_set_storage) +{ + int ret; + sigset_t block_signals; + + ret = sigemptyset(&block_signals); + if (ret != 0) { + CtdlLogPrintf(CTDL_EMERG, "Thread system PANIC. Couldn't initialize signal set\n"); + return -1; + } + ret = sigaddset(&block_signals, SIGHUP); + ret |= sigaddset(&block_signals, SIGTERM); + ret |= sigaddset(&block_signals, SIGQUIT); + ret |= sigaddset(&block_signals, SIGINT); + if (ret != 0) { + CtdlLogPrintf(CTDL_EMERG, "Thread system PANIC. Couldn't add signal to signal set.\n"); + return -1; + } + ret = pthread_sigmask(SIG_BLOCK, &block_signals, old_set_storage); + if (ret != 0) { + CtdlLogPrintf(CTDL_EMERG, "Thread system PANIC. Couldn't disable signals for thread creation\n"); + return -1; + } + return 0; +} + +static void ctdl_thread_internal_restore_signals(sigset_t *old_set) +{ + int ret; + + ret = pthread_sigmask(SIG_SETMASK, old_set, NULL); + if (ret != 0) { + CtdlLogPrintf(CTDL_EMERG, "Thread system PANIC. Couldn't restore signal set.\n"); + } +} + + +void ctdl_thread_internal_init(void) +{ + GC_thread = pthread_self(); + CtdlThreadStates[CTDL_THREAD_INVALID] = strdup ("Invalid Thread"); + CtdlThreadStates[CTDL_THREAD_VALID] = strdup("Valid Thread"); + CtdlThreadStates[CTDL_THREAD_CREATE] = strdup("Thread being Created"); + CtdlThreadStates[CTDL_THREAD_CANCELLED] = strdup("Thread Cancelled"); + CtdlThreadStates[CTDL_THREAD_EXITED] = strdup("Thread Exited"); + CtdlThreadStates[CTDL_THREAD_STOPPING] = strdup("Thread Stopping"); + CtdlThreadStates[CTDL_THREAD_STOP_REQ] = strdup("Thread Stop Requested"); + CtdlThreadStates[CTDL_THREAD_SLEEPING] = strdup("Thread Sleeping"); + CtdlThreadStates[CTDL_THREAD_RUNNING] = strdup("Thread Running"); +} + +/* + * A function to tell all threads to exit + */ +void CtdlThreadStopAll(void) +{ + struct CtdlThreadNode *this_thread; + + begin_critical_section(S_THREAD_LIST); + this_thread = CtdlThreadList; + while(this_thread) + { + pthread_mutex_lock(&this_thread->ThreadMutex); /* To prevent race condition of a sleeping thread */ + if (this_thread->state > CTDL_THREAD_STOP_REQ) + this_thread->state = CTDL_THREAD_STOP_REQ; + pthread_mutex_unlock(&this_thread->ThreadMutex); + pthread_cond_signal(&this_thread->ThreadCond); + CtdlLogPrintf(CTDL_DEBUG, "Thread system stopping thread \"%s\" (%ld).\n", this_thread->name, this_thread->tid); + this_thread = this_thread->next; + } + end_critical_section(S_THREAD_LIST); +} + + +/* + * A function to signal that we need to do garbage collection on the thread list + */ +void CtdlThreadGC(void) +{ + pthread_cond_signal(&thread_gc_cond); +} +/* + * A function to return the number of threads running in the system + */ +int CtdlThreadGetCount(void) +{ + return num_threads; +} + +/* + * A function to find the thread structure for this thread + */ +struct CtdlThreadNode *CtdlThreadSelf(void) +{ + pthread_t self_tid; + struct CtdlThreadNode *this_thread; + + self_tid = pthread_self(); + + begin_critical_section(S_THREAD_LIST); + this_thread = CtdlThreadList; + while(this_thread) + { + if (pthread_equal(self_tid, this_thread->tid)) + { + end_critical_section(S_THREAD_LIST); + return this_thread; + } + this_thread = this_thread->next; + } + end_critical_section(S_THREAD_LIST); + return NULL; +} + + + + +/* + * A function to rename a thread + * Returns a char * and the caller owns the memory and should free it + */ +char *CtdlThreadName(struct CtdlThreadNode *thread, char *name) +{ + struct CtdlThreadNode *this_thread; + char *old_name; + + if (!thread) + this_thread = CtdlThreadSelf(); + else + this_thread = thread; + if (!this_thread) + { + CtdlLogPrintf(CTDL_WARNING, "Thread system WARNING. Attempt to CtdlThreadRename() a non thread.\n"); + return NULL; + } + begin_critical_section(S_THREAD_LIST); + if (name) + { + old_name = this_thread->name; + this_thread->name = strdup (name); + free(old_name); + } + old_name = strdup(this_thread->name); + end_critical_section (S_THREAD_LIST); + return (old_name); +} + + +/* + * A function to force a thread to exit + */ +void CtdlThreadCancel(struct CtdlThreadNode *thread) +{ + struct CtdlThreadNode *this_thread; + + if (!thread) + this_thread = CtdlThreadSelf(); + else + this_thread = thread; + if (!this_thread) + { + CtdlLogPrintf(CTDL_EMERG, "Thread system PANIC. Attempt to CtdlThreadCancel() a non thread.\n"); + CtdlThreadStopAll(); + return; + } + begin_critical_section(S_THREAD_LIST); + this_thread->state = CTDL_THREAD_CANCELLED; + pthread_cancel(this_thread->tid); + end_critical_section (S_THREAD_LIST); +} + + + +/* + * A function for a thread to check if it has been asked to stop + */ +int CtdlThreadCheckStop(void) +{ + struct CtdlThreadNode *this_thread; + + this_thread = CtdlThreadSelf(); + if (!this_thread) + { + CtdlLogPrintf(CTDL_EMERG, "Thread system PANIC, CtdlThreadCheckStop() called by a non thread.\n"); + CtdlThreadStopAll(); + return -1; + } + if(this_thread->state == CTDL_THREAD_STOP_REQ) + { + this_thread->state = CTDL_THREAD_STOPPING; + return -1; + } + else if(this_thread->state < CTDL_THREAD_STOP_REQ) + return -1; + + return 0; +} + + +/* + * A function to ask a thread to exit + * The thread must call CtdlThreadCheckStop() periodically to determine if it should exit + */ +void CtdlThreadStop(struct CtdlThreadNode *thread) +{ + struct CtdlThreadNode *this_thread; + + if (!thread) + this_thread = CtdlThreadSelf(); + else + this_thread = thread; + if (!this_thread) + return; + + begin_critical_section (S_THREAD_LIST); + pthread_mutex_lock(&this_thread->ThreadMutex); /* To prevent race condition of a sleeping thread */ + if (this_thread->state > CTDL_THREAD_STOP_REQ) + this_thread->state = CTDL_THREAD_STOP_REQ; + pthread_mutex_unlock(&this_thread->ThreadMutex); + pthread_cond_signal(&this_thread->ThreadCond); + end_critical_section(S_THREAD_LIST); +} + /* * 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; + struct CtdlThreadNode *self; + int state; + self = CtdlThreadSelf(); + if (!self) + { + CtdlLogPrintf(CTDL_WARNING, "CtdlThreadSleep() called by something that is not a thread. Should we die?\n"); + return; + } + + begin_critical_section(S_THREAD_LIST); + pthread_mutex_lock(&self->ThreadMutex); /* Prevent something asking us to awaken before we've gone to sleep */ + state = self->state; + if (state == CTDL_THREAD_RUNNING) + self->state = CTDL_THREAD_SLEEPING; + end_critical_section(S_THREAD_LIST); + + if(state != CTDL_THREAD_RUNNING) + { + CtdlLogPrintf(CTDL_DEBUG, "CtdlThreadSleep() called by a thread that is not running.\n"); + pthread_mutex_unlock(&self->ThreadMutex); + return; + } + 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); + wake_time.tv_nsec = time_now.tv_usec * 10; + pthread_cond_timedwait(&self->ThreadCond, &self->ThreadMutex, &wake_time); + begin_critical_section(S_THREAD_LIST); + if (self->state == CTDL_THREAD_SLEEPING) /* Don't change state if something else changed it while we were asleep */ + self->state = state; + pthread_mutex_unlock(&self->ThreadMutex); + end_critical_section(S_THREAD_LIST); } /* * Routine to clean up our thread function on exit */ -void ctdl_internal_thread_cleanup(void *arg) +static 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); - } + this_thread = CtdlThreadSelf(); + /* + * 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); + begin_critical_section(S_THREAD_LIST); + #ifdef HAVE_BACKTRACE + eCrash_UnregisterThread(); + #endif + this_thread->state = CTDL_THREAD_EXITED; // needs to be last thing else house keeping will unlink us too early + end_critical_section(S_THREAD_LIST); } + + /* * Garbage collection routine. - * Gets called by do_housekeeping() and in master_cleanup() to clean up the thread list + * Gets called by main() in a loop to clean up the thread list periodically. */ -void ctdl_internal_thread_gc (int shutdown) +void ctdl_internal_thread_gc (void) { struct CtdlThreadNode *this_thread, *that_thread; + struct timespec wake_time; + struct timeval time_now; + int workers = 0; + /* + * Wait on the condition variable that tells us garbage collection is needed + * We wake up every 10 seconds just in case someone forgot to inform us of a thread exiting + */ + pthread_mutex_lock(&thread_gc_mutex); + memset (&wake_time, 0, sizeof(struct timespec)); + gettimeofday(&time_now, NULL); + wake_time.tv_sec = time_now.tv_sec + 10; + pthread_cond_timedwait(&thread_gc_cond, &thread_gc_mutex, &wake_time); + + CtdlLogPrintf(CTDL_DEBUG, "Thread system running garbage collection.\n"); + /* + * Woke up to do garbage collection + */ + begin_critical_section(S_THREAD_LIST); 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); + CtdlLogPrintf(CTDL_DEBUG, "CtdlThread, \"%s\" (%ld) \"%s\".\n", that_thread->name, that_thread->tid, CtdlThreadStates[that_thread->state]); + /* Do we need to clean up this thread? */ + if (that_thread->state != CTDL_THREAD_EXITED) + { + if(that_thread->flags & CTDLTHREAD_WORKER) + workers++; /* Sanity check on number of worker threads */ + continue; } - 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); + if (pthread_equal(that_thread->tid, pthread_self())) + { /* Sanity check */ + end_critical_section(S_THREAD_LIST); + CtdlLogPrintf(CTDL_EMERG, "Thread system PANIC, a thread is trying to clean up after itself.\n"); + pthread_mutex_unlock(&thread_gc_mutex); + CtdlThreadStopAll(); + return; + } + + if (num_threads <= 0) + { /* Sanity check */ + end_critical_section (S_THREAD_LIST); + CtdlLogPrintf(CTDL_EMERG, "Thread system PANIC, num_threads <= 0 and trying to do Garbage Collection.\n"); + pthread_mutex_unlock(&thread_gc_mutex); + CtdlThreadStopAll(); + return; } + + /* If we are unlinking the list head then the next becomes the list head */ + if (that_thread == CtdlThreadList) + CtdlThreadList = that_thread->next; + if(that_thread->prev) + that_thread->prev->next = that_thread->next; + if(that_thread->next) + that_thread->next->prev = that_thread->next; + num_threads--; + if(that_thread->flags & CTDLTHREAD_WORKER) + num_workers--; /* This is a wroker thread so reduce the count. */ + + /* + * Join on the thread to do clean up and prevent memory leaks + * Also makes sure the thread has cleaned up after itself before we remove it from the list + */ + pthread_join (that_thread->tid, NULL); + + /* + * Now we own that thread entry + */ + CtdlLogPrintf(CTDL_INFO, "Garbage Collection for thread \"%s\" (%ld).\n", that_thread->name, that_thread->tid); + if(that_thread->name) + free(that_thread->name); + pthread_mutex_destroy(&that_thread->ThreadMutex); + pthread_cond_destroy(&that_thread->ThreadCond); + pthread_attr_destroy(&that_thread->attr); + free(that_thread); + } + + /* Sanity check number of worker threads */ + if (workers != num_workers) + { + end_critical_section(S_THREAD_LIST); + CtdlLogPrintf(CTDL_EMERG, "Thread system PANIC, discrepancy in number of worker threads. Counted %d, should be %d.\n", workers, num_workers); + pthread_mutex_unlock(&thread_gc_mutex); +// CtdlThreadStopAll(); + return; } + pthread_mutex_unlock(&thread_gc_mutex); + + end_critical_section(S_THREAD_LIST); } + + + /* * 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) +static void *ctdl_internal_thread_func (void *arg) { struct CtdlThreadNode *this_thread; void *ret = NULL; + /* lock and unlock the thread list. + * This causes this thread to wait until all its creation stuff has finished before it + * can continue its execution. + */ + begin_critical_section(S_THREAD_LIST); // Get our thread data structure this_thread = (struct CtdlThreadNode *) arg; + this_thread->state = CTDL_THREAD_RUNNING; + this_thread->pid = getpid(); + end_critical_section(S_THREAD_LIST); + // 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 + CtdlLogPrintf(CTDL_NOTICE, "Created a new thread \"%s\" (%ld). \n", this_thread->name, this_thread->tid); // Register the cleanup function to take care of when we exit. - pthread_cleanup_push(ctdl_internal_thread_cleanup, arg); + pthread_cleanup_push(ctdl_internal_thread_cleanup, NULL); - 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); - } + /* + * run the thread to do the work + */ + 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); @@ -1125,30 +1435,33 @@ void *ctdl_internal_thread_func (void *arg) * 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) +struct CtdlThreadNode *ctdl_internal_create_thread(char *name, long flags, void *(*thread_func) (void *arg), void *args) { int ret = 0; - pthread_attr_t attr; struct CtdlThreadNode *this_thread; - - if (*new_thread) + int sigtrick = 0; + sigset_t old_signal_set; + + if (num_threads >= 32767) { - lprintf(CTDL_EMERG, "Possible attempt to overwrite an existing thread!!!\n"); - return -1; + CtdlLogPrintf(CTDL_EMERG, "Thread system. Thread list full.\n"); + return NULL; } - + this_thread = malloc(sizeof(struct CtdlThreadNode)); if (this_thread == NULL) { - lprintf(CTDL_EMERG, "can't allocate CtdlThreadNode, exiting\n"); - return ret; + CtdlLogPrintf(CTDL_EMERG, "Thread system, can't allocate CtdlThreadNode, exiting\n"); + return NULL; } // 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)); + this_thread->state = CTDL_THREAD_CREATE; + + if ((ret = pthread_attr_init(&this_thread->attr))) { + CtdlLogPrintf(CTDL_EMERG, "Thread system, pthread_attr_init: %s\n", strerror(ret)); free(this_thread); - return ret; + return NULL; } /* Our per-thread stacks need to be bigger than the default size, @@ -1157,12 +1470,13 @@ int ctdl_internal_create_thread(char *name, int flags, void *(*thread_func) (voi */ if (flags & CTDLTHREAD_BIGSTACK) { - if ((ret = pthread_attr_setstacksize(&attr, THREADSTACKSIZE))) { - lprintf(CTDL_EMERG, "pthread_attr_setstacksize: %s\n", + CtdlLogPrintf(CTDL_INFO, "Thread system. Creating BIG STACK thread.\n"); + if ((ret = pthread_attr_setstacksize(&this_thread->attr, THREADSTACKSIZE))) { + CtdlLogPrintf(CTDL_EMERG, "Thread system, pthread_attr_setstacksize: %s\n", strerror(ret)); - pthread_attr_destroy(&attr); + pthread_attr_destroy(&this_thread->attr); free(this_thread); - return ret; + return NULL; } } @@ -1177,34 +1491,64 @@ int ctdl_internal_create_thread(char *name, int flags, void *(*thread_func) (voi } else { - this_thread->name = strdup("Unknown Thread"); + this_thread->name = strdup("Un-named 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 + this_thread->user_args = args; + pthread_mutex_init (&(this_thread->ThreadMutex), NULL); + pthread_cond_init (&(this_thread->ThreadCond), NULL); + + /* + * We want to make sure that only the main thread handles signals, + * so that each signal is handled exactly once. To do this, we + * make sure that each new thread has all the signals that we + * handle blocked. To avoid race conditions, we block them in + * the spawning thread first, then create the new thread (which + * inherits the settings), and then restore the old settings in + * the spawning thread. This means that there is a brief period + * when no signals will be processed, but during that time they + * should be queued by the operating system. + */ + if (pthread_equal(GC_thread, pthread_self())) + sigtrick = ctdl_thread_internal_block_signals(&old_signal_set) == 0; + /* * 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)) + if ((ret = pthread_create(&this_thread->tid, &this_thread->attr, ctdl_internal_thread_func, this_thread) != 0)) { - lprintf(CTDL_ALERT, "Can't create thread: %s\n", + CtdlLogPrintf(CTDL_ALERT, "Thread system, Can't create thread: %s\n", strerror(ret)); if (this_thread->name) free (this_thread->name); + pthread_mutex_destroy(&(this_thread->ThreadMutex)); + pthread_cond_destroy(&(this_thread->ThreadCond)); + pthread_attr_destroy(&this_thread->attr); free(this_thread); - pthread_attr_destroy(&attr); - return ret; + if (sigtrick) + ctdl_thread_internal_restore_signals(&old_signal_set); + return NULL; } + if (sigtrick) + ctdl_thread_internal_restore_signals(&old_signal_set); + + num_threads++; // Increase the count of threads in the system. + if(this_thread->flags & CTDLTHREAD_WORKER) + num_workers++; + this_thread->next = CtdlThreadList; CtdlThreadList = this_thread; - *new_thread = this_thread; - pthread_attr_destroy(&attr); - return 0; + // Register for tracing + #ifdef HAVE_BACKTRACE + eCrash_RegisterThread(this_thread->name, 0); + #endif + return this_thread; } /* @@ -1213,154 +1557,17 @@ int ctdl_internal_create_thread(char *name, int flags, void *(*thread_func) (voi * 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) +struct CtdlThreadNode *CtdlThreadCreate(char *name, long flags, void *(*thread_func) (void *arg), void *args) { - int ret; + struct CtdlThreadNode *ret = NULL; begin_critical_section(S_THREAD_LIST); - ret = ctdl_internal_create_thread(name, flags, thread_func, arg, new_thread); + ret = ctdl_internal_create_thread(name, flags, thread_func, args); end_critical_section(S_THREAD_LIST); return ret; } - -/* - * Old thread interface. - */ - - -struct worker_node *worker_list = NULL; - - -/* - * create a worker thread. this function must always be called from within - * an S_WORKER_LIST critical section! - */ -void create_worker(void) { - int ret; - struct worker_node *n; - pthread_attr_t attr; - - n = malloc(sizeof(struct worker_node)); - if (n == NULL) { - lprintf(CTDL_EMERG, "can't allocate worker_node, exiting\n"); - time_to_die = -1; - return; - } - - if ((ret = pthread_attr_init(&attr))) { - lprintf(CTDL_EMERG, "pthread_attr_init: %s\n", strerror(ret)); - time_to_die = -1; - free(n); - return; - } - - /* 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 ((ret = pthread_attr_setstacksize(&attr, THREADSTACKSIZE))) { - lprintf(CTDL_EMERG, "pthread_attr_setstacksize: %s\n", - strerror(ret)); - time_to_die = -1; - pthread_attr_destroy(&attr); - free(n); - return; - } - - if ((ret = pthread_create(&n->tid, &attr, worker_thread, NULL) != 0)) - { - - 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; - worker_list = n; - pthread_attr_destroy(&attr); -} - -void DestroyWorkerList(void) -{ - struct CitContext *ptr; /* general-purpose utility pointer */ - struct CitContext *rem = NULL; /* list of sessions to be destroyed */ - - begin_critical_section(S_SESSION_TABLE); - ptr = ContextList; - while (ptr != NULL){ - /* Remove the session from the active list */ - rem = ptr->next; - --num_sessions; - - lprintf(CTDL_DEBUG, "Purging session %d\n", rem->cs_pid); - end_critical_section(S_SESSION_TABLE); - RemoveContext(ptr); - begin_critical_section(S_SESSION_TABLE); - free (ptr); - ptr = rem; - } - end_critical_section(S_SESSION_TABLE); - - struct worker_node *cur, *p; - cur = worker_list; - while (cur != NULL) - { - p = cur->next; - free (cur); - cur = p; - } - worker_list = NULL; -} - -/* - * Create the maintenance threads and begin their operation. - */ -void create_maintenance_threads(void) { - int ret; - pthread_attr_t attr; - - if ((ret = pthread_attr_init(&attr))) { - lprintf(CTDL_EMERG, "pthread_attr_init: %s\n", strerror(ret)); - time_to_die = -1; - return; - } - - /* 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 ((ret = pthread_attr_setstacksize(&attr, THREADSTACKSIZE))) { - lprintf(CTDL_EMERG, "pthread_attr_setstacksize: %s\n", - strerror(ret)); - time_to_die = -1; - pthread_attr_destroy(&attr); - return; - } - - struct MaintenanceThreadHook *fcn; - - lprintf(CTDL_DEBUG, "Performing startup of maintenance thread hooks\n"); - - for (fcn = MaintenanceThreadHookTable; fcn != NULL; fcn = fcn->next) { - if ((ret = pthread_create(&(fcn->MaintenanceThread_tid), &attr, fcn->fcn_ptr, NULL) != 0)) { - lprintf(CTDL_ALERT, "Can't create thread: %s\n", strerror(ret)); - } - else - { - lprintf(CTDL_NOTICE, "Spawned a new maintenance thread \"%s\" (%ld). \n", fcn->name, - fcn->MaintenanceThread_tid); - } - } - - - pthread_attr_destroy(&attr); -} - /* @@ -1415,7 +1622,7 @@ void dead_session_purge(int force) { * is allocated privately on this thread's stack. */ while (rem != NULL) { - lprintf(CTDL_DEBUG, "Purging session %d\n", rem->cs_pid); + CtdlLogPrintf(CTDL_DEBUG, "Purging session %d\n", rem->cs_pid); RemoveContext(rem); ptr = rem; rem = rem->next; @@ -1423,12 +1630,13 @@ void dead_session_purge(int force) { } /* Raise the size of the worker thread pool if necessary. */ - if ( (num_sessions > num_threads) - && (num_threads < config.c_max_workers) ) { - begin_critical_section(S_WORKER_LIST); - create_worker(); - end_critical_section(S_WORKER_LIST); + begin_critical_section(S_THREAD_LIST); + if ( (num_sessions > num_workers) + && (num_workers < config.c_max_workers) ) { + ctdl_internal_create_thread(NULL, CTDLTHREAD_BIGSTACK + CTDLTHREAD_WORKER, worker_thread, NULL); } + end_critical_section(S_THREAD_LIST); + // FIXME: reduce the number of worker threads too } @@ -1476,15 +1684,9 @@ void *worker_thread(void *arg) { int force_purge = 0; int m; - num_threads++; - cdb_allocate_tsd(); - // Register for tracing - #ifdef HAVE_BACKTRACE - eCrash_RegisterThread("WorkerThread", 0); - #endif - while (!time_to_die) { + while (!CtdlThreadCheckStop()) { /* make doubly sure we're not holding any stale db handles * which might cause a deadlock. @@ -1530,30 +1732,34 @@ do_select: force_purge = 0; } } - if (!time_to_die) { + if (!CtdlThreadCheckStop()) { tv.tv_sec = 1; /* wake up every second if no input */ tv.tv_usec = 0; retval = select(highest + 1, &readfds, NULL, NULL, &tv); } - if (time_to_die) return(NULL); + if (CtdlThreadCheckStop()) return(NULL); /* Now figure out who made this select() unblock. * First, check for an error or exit condition. */ if (retval < 0) { if (errno == EBADF) { - lprintf(CTDL_NOTICE, "select() failed: (%s)\n", + CtdlLogPrintf(CTDL_NOTICE, "select() failed: (%s)\n", strerror(errno)); goto do_select; } if (errno != EINTR) { - lprintf(CTDL_EMERG, "Exiting (%s)\n", strerror(errno)); - time_to_die = 1; - } else if (!time_to_die) + CtdlLogPrintf(CTDL_EMERG, "Exiting (%s)\n", strerror(errno)); + CtdlThreadStopAll(); + } else if (!CtdlThreadCheckStop()) { + CtdlLogPrintf(CTDL_DEBUG, "Un handled select failure.\n"); goto do_select; + } + } + else if(retval == 0) { + goto SKIP_SELECT; } - /* Next, check to see if it's a new client connecting * on a master socket. */ @@ -1563,7 +1769,7 @@ do_select: force_purge = 0; if (FD_ISSET(serviceptr->msock, &readfds)) { ssock = accept(serviceptr->msock, NULL, 0); if (ssock >= 0) { - lprintf(CTDL_DEBUG, + CtdlLogPrintf(CTDL_DEBUG, "New client socket %d\n", ssock); @@ -1572,7 +1778,7 @@ do_select: force_purge = 0; * operations barf on FreeBSD. Not a fatal error. */ if (fcntl(ssock, F_SETFL, 0) < 0) { - lprintf(CTDL_EMERG, + CtdlLogPrintf(CTDL_EMERG, "citserver: Can't set socket to blocking: %s\n", strerror(errno)); } @@ -1663,9 +1869,8 @@ SKIP_SELECT: } if (con != NULL) free (con);//// TODO: could this harm other threads? /* If control reaches this point, the server is shutting down */ - #ifdef HAVE_BACKTRACE - eCrash_UnregisterThread(); - #endif + begin_critical_section(S_THREAD_LIST); + end_critical_section(S_THREAD_LIST); return(NULL); } @@ -1814,7 +2019,7 @@ void dump_heap(void) { struct igheap *thisheap; for (thisheap = igheap; thisheap != NULL; thisheap = thisheap->next) { - lprintf(CTDL_CRIT, "UNFREED: %30s : %d\n", + CtdlLogPrintf(CTDL_CRIT, "UNFREED: %30s : %d\n", thisheap->file, thisheap->line); } } diff --git a/citadel/sysdep_decls.h b/citadel/sysdep_decls.h index ab8745c79..3adcf61f6 100644 --- a/citadel/sysdep_decls.h +++ b/citadel/sysdep_decls.h @@ -84,11 +84,10 @@ 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; +extern volatile int exit_signal; extern volatile int shutdown_and_halt; extern volatile int running_as_daemon; extern volatile int restart_server; @@ -102,16 +101,47 @@ extern struct worker_node { } *worker_list; + +/* + * Thread stuff + */ +#define CTDLTHREAD_BIGSTACK 0x0001 +#define CTDLTHREAD_WORKER 0x0002 + +void ctdl_internal_thread_gc (void); +void ctdl_thread_internal_init(void); +struct CtdlThreadNode *ctdl_internal_create_thread(char *name, long flags, void *(*thread_func) (void *arg), void *args); + +enum CtdlThreadState { + CTDL_THREAD_INVALID, + CTDL_THREAD_VALID, + CTDL_THREAD_CREATE, + CTDL_THREAD_CANCELLED, + CTDL_THREAD_EXITED, + CTDL_THREAD_STOPPING, + CTDL_THREAD_STOP_REQ, /* Do NOT put any running states before this state */ + CTDL_THREAD_SLEEPING, + CTDL_THREAD_RUNNING, + CTDL_THREAD_LAST_STATE +}; + 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; + pthread_t tid; /* id as returned by pthread_create() */ + pid_t pid; /* pid, as best the OS will let us determine */ + struct CitConext *Context; /* The session context that this thread mught be working on or NULL if none */ + long number; /* A unigue number for this thread (not implimented yet) */ + int wakefd_recv; /* An fd that this thread can sleep on (not implimented yet) */ + int wakefd_send; /* An fd that this thread can send out on (Not implimented yet) */ + char *name; /* A name for this thread */ + void *(*thread_func) (void *arg); /* The actual function that does this threads work */ + void *user_args; /* Arguments passed to this threads work function */ + long flags; /* Flags that describe this thread */ + enum CtdlThreadState state; /* Flag to show state of this thread */ + pthread_mutex_t ThreadMutex; /* A mutex to sync this thread to others if this thread allows (also used for sleeping) */ + pthread_cond_t ThreadCond; /* A condition variable to sync this thread with others (also used for sleeping) */ + pthread_attr_t attr; /* Attributes of this thread */ + struct CtdlThreadNode *prev; /* Previous thread in the thread table */ + struct CtdlThreadNode *next; /* Next thread in the thread table */ } *CtdlThreadList; extern int SyslogFacility(char *name); -- 2.30.2