+/*
+ * Internal function to schedule a thread.
+ * Must be called from within a S_THREAD_LIST critical section
+ */
+struct CtdlThreadNode *CtdlThreadSchedule(char *name, long flags, void *(*thread_func) (void *arg), void *args, time_t when)
+{
+ int ret = 0;
+ struct CtdlThreadNode *this_thread;
+
+ if (num_threads >= 32767)
+ {
+ CtdlLogPrintf(CTDL_EMERG, "Thread system. Thread list full.\n");
+ return NULL;
+ }
+
+ this_thread = malloc(sizeof(struct CtdlThreadNode));
+ if (this_thread == NULL) {
+ 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));
+
+ /* Create the mutex's early so we can use them */
+ pthread_mutex_init (&(this_thread->ThreadMutex), NULL);
+ pthread_cond_init (&(this_thread->ThreadCond), NULL);
+ pthread_mutex_init (&(this_thread->SleepMutex), NULL);
+ pthread_cond_init (&(this_thread->SleepCond), NULL);
+
+ this_thread->state = CTDL_THREAD_CREATE;
+
+ if ((ret = pthread_attr_init(&this_thread->attr))) {
+ pthread_mutex_destroy(&(this_thread->ThreadMutex));
+ pthread_cond_destroy(&(this_thread->ThreadCond));
+ pthread_mutex_destroy(&(this_thread->SleepMutex));
+ pthread_cond_destroy(&(this_thread->SleepCond));
+ CtdlLogPrintf(CTDL_EMERG, "Thread system, pthread_attr_init: %s\n", strerror(ret));
+ free(this_thread);
+ return NULL;
+ }
+
+ /* 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)
+ {
+ CtdlLogPrintf(CTDL_INFO, "Thread system. Creating BIG STACK thread.\n");
+ if ((ret = pthread_attr_setstacksize(&this_thread->attr, THREADSTACKSIZE))) {
+ pthread_mutex_destroy(&(this_thread->ThreadMutex));
+ pthread_cond_destroy(&(this_thread->ThreadCond));
+ pthread_mutex_destroy(&(this_thread->SleepMutex));
+ pthread_cond_destroy(&(this_thread->SleepCond));
+ pthread_attr_destroy(&this_thread->attr);
+ CtdlLogPrintf(CTDL_EMERG, "Thread system, pthread_attr_setstacksize: %s\n",
+ strerror(ret));
+ free(this_thread);
+ return NULL;
+ }
+ }
+
+ /*
+ * 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 = name;
+ }
+ else
+ {
+ this_thread->name = "Un-named Thread";
+ }
+
+ this_thread->flags = flags;
+ this_thread->thread_func = thread_func;
+ this_thread->user_args = args;
+ /* Set this new thread with an avg_blocked of 2. We do this so that its creation affects the
+ * load average for the system. If we don't do this then we create a mass of threads at the same time
+ * because the creation didn't affect the load average.
+ */
+ this_thread->avg_blocked = 2;
+
+ /*
+ * When to start this thread
+ */
+ this_thread->when = when;
+
+ begin_critical_section(S_SCHEDULE_LIST);
+ this_thread->next = CtdlThreadSchedList;
+ CtdlThreadSchedList = this_thread;
+ if (this_thread->next)
+ this_thread->next->prev = this_thread;
+ end_critical_section(S_SCHEDULE_LIST);
+
+ return this_thread;
+}
+
+
+
+struct CtdlThreadNode *ctdl_thread_internal_start_scheduled (struct CtdlThreadNode *this_thread)
+{
+ int ret = 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, &this_thread->attr, ctdl_internal_thread_func, this_thread) != 0))
+ {
+
+ CtdlLogPrintf(CTDL_ALERT, "Thread system, Can't create thread: %s\n",
+ strerror(ret));
+ return NULL;
+ }
+
+
+ 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;
+ if (this_thread->next)
+ this_thread->next->prev = this_thread;
+
+ return this_thread;
+}
+
+
+
+void ctdl_thread_internal_check_scheduled(void)
+{
+ struct CtdlThreadNode *this_thread, *that_thread;
+ time_t now;
+
+ if (try_critical_section(S_SCHEDULE_LIST))
+ return; /* If this list is locked we wait till the next chance */
+
+ now = time(NULL);
+
+ this_thread = CtdlThreadSchedList;
+ while(this_thread)
+ {
+ that_thread = this_thread;
+ this_thread = this_thread->next;
+
+ if (now > that_thread->when)
+ {
+ /* Unlink from schedule list */
+ if (that_thread->next)
+ that_thread->next->prev = that_thread->prev;
+ if (that_thread->prev)
+ that_thread->prev->next = that_thread->next;
+ else
+ CtdlThreadSchedList = that_thread->next;
+
+ begin_critical_section(S_THREAD_LIST);
+ if (CT->state > CTDL_THREAD_STOP_REQ)
+ { /* Only start it if the system is not stopping */
+ pthread_mutex_lock(&that_thread->ThreadMutex);
+ if (ctdl_thread_internal_start_scheduled (that_thread) == NULL)
+ {
+ pthread_mutex_unlock(&that_thread->ThreadMutex);
+ pthread_mutex_destroy(&(that_thread->ThreadMutex));
+ pthread_cond_destroy(&(that_thread->ThreadCond));
+ pthread_mutex_destroy(&(that_thread->SleepMutex));
+ pthread_cond_destroy(&(that_thread->SleepCond));
+ pthread_attr_destroy(&that_thread->attr);
+ free(that_thread);
+ }
+ else
+ {
+ CtdlLogPrintf(CTDL_INFO, "Thread system, Started a sceduled thread \"%s\".\n",
+ that_thread->name);
+ pthread_mutex_unlock(&that_thread->ThreadMutex);
+ ctdl_thread_internal_calc_loadavg();
+ }
+ }
+ end_critical_section(S_THREAD_LIST);
+ }
+ }
+ end_critical_section(S_SCHEDULE_LIST);
+}
+
+