--- /dev/null
+Mon Jul 2 17:13:51 EEST 2007 Pekka Riikonen <priikone@silcnet.org>
+
+ * Added SILC Thread Pool API to lib/silcutil/silcthread.[ch],
+ and test program in lib/silcutil/tests/test_silcthread.c.
+
+-----------------------------------------------------------------------------
+
+For older changes please see the CHANGES file from the CVS tree.
o SilcStack aware SilcDList.
- o Thread pool API. Add this to lib/silcutil/silcthread.[ch].
-
- typedef void (*SilcThreadPoolFunc)(SilcSchedule schedule,
- void *context);
-
- /* Allocate thread pool with at least `min_threads' and at most
- `max_threads' many threads. If `stack' is non-NULL all memory
- is allocated from the `stack'. If `start_min_threads' is TRUE
- this will start `min_threads' many threads immediately. */
- SilcThreadPool silc_thread_pool_alloc(SilcStack stack,
- SilcUInt32 min_threads,
- SilcUInt32 max_threads,
- SilcBool start_min_threads);
-
- /* Free thread pool. If `wait_unfinished' is TRUE this will block
- and waits that all remaining active threads finish before freeing
- the pool. */
- void silc_thread_pool_free(SilcThreadPool tp, SilcBool wait_unfinished);
-
- /* Run `run' function with `run_context' in one of the threads in the
- thread pool. Returns FALSE if the thread pool is being freed. If
- there are no free threads left in the pool this will queue the
- the `run' and will call it once a thread becomes free.
-
- If `completion' is non-NULL it will be called to indicate completion
- of the `run' function. If `schedule' is non-NULL the `completion'
- will be called through the scheduler in the main thread. If it is
- NULL the `completion' is called directly from the thread after the
- `run' has returned. */
- SilcBool silc_thread_pool_run(SilcThreadPool tp,
- SilcSchedule schedule,
- SilcThreadPoolFunc run,
- void *run_context,
- SilcThreadPoolFunc completion,
- void *completion_context);
-
- /* Modify the amount of maximum threads of the pool. */
- void silc_thread_pool_set_max_threads(SilcThreadPool tp,
- SilcUInt32 max_threads);
-
- /* Returns the amount of maximum size the pool can grow. */
- SilcUInt32 silc_thread_pool_num_max_threads(SilcThreadPool tp);
-
- /* Returns the amount of free threads in the pool currently. */
- SilcUInt32 silc_thread_pool_num_free_threads(SilcThreadPool tp);
-
- /* Stops all free and started threads. The minumum amount of threads
- specified to silc_thread_pool_alloc always remains. */
- void silc_thread_pool_purge(SilcThreadPool tp);
+ o Thread pool API. Add this to lib/silcutil/silcthread.[ch]. (***DONE)
o Fast mutex implementation. Fast rwlock implementation. Mutex and
rwlock implementation using atomic operations.
o Library must have support for SERVICE command.
- o The server must be able to run behind NAT device. This means that
+ o The server must be able to run behind NAT device. This means that
Server ID must be based on public IP instead of private IP.
- o The following data must be in per-connection context: client id cache,
- server id cache, channel id cache, all statistics must be
+ o The following data must be in per-connection context: client id cache,
+ server id cache, channel id cache, all statistics must be
per-connection.
o The following data must be in per-thread context: command context
freelist/pool, pending commands, random number generator.
- o Do inccoming packet processing in an own FSM thread in the
+ o Do inccoming packet processing in an own FSM thread in the
server-threads FSM. Same as in client library.
o Reference count all Silc*Entry structures.
silctime.c \
silcmime.c \
silcstack.c \
- silcsnprintf.c
+ silcsnprintf.c \
+ silcthread.c
#ifdef SILC_DIST_TOOLKIT
include_HEADERS = \
--- /dev/null
+/*
+
+ silcthread.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2007 Pekka Riikonen
+
+ 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
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+
+#include "silc.h"
+
+/************************** Types and definitions ***************************/
+
+/* Thread pool thread context */
+typedef struct SilcThreadPoolThreadStruct {
+ struct SilcThreadPoolThreadStruct *next;
+ struct SilcThreadPoolThreadStruct *next2;
+ SilcThreadPool tp; /* The thread pool */
+ SilcSchedule schedule; /* Scheduler, may be NULL */
+ SilcThreadPoolFunc run; /* The function to run in a thread */
+ SilcThreadPoolFunc completion; /* Completion function */
+ void *run_context;
+ void *completion_context;
+ unsigned int stop : 1; /* Set to stop the thread */
+} *SilcThreadPoolThread;
+
+/* Completion context */
+typedef struct SilcThreadPoolCompletionStruct {
+ SilcSchedule schedule; /* Scheduler, may be NULL */
+ SilcThreadPoolFunc completion; /* Completion function */
+ void *completion_context;
+} *SilcThreadPoolCompletion;
+
+/* Thread pool context */
+struct SilcThreadPoolStruct {
+ SilcStack stack; /* Stack for memory allocation */
+ SilcMutex lock; /* Pool lock */
+ SilcCond pool_signal; /* Condition variable for signalling */
+ SilcList threads; /* Threads in the pool */
+ SilcList free_threads; /* Threads freelist */
+ SilcList queue; /* Queue for waiting calls */
+ SilcUInt16 min_threads; /* Minimum threads in the pool */
+ SilcUInt16 max_threads; /* Maximum threads in the pool */
+ SilcUInt16 refcnt; /* Reference counter */
+ unsigned int destroy : 1; /* Set when pool is to be destroyed */
+};
+
+/************************ Static utility functions **************************/
+
+/* Reference thread pool. Must be called locked. */
+
+static void silc_thread_pool_ref(SilcThreadPool tp)
+{
+ tp->refcnt++;
+ SILC_LOG_DEBUG(("Thread pool %p, refcnt %d -> %d", tp, tp->refcnt - 1,
+ tp->refcnt));
+}
+
+/* Unreference thread pool. Must be called locked. Releases the lock. */
+
+static void silc_thread_pool_unref(SilcThreadPool tp)
+{
+ tp->refcnt--;
+ SILC_LOG_DEBUG(("Thread pool %p refcnt %d -> %d", tp, tp->refcnt + 1,
+ tp->refcnt));
+ if (!tp->refcnt) {
+ silc_mutex_unlock(tp->lock);
+ silc_mutex_free(tp->lock);
+ silc_cond_free(tp->pool_signal);
+ silc_free(tp);
+ return;
+ }
+ silc_mutex_unlock(tp->lock);
+}
+
+/* Thread completion callback */
+
+SILC_TASK_CALLBACK(silc_thread_pool_run_completion)
+{
+ SilcThreadPoolCompletion c = context;
+ c->completion(c->schedule, c->completion_context);
+ silc_free(c);
+}
+
+/* The thread executor. Each thread in the pool is run here. They wait
+ here for something to do which is given to them by silc_thread_pool_run. */
+
+static void *silc_thread_pool_run_thread(void *context)
+{
+ SilcThreadPoolThread t = context, q;
+ SilcThreadPool tp = t->tp;
+ SilcMutex lock = tp->lock;
+ SilcCond pool_signal = tp->pool_signal;
+
+ silc_mutex_lock(lock);
+
+ while (1) {
+ /* Wait here for code to execute */
+ while (!t->run && !t->stop)
+ silc_cond_wait(pool_signal, lock);
+
+ if (t->stop) {
+ /* Stop the thread. Remove from threads list and free memory. */
+ SILC_LOG_DEBUG(("Stop thread %p", t));
+ silc_list_del(tp->threads, t);
+ silc_free(t);
+
+ /* If we are last thread, signal the waiting destructor. */
+ if (silc_list_count(tp->threads) == 0)
+ silc_cond_signal(pool_signal);
+
+ /* Release pool reference. Releases lock also. */
+ silc_thread_pool_unref(tp);
+ break;
+ }
+ silc_mutex_unlock(lock);
+
+ /* Execute code */
+ SILC_LOG_DEBUG(("Execute call %p, context %p, thread %p", t->run,
+ t->run_context, t));
+ t->run(t->schedule, t->run_context);
+
+ /* If scheduler is NULL, call completion directly from here. Otherwise
+ it is called through the scheduler in the thread where the scheduler
+ is running. */
+ if (t->completion) {
+ if (t->schedule) {
+ SilcThreadPoolCompletion c = silc_calloc(1, sizeof(*c));
+ if (c) {
+ SILC_LOG_DEBUG(("Run completion through scheduler %p", t->schedule));
+ c->schedule = t->schedule;
+ c->completion = t->completion;
+ c->completion_context = t->completion_context;
+ silc_schedule_task_add_timeout(c->schedule,
+ silc_thread_pool_run_completion, c,
+ 0, 0);
+ silc_schedule_wakeup(c->schedule);
+ } else {
+ t->completion(NULL, t->completion_context);
+ }
+ } else {
+ SILC_LOG_DEBUG(("Run completion directly"));
+ t->completion(NULL, t->completion_context);
+ }
+ }
+
+ silc_mutex_lock(lock);
+
+ /* Check if there are calls in queue */
+ if (silc_list_count(tp->queue) > 0) {
+ silc_list_start(tp->queue);
+ q = silc_list_get(tp->queue);
+
+ SILC_LOG_DEBUG(("Execute call from queue"));
+
+ /* Execute this call now */
+ t->run = q->run;
+ t->run_context = q->run_context;
+ t->completion = q->completion;
+ t->completion_context = q->completion_context;
+ t->schedule = q->schedule;
+
+ silc_list_del(tp->queue, q);
+ silc_free(q);
+ continue;
+ }
+
+ /* The thread is now free for use again. */
+ t->run = NULL;
+ t->completion = NULL;
+ t->schedule = NULL;
+ silc_list_add(tp->free_threads, t);
+ }
+
+ return NULL;
+}
+
+/* Creates new thread to thread pool */
+
+static SilcThreadPoolThread silc_thread_pool_new_thread(SilcThreadPool tp)
+{
+ SilcThreadPoolThread t;
+
+ t = silc_calloc(1, sizeof(*t));
+ if (!t)
+ return NULL;
+ t->tp = tp;
+ silc_list_add(tp->threads, t);
+ silc_list_add(tp->free_threads, t);
+ silc_thread_pool_ref(tp);
+
+ SILC_LOG_DEBUG(("Start thread %p", t));
+
+ /* Start the thread */
+ silc_thread_create(silc_thread_pool_run_thread, t, FALSE);
+
+ return t;
+}
+
+/**************************** Thread Pool API *******************************/
+
+/* Allocate thread pool */
+
+SilcThreadPool silc_thread_pool_alloc(SilcStack stack,
+ SilcUInt32 min_threads,
+ SilcUInt32 max_threads,
+ SilcBool start_min_threads)
+{
+ SilcThreadPool tp;
+ int i;
+
+ if (max_threads < min_threads)
+ return NULL;
+
+ tp = silc_calloc(1, sizeof(*tp));
+ if (!tp)
+ return NULL;
+
+ SILC_LOG_DEBUG(("Starting thread pool %p, min threads %d, max threads %d",
+ tp, min_threads, max_threads));
+
+ tp->stack = stack;
+ tp->min_threads = min_threads;
+ tp->max_threads = max_threads;
+ tp->refcnt++;
+
+ if (!silc_mutex_alloc(&tp->lock)) {
+ silc_free(tp);
+ return NULL;
+ }
+
+ if (!silc_cond_alloc(&tp->pool_signal)) {
+ silc_mutex_free(tp->lock);
+ silc_free(tp);
+ return NULL;
+ }
+
+ silc_list_init(tp->threads, struct SilcThreadPoolThreadStruct, next);
+ silc_list_init(tp->free_threads, struct SilcThreadPoolThreadStruct, next2);
+ silc_list_init(tp->queue, struct SilcThreadPoolThreadStruct, next);
+
+ for (i = 0; i < tp->min_threads && start_min_threads; i++)
+ silc_thread_pool_new_thread(tp);
+
+ return tp;
+}
+
+/* Free thread pool */
+
+void silc_thread_pool_free(SilcThreadPool tp, SilcBool wait_unfinished)
+{
+ SilcThreadPoolThread t;
+
+ SILC_LOG_DEBUG(("Free thread pool %p", tp));
+
+ silc_mutex_lock(tp->lock);
+ tp->destroy = TRUE;
+
+ /* Stop threads */
+ silc_list_start(tp->threads);
+ while ((t = silc_list_get(tp->threads)))
+ t->stop = TRUE;
+ silc_cond_signal(tp->pool_signal);
+
+ if (wait_unfinished) {
+ SILC_LOG_DEBUG(("Wait threads to finish"));
+ while (silc_list_count(tp->threads))
+ silc_cond_wait(tp->pool_signal, tp->lock);
+ }
+
+ /* Free calls from queue */
+ silc_list_start(tp->queue);
+ while ((t = silc_list_get(tp->queue)))
+ silc_free(t);
+ silc_list_init(tp->queue, struct SilcThreadPoolThreadStruct, next);
+
+ /* Release reference. Releases lock also. */
+ silc_thread_pool_unref(tp);
+}
+
+/* Execute code in a thread in the pool */
+
+SilcBool silc_thread_pool_run(SilcThreadPool tp,
+ SilcBool queuable,
+ SilcSchedule schedule,
+ SilcThreadPoolFunc run,
+ void *run_context,
+ SilcThreadPoolFunc completion,
+ void *completion_context)
+{
+ SilcThreadPoolThread t;
+
+ silc_mutex_lock(tp->lock);
+
+ if (tp->destroy) {
+ silc_mutex_unlock(tp->lock);
+ return FALSE;
+ }
+
+ /* Get free thread */
+ silc_list_start(tp->free_threads);
+ t = silc_list_get(tp->free_threads);
+ if (!t) {
+ if (silc_list_count(tp->threads) + 1 > tp->max_threads) {
+ /* Maximum threads reached */
+ if (!queuable) {
+ silc_mutex_unlock(tp->lock);
+ return FALSE;
+ }
+
+ SILC_LOG_DEBUG(("Queue call %p, context %p", run, run_context));
+
+ /* User wants to queue this call until thread becomes free */
+ t = silc_calloc(1, sizeof(*t));
+ if (!t) {
+ silc_mutex_unlock(tp->lock);
+ return FALSE;
+ }
+
+ t->run = run;
+ t->run_context = run_context;
+ t->completion = completion;
+ t->completion_context = completion_context;
+ t->schedule = schedule;
+
+ silc_list_add(tp->queue, t);
+ silc_mutex_unlock(tp->lock);
+ return TRUE;
+ } else {
+ /* Create new thread */
+ t = silc_thread_pool_new_thread(tp);
+ if (!t) {
+ silc_mutex_unlock(tp->lock);
+ return FALSE;
+ }
+ }
+ }
+
+ SILC_LOG_DEBUG(("Run call %p, context %p, thread %p", run, run_context, t));
+
+ /* Mark this call to be executed in this thread */
+ t->run = run;
+ t->run_context = run_context;
+ t->completion = completion;
+ t->completion_context = completion_context;
+ t->schedule = schedule;
+ silc_list_del(tp->free_threads, t);
+
+ /* Signal threads */
+ silc_cond_signal(tp->pool_signal);
+
+ silc_mutex_unlock(tp->lock);
+ return TRUE;
+}
+
+/* Set maximum threads in the pool */
+
+void silc_thread_pool_set_max_threads(SilcThreadPool tp,
+ SilcUInt32 max_threads)
+{
+ SILC_LOG_DEBUG(("Set thread pool %p max threads to %d", tp, max_threads));
+
+ silc_mutex_lock(tp->lock);
+ tp->max_threads = max_threads;
+ silc_mutex_unlock(tp->lock);
+}
+
+/* Get maximum threads in the pool */
+
+SilcUInt32 silc_thread_pool_num_max_threads(SilcThreadPool tp)
+{
+ SilcUInt32 max_threads;
+
+ silc_mutex_lock(tp->lock);
+ max_threads = tp->max_threads;
+ silc_mutex_unlock(tp->lock);
+
+ return max_threads;
+}
+
+/* Get numnber of free threads in the pool */
+
+SilcUInt32 silc_thread_pool_num_free_threads(SilcThreadPool tp)
+{
+ SilcUInt32 free_threads;
+
+ silc_mutex_lock(tp->lock);
+ free_threads = silc_list_count(tp->free_threads);
+ silc_mutex_unlock(tp->lock);
+
+ return free_threads;
+}
+
+/* Purge pool */
+
+void silc_thread_pool_purge(SilcThreadPool tp)
+{
+ SilcThreadPoolThread t;
+ int i;
+
+ silc_mutex_lock(tp->lock);
+
+ if (silc_list_count(tp->free_threads) <= tp->min_threads) {
+ silc_mutex_unlock(tp->lock);
+ return;
+ }
+
+ i = silc_list_count(tp->free_threads) - tp->min_threads;
+
+ SILC_LOG_DEBUG(("Purge %d threads", i));
+
+ silc_list_start(tp->threads);
+ while ((t = silc_list_get(tp->threads))) {
+ if (t->run)
+ continue;
+
+ t->stop = TRUE;
+ silc_list_del(tp->free_threads, t);
+
+ i--;
+ if (!i)
+ break;
+ }
+
+ /* Signal threads to stop */
+ silc_cond_signal(tp->pool_signal);
+
+ silc_mutex_unlock(tp->lock);
+}
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2001 - 2005 Pekka Riikonen
+ Copyright (C) 2001 - 2007 Pekka Riikonen
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
*
* DESCRIPTION
*
- * Interface for SILC Thread implementation. This is platform independent
- * interface of threads for applications that need concurrent execution
- * with the application's main thread. The threads created with this
- * interface executes concurrently with the calling thread.
+ * Interface for platform independent thread implementation and thread pool
+ * system. The interface provides routines for applications that need
+ * concurrent execution with the application's main thread. The threads
+ * created with this interface executes concurrently with the calling thread.
+ *
+ * The thread pool system can be used to start many threads and execute code
+ * in the threads. The thread pool manages the threads creation and
+ * destruction.
*
***/
#ifndef SILCTHREAD_H
#define SILCTHREAD_H
+#include "silcschedule.h"
+
/* Prototypes */
/****s* silcutil/SilcThreadAPI/SilcThread
***/
void silc_thread_yield(void);
+/****s* silcutil/SilcThreadAPI/SilcThreadPool
+ *
+ * NAME
+ *
+ * typedef struct SilcThreadPoolStruct *SilcThreadPool;
+ *
+ * DESCRIPTION
+ *
+ * This context is the actual SILC Thread Pool and is returned by
+ * the silc_thread_pool_alloc function, and given as arguments to
+ * some of the silc_thread_pool_* functions. This context and its
+ * resources are freed by calling silc_thread_pool_free;
+ *
+ ***/
+typedef struct SilcThreadPoolStruct *SilcThreadPool;
+
+/****f* silcutil/SilcThreadAPI/SilcThreadPoolFunc
+ *
+ * SYNOPSIS
+ *
+ * typedef void (*SilcThreadPoolFunc)(SilcSchedule schedule,
+ * void *context);
+ *
+ * DESCRIPTION
+ *
+ * A callback function of this type is given as argument to the
+ * silc_thread_pool_run. The `schedule' is the scheduler and the
+ * `context' is the `run_context' or `completion_context' given as
+ * argument to silc_thread_pool_run.
+ *
+ ***/
+typedef void (*SilcThreadPoolFunc)(SilcSchedule schedule, void *context);
+
+/****f* silcutil/SilcThreadAPI/silc_thread_pool_alloc
+ *
+ * SYNOPSIS
+ *
+ * SilcThreadPool silc_thread_pool_alloc(SilcStack stack,
+ * SilcUInt32 min_threads,
+ * SilcUInt32 max_threads,
+ * SilcBool start_min_threads);
+ *
+ * DESCRIPTION
+ *
+ * Allocate thread pool with at least `min_threads' and at most
+ * `max_threads' many threads. If `stack' is non-NULL all memory is
+ * allocated from the `stack'. If `start_min_threads' is TRUE this will
+ * start `min_threads' many threads immediately. Returns the thread
+ * pool context or NULL on error.
+ *
+ * EXAMPLE
+ *
+ * // Start thread pool, by default it has 0 threads.
+ * pool = silc_thread_pool_alloc(NULL, 0, 5, FALSE);
+ *
+ * // Function to execute in a thread
+ * void my_func(SilcSchedule schedule, void *context)
+ * {
+ * MyContext mycontext = context;
+ * ...
+ * }
+ *
+ * // Execute code in a thread in the pool
+ * silc_thread_pool_run(pool, TRUE, NULL, my_func, my_context, NULL, NULL);
+ *
+ ***/
+SilcThreadPool silc_thread_pool_alloc(SilcStack stack,
+ SilcUInt32 min_threads,
+ SilcUInt32 max_threads,
+ SilcBool start_min_threads);
+
+/****f* silcutil/SilcThreadAPI/silc_thread_pool_free
+ *
+ * SYNOPSIS
+ *
+ * void silc_thread_pool_free(SilcThreadPool tp, SilcBool wait_unfinished);
+ *
+ * DESCRIPTION
+ *
+ * Free the thread pool. If `wait_unfinished' is TRUE this will block
+ * and waits that all remaining active threads finish before freeing
+ * the pool.
+ *
+ ***/
+void silc_thread_pool_free(SilcThreadPool tp, SilcBool wait_unfinished);
+
+/****f* silcutil/SilcThreadAPI/silc_thread_pool_run
+ *
+ * SYNOPSIS
+ *
+ * SilcBool silc_thread_pool_run(SilcThreadPool tp,
+ * SilcBool queueable,
+ * SilcSchedule schedule,
+ * SilcThreadPoolFunc run,
+ * void *run_context,
+ * SilcThreadPoolFunc completion,
+ * void *completion_context);
+ *
+ * DESCRIPTION
+ *
+ * Run the `run' function with `run_context' in one of the threads in the
+ * thread pool. Returns FALSE if the thread pool is being freed. If
+ * there are no free threads left in the pool this will queue the `run'
+ * and call it once a thread becomes free, if `queueable' is TRUE. If
+ * `queueable' is FALSE and there are no free threads, this returns FALSE
+ * and `run' is not executed.
+ *
+ * If `completion' is non-NULL it will be called to indicate completion
+ * of the `run' function. If `schedule' is non-NULL the `completion'
+ * will be called through the scheduler in the main thread. If it is
+ * NULL the `completion' is called directly from the thread after the
+ * `run' has returned.
+ *
+ ***/
+SilcBool silc_thread_pool_run(SilcThreadPool tp,
+ SilcBool queue,
+ SilcSchedule schedule,
+ SilcThreadPoolFunc run,
+ void *run_context,
+ SilcThreadPoolFunc completion,
+ void *completion_context);
+
+/****f* silcutil/SilcThreadAPI/silc_thread_pool_set_max_threads
+ *
+ * SYNOPSIS
+ *
+ * void silc_thread_pool_set_max_threads(SilcThreadPool tp,
+ * SilcUInt32 max_threads);
+ *
+ * DESCRIPTION
+ *
+ * Modify the amount of maximum threads of the pool. This call does not
+ * affect any currently active or running thread.
+ *
+ ***/
+void silc_thread_pool_set_max_threads(SilcThreadPool tp,
+ SilcUInt32 max_threads);
+
+/****f* silcutil/SilcThreadAPI/silc_thread_pool_num_max_threads
+ *
+ * SYNOPSIS
+ *
+ * SilcUInt32 silc_thread_pool_num_max_threads(SilcThreadPool tp);
+ *
+ * DESCRIPTION
+ *
+ * Returns the number of maximum threads to which the pool can grow.
+ *
+ ***/
+SilcUInt32 silc_thread_pool_num_max_threads(SilcThreadPool tp);
+
+/****f* silcutil/SilcThreadAPI/silc_thread_pool_num_free_threads
+ *
+ * SYNOPSIS
+ *
+ * SilcUInt32 silc_thread_pool_num_free_threads(SilcThreadPool tp);
+ *
+ * DESCRIPTION
+ *
+ * Returns the number of free threads in the pool currently. Free threads
+ * are threads that are not currently executing any code.
+ *
+ ***/
+SilcUInt32 silc_thread_pool_num_free_threads(SilcThreadPool tp);
+
+/****f* silcutil/SilcThreadAPI/silc_thread_pool_purge
+ *
+ * SYNOPSIS
+ *
+ * void silc_thread_pool_purge(SilcThreadPool tp);
+ *
+ * DESCRIPTION
+ *
+ * Stops all free and started threads. The minumum amount of threads
+ * specified to silc_thread_pool_alloc always remains. Any thread that
+ * is currently executing code is not affected by this call.
+ *
+ ***/
+void silc_thread_pool_purge(SilcThreadPool tp);
+
#endif
#
# Author: Pekka Riikonen <priikone@silcnet.org>
#
-# Copyright (C) 2004 - 2005 Pekka Riikonen
+# Copyright (C) 2004 - 2007 Pekka Riikonen
#
# 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
bin_PROGRAMS = test_silcstrutil test_silcstringprep test_silchashtable \
test_silclist test_silcfsm test_silcasync test_silcschedule \
test_silcnet test_silcstack test_silcmime test_silcfdstream \
- test_silcatomic test_silcmutex test_silctime
+ test_silcatomic test_silcmutex test_silctime test_silcthread
test_silcstrutil_SOURCES = test_silcstrutil.c
test_silcstringprep_SOURCES = test_silcstringprep.c
test_silcatomic_SOURCES = test_silcatomic.c
test_silcmutex_SOURCES = test_silcmutex.c
test_silctime_SOURCES = test_silctime.c
+test_silcthread_SOURCES = test_silcthread.c
LIBS = $(SILC_COMMON_LIBS)
LDADD = -L.. -L../.. -lsilc
--- /dev/null
+/* SilcThreadPool tests */
+
+#include "silc.h"
+
+SilcSchedule schedule;
+
+static void func(SilcSchedule schedule, void *context)
+{
+ SILC_LOG_DEBUG(("func: %d", (int)context));
+ sleep(1);
+}
+
+static void compl(SilcSchedule schedule, void *context)
+{
+ SILC_LOG_DEBUG(("completion: %d", (int)context));
+ if ((int)context == 0xff)
+ silc_schedule_stop(schedule);
+}
+
+int main(int argc, char **argv)
+{
+ SilcBool success = FALSE;
+ SilcThreadPool tp;
+ int i;
+
+ if (argc > 1 && !strcmp(argv[1], "-d")) {
+ silc_log_debug(TRUE);
+ silc_log_quick(TRUE);
+ silc_log_debug_hexdump(TRUE);
+ silc_log_set_debug_string("*thread*");
+ }
+
+ schedule = silc_schedule_init(0, NULL);
+ if (!schedule)
+ goto err;
+
+ SILC_LOG_DEBUG(("Allocate thread pool"));
+ tp = silc_thread_pool_alloc(NULL, 2, 4, TRUE);
+ if (!tp)
+ goto err;
+ SILC_LOG_DEBUG(("Stop thread pool"));
+ silc_thread_pool_free(tp, TRUE);
+
+
+ SILC_LOG_DEBUG(("Allocate thread pool"));
+ tp = silc_thread_pool_alloc(NULL, 0, 2, TRUE);
+ if (!tp)
+ goto err;
+ for (i = 0; i < 4; i++) {
+ SILC_LOG_DEBUG(("Run thread %d", i + 1));
+ if (!silc_thread_pool_run(tp, TRUE, NULL, func, (void *) i + 1,
+ compl, (void *)i + 1))
+ goto err;
+ }
+ sleep(3);
+ SILC_LOG_DEBUG(("Stop thread pool"));
+ silc_thread_pool_free(tp, TRUE);
+
+ SILC_LOG_DEBUG(("Allocate thread pool"));
+ tp = silc_thread_pool_alloc(NULL, 0, 2, TRUE);
+ if (!tp)
+ goto err;
+ for (i = 0; i < 2; i++) {
+ SILC_LOG_DEBUG(("Run thread %d", i + 1));
+ if (!silc_thread_pool_run(tp, FALSE, NULL, func, (void *) i + 1,
+ compl, (void *)i + 1))
+ goto err;
+ }
+ if (silc_thread_pool_run(tp, FALSE, NULL, func, (void *)3,
+ compl, (void *)3))
+ goto err;
+ sleep(3);
+ SILC_LOG_DEBUG(("Stop thread pool"));
+ silc_thread_pool_free(tp, TRUE);
+
+ SILC_LOG_DEBUG(("Allocate thread pool"));
+ tp = silc_thread_pool_alloc(NULL, 8, 16, FALSE);
+ if (!tp)
+ goto err;
+ for (i = 0; i < 8; i++) {
+ SILC_LOG_DEBUG(("Run thread %d", i + 1));
+ if (!silc_thread_pool_run(tp, FALSE, schedule, func, (void *) i + 1,
+ compl, (void *)i + 1))
+ goto err;
+ }
+ if (!silc_thread_pool_run(tp, FALSE, schedule, func, (void *)0xff,
+ compl, (void *)0xff))
+ goto err;
+
+ silc_schedule(schedule);
+
+ SILC_LOG_DEBUG(("Stop thread pool"));
+ silc_thread_pool_free(tp, TRUE);
+
+ silc_schedule_uninit(schedule);
+ success = TRUE;
+
+ err:
+ SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
+ fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
+
+ return success;
+}