From: Pekka Riikonen Date: Mon, 2 Jul 2007 14:20:42 +0000 (+0000) Subject: Added SILC Thread Pool API. X-Git-Tag: 1.2.beta1~234 X-Git-Url: http://git.silcnet.org/gitweb/?p=crypto.git;a=commitdiff_plain;h=da102216367c1d997f60755357d759e03f87940a Added SILC Thread Pool API. --- diff --git a/CHANGES.RUNTIME b/CHANGES.RUNTIME new file mode 100644 index 00000000..6f9443f7 --- /dev/null +++ b/CHANGES.RUNTIME @@ -0,0 +1,8 @@ +Mon Jul 2 17:13:51 EEST 2007 Pekka Riikonen + + * 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. diff --git a/TODO b/TODO index 92ce8299..ec144295 100644 --- a/TODO +++ b/TODO @@ -187,55 +187,7 @@ Runtime library, lib/silcutil/ 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. @@ -642,17 +594,17 @@ lib/silcserver 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. diff --git a/lib/silcutil/Makefile.ad b/lib/silcutil/Makefile.ad index b685a75f..d14a624d 100644 --- a/lib/silcutil/Makefile.ad +++ b/lib/silcutil/Makefile.ad @@ -68,7 +68,8 @@ libsilcutil_la_SOURCES = \ silctime.c \ silcmime.c \ silcstack.c \ - silcsnprintf.c + silcsnprintf.c \ + silcthread.c #ifdef SILC_DIST_TOOLKIT include_HEADERS = \ diff --git a/lib/silcutil/silcthread.c b/lib/silcutil/silcthread.c new file mode 100644 index 00000000..0c6d6421 --- /dev/null +++ b/lib/silcutil/silcthread.c @@ -0,0 +1,439 @@ +/* + + silcthread.c + + Author: Pekka Riikonen + + 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); +} diff --git a/lib/silcutil/silcthread.h b/lib/silcutil/silcthread.h index 3a4036f6..2a788804 100644 --- a/lib/silcutil/silcthread.h +++ b/lib/silcutil/silcthread.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - 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 @@ -21,16 +21,22 @@ * * 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 @@ -162,4 +168,184 @@ SilcBool silc_thread_wait(SilcThread thread, void **exit_value); ***/ 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 diff --git a/lib/silcutil/tests/Makefile.am b/lib/silcutil/tests/Makefile.am index f08893d7..4b5513a9 100644 --- a/lib/silcutil/tests/Makefile.am +++ b/lib/silcutil/tests/Makefile.am @@ -3,7 +3,7 @@ # # Author: Pekka Riikonen # -# 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 @@ -20,7 +20,7 @@ AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign 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 @@ -36,6 +36,7 @@ test_silcfdstream_SOURCES = test_silcfdstream.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 diff --git a/lib/silcutil/tests/test_silcthread.c b/lib/silcutil/tests/test_silcthread.c new file mode 100644 index 00000000..920cb6f9 --- /dev/null +++ b/lib/silcutil/tests/test_silcthread.c @@ -0,0 +1,103 @@ +/* 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; +}