From: Pekka Riikonen Date: Sat, 15 Dec 2007 18:03:49 +0000 (+0000) Subject: Added SILC Tls API for Thread-local storage. Added SilcTls X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=c6bb9e5e82ae3a2d667e826b9bd979cbe72e1493 Added SILC Tls API for Thread-local storage. Added SilcTls structure that is now in all threads' Tls. --- diff --git a/CHANGES.RUNTIME b/CHANGES.RUNTIME index 9d7a6ffc..119de24d 100644 --- a/CHANGES.RUNTIME +++ b/CHANGES.RUNTIME @@ -1,3 +1,10 @@ +Sat Dec 15 19:59:53 EET 2007 Pekka Riikonen + + * Added SILC Tls API for Thread-local storage in + lib/silcutil/silcthread.[ch]. Added all platform specific + implementations. Added SilcTls structure to + lib/silcutil/silcthread_i.h that is now in all threads' Tls. + Thu Dec 13 17:37:21 EET 2007 Pekka Riikonen * Clear the locked flag before unlocking mutex, not after. diff --git a/TODO b/TODO index be0f0e5f..20ff6e95 100644 --- a/TODO +++ b/TODO @@ -73,7 +73,7 @@ lib/silcclient, The Client Library access to them for programmers. Currently these have to be digged up from the packet stream. - o Connection option that attemps to connect to remot host with various + o Connection option that attemps to connect to remot host with various different mechanisms: UDP 706, TCP 706, TCP 80, TCP 443, UDP 7706 and TCP 7706. This is the so called hole punching mechanism. @@ -105,7 +105,7 @@ Runtime library, lib/silcutil/ o silc_getopt routines - o Add silc_stream_get_root and add get_root stream operation. It + o Add silc_stream_get_root and add get_root stream operation. It returns the root of the stream or NULL if stream doesn't have root. o Change some stream routines (like socket stream API) to accept ANY @@ -217,7 +217,7 @@ Runtime library, lib/silcutil/ _ua unaligned memory allocation routines. Remove unaligned memory allocation possibility. (***DONE) - o silc_stack_alloc shouldn't require multiple by 8 size argument, it + o silc_stack_alloc shouldn't require multiple by 8 size argument, it should figure it out itself. o silc_malloc et. al. to respect --with-alignment. @@ -243,10 +243,13 @@ Runtime library, lib/silcutil/ silc_snprintf(buf, sizeof(buf), "Client ID %@", id_renderer, client_id); (***DONE) + o SILC Tls (Thread-local storage) API to lib/silcutil/silcthread.[ch]. + (***DONE) + o Change silc_gettimeofday on Unix to use clock_gettime with REALTIME clock if it is available, otherwise use gettimeofday(). (***DONE) - (o SilcIpAddr abstraction. Ipv4 and Ipv6 support to the abstaction.) + (o SilcIpAddr abstraction. Ipv4 and Ipv6 support to the abstaction.) maybe (o Generic SilcStatus or SilcResult that includes all possible status and @@ -377,7 +380,7 @@ SILC Accelerator Library (***DONE) o Implement software accelerator. It is a thread pool system where the - public key and private key operations are executed in threads. + public key and private key operations are executed in threads. (***DONE) o Add init options to SilcAcceleratorObject as a SilcAcceleratorOption @@ -412,7 +415,7 @@ lib/silcmath ============ o Import TFM. We want TFM's speed but its memory requirements are - just too much. By default it uses large pre-allocated tables which + just too much. By default it uses large pre-allocated tables which will eat memory when there are thousands of public keys in system. We probably want to change TFM's fp_int dynamic so that a specific size can be allocated for the int. We could have two new functions: @@ -435,7 +438,7 @@ lib/silcmath any other MP function (including utility ones) that may allocate memory. - o Prime generation progress using callback instead of printing to + o Prime generation progress using callback instead of printing to stdout. o All utility functions should be made non-allocating ones. @@ -546,7 +549,7 @@ lib/silcserver term UDP sessions. 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 (See + Server ID must be based on public IP instead of private IP (See also NAT detection protocol in SILC protocol specification). o The following data must be in per-connection context: client id cache, @@ -564,7 +567,7 @@ lib/silcserver by default (at least try to bind). Connections must work normally even if they were established to some other port other than 706. - Connection option that attemps to connect to remot server with various + Connection option that attemps to connect to remot server with various different mechanisms: UDP 706, TCP 706, TCP 80, TCP 443, UDP 7706 and TCP 7706. This is the so called hole punching mechanism. @@ -574,7 +577,7 @@ lib/silcserver Some issues that must be kept in mind from 1.0 and 1.1 silcd's: - o The server and router software MUST work out of the box. After + o The server and router software MUST work out of the box. After installation the server must not require any configuration to run the most basic working configuration. No defining IP addresses, etc. The server must work just by running it. diff --git a/configure.ad b/configure.ad index bb21ee68..0e53cfa9 100644 --- a/configure.ad +++ b/configure.ad @@ -1246,7 +1246,7 @@ if test x$has_threads = xtrue; then esac # Check for threads - AC_CHECK_FUNC(pthread_create) + AC_CHECK_FUNCS(pthread_create pthread_key_create pthread_once) # Check for read/write locks AC_CHECK_FUNC(pthread_rwlock_init, diff --git a/lib/silcutil/silcthread.c b/lib/silcutil/silcthread.c index 794259c7..3ada012c 100644 --- a/lib/silcutil/silcthread.c +++ b/lib/silcutil/silcthread.c @@ -19,6 +19,8 @@ #include "silc.h" +/***************************** Thread Pool API *****************************/ + /* Explanation of the thread pool execution. When new call is added to thread pool by calling silc_thread_pool_run @@ -572,3 +574,27 @@ void silc_thread_pool_purge(SilcThreadPool tp) silc_list_start(tp->threads); silc_mutex_unlock(tp->lock); } + +/*************************** Thread-local Storage ***************************/ + +void silc_thread_tls_set(void *context) +{ + SilcTls tls = silc_thread_get_tls(); + + if (!tls) { + /* Initialize Tls for this thread */ + tls = silc_thread_tls_init(); + if (!tls) + return; + } + + tls->thread_context = context; +} + +void *silc_thread_tls_get(void) +{ + SilcTls tls = silc_thread_get_tls(); + if (!tls) + return NULL; + return tls->thread_context; +} diff --git a/lib/silcutil/silcthread.h b/lib/silcutil/silcthread.h index c439b38d..5afc0a03 100644 --- a/lib/silcutil/silcthread.h +++ b/lib/silcutil/silcthread.h @@ -30,6 +30,9 @@ * in the threads. The thread pool manages the threads creation and * destruction. * + * The interface also provides routines for accessing the Thread-local + * storage (Tls) on all supported platforms. + * ***/ #ifndef SILCTHREAD_H @@ -349,4 +352,38 @@ SilcUInt32 silc_thread_pool_num_free_threads(SilcThreadPool tp); ***/ void silc_thread_pool_purge(SilcThreadPool tp); +/****f* silcutil/SilcThreadAPI/silc_thread_tls_set + * + * SYNOPSIS + * + * void silc_thread_tls_set(void *context); + * + * DESCRIPTION + * + * Sets `context' into the Thread-local storage. Any previously set + * value will be replaced. This function may be called for the main + * thread also. This function may be called also if the program does + * not support threads. + * + * To retrieve the context from the Tls, call silc_thread_tls_get. + * + ***/ +void silc_thread_tls_set(void *context); + +/****f* silcutil/SilcThreadAPI/silc_thread_tls_get + * + * SYNOPSIS + * + * void *silc_thread_tls_get(void); + * + * DESCRIPTION + * + * Returns context from the Thread-local storage. If context has no been + * set for the current thread NULL will be returned. + * + ***/ +void *silc_thread_tls_get(void); + +#include "silcthread_i.h" + #endif diff --git a/lib/silcutil/symbian/silcsymbianthread.cpp b/lib/silcutil/symbian/silcsymbianthread.cpp index 10d7142a..05a2e893 100644 --- a/lib/silcutil/symbian/silcsymbianthread.cpp +++ b/lib/silcutil/symbian/silcsymbianthread.cpp @@ -46,9 +46,12 @@ static TInt silc_thread_start(TAny *context) void *user_context = tc->context; SilcBool waitable = tc->waitable; void *ret = NULL; + SilcTls tls; silc_free(tc); + tls = silc_thread_tls_init(); + CTrapCleanup *cs = CTrapCleanup::New(); if (cs) { CActiveScheduler *s = new CActiveScheduler; @@ -63,6 +66,7 @@ static TInt silc_thread_start(TAny *context) delete cs; } + silc_free(tls); silc_thread_exit(ret); #endif @@ -409,4 +413,28 @@ SilcBool silc_cond_timedwait(SilcCond cond, SilcMutex mutex, #endif /* SILC_THREADS*/ } +/************************** Thread-local Storage ****************************/ + +SilcTls silc_thread_tls_init(void) +{ + SilcTls tls; + + if (silc_thread_get_tls()) + return silc_thread_get_tls(); + + /* Allocate Tls for the thread */ + tls = (SilcTls)silc_calloc(1, sizeof(*tls)); + if (!tls) + return NULL; + + Dll::SetTls(tls); + + return tls; +} + +SilcTls silc_thread_get_tls(void) +{ + return STATIC_CAST(SilcTls, Dll::Tls()); +} + } /* extern "C" */ diff --git a/lib/silcutil/tests/test_silcthread.c b/lib/silcutil/tests/test_silcthread.c index d59ed4e0..19315701 100644 --- a/lib/silcutil/tests/test_silcthread.c +++ b/lib/silcutil/tests/test_silcthread.c @@ -6,7 +6,8 @@ SilcSchedule schedule; static void func(SilcSchedule schedule, void *context) { - SILC_LOG_DEBUG(("func: %d", (int)context)); + silc_thread_tls_set(context); + SILC_LOG_DEBUG(("func: %d", (int)silc_thread_tls_get())); sleep(1); } diff --git a/lib/silcutil/unix/silcunixthread.c b/lib/silcutil/unix/silcunixthread.c index 2415a380..9ffb365c 100644 --- a/lib/silcutil/unix/silcunixthread.c +++ b/lib/silcutil/unix/silcunixthread.c @@ -22,10 +22,29 @@ /**************************** SILC Thread API *******************************/ +typedef struct { + SilcThreadStart start_func; + void *context; +} *SilcThreadStartContext; + +static void *silc_thread_start(void *context) +{ + SilcThreadStartContext c = context; + SilcThreadStart start_func = c->start_func; + void *start_context = c->context; + + silc_free(c); + + silc_thread_tls_init(); + + return start_func(start_context); +} + SilcThread silc_thread_create(SilcThreadStart start_func, void *context, SilcBool waitable) { #ifdef SILC_THREADS + SilcThreadStartContext c; pthread_attr_t attr; pthread_t thread; int ret; @@ -35,8 +54,15 @@ SilcThread silc_thread_create(SilcThreadStart start_func, void *context, if (!start_func) return NULL; + c = silc_calloc(1, sizeof(*c)); + if (!c) + return NULL; + c->start_func = start_func; + c->context = context; + if (pthread_attr_init(&attr)) { SILC_LOG_ERROR(("Thread error: %s", strerror(errno))); + silc_free(c); return NULL; } @@ -45,14 +71,15 @@ SilcThread silc_thread_create(SilcThreadStart start_func, void *context, PTHREAD_CREATE_DETACHED)) { SILC_LOG_ERROR(("Thread error: %s", strerror(errno))); pthread_attr_destroy(&attr); + silc_free(c); return NULL; } - ret = pthread_create(&thread, &attr, (void * (*)(void *))start_func, - context); + ret = pthread_create(&thread, &attr, silc_thread_start, c); if (ret) { SILC_LOG_ERROR(("Thread error: %s", strerror(errno))); pthread_attr_destroy(&attr); + silc_free(c); return NULL; } @@ -295,3 +322,69 @@ SilcBool silc_cond_timedwait(SilcCond cond, SilcMutex mutex, return FALSE; #endif /* SILC_THREADS*/ } + +/************************** Thread-local Storage ****************************/ + +#if (defined(SILC_THREADS) && defined(HAVE_PTHREAD_KEY_CREATE) && \ + defined(HAVE_PTHREAD_ONCE)) + +static pthread_key_t key; +static pthread_once_t key_once; + +static void silc_thread_tls_destructor(void *context) +{ + silc_free(context); +} + +static void silc_thread_tls_alloc(void) +{ + if (pthread_key_create(&key, silc_thread_tls_destructor)) + SILC_LOG_ERROR(("Error creating Thread-local storage")); +} + +SilcTls silc_thread_tls_init(void) +{ + SilcTls tls; + + pthread_once(&key_once, silc_thread_tls_alloc); + + if (silc_thread_get_tls()) + return silc_thread_get_tls(); + + /* Allocate Tls for the thread */ + tls = silc_calloc(1, sizeof(*tls)); + if (!tls) { + SILC_LOG_ERROR(("Error allocating Thread-local storage")); + return NULL; + } + + pthread_setspecific(key, tls); + return tls; +} + +SilcTls silc_thread_get_tls(void) +{ + return pthread_getspecific(key); +} + +#else + +SilcTlsStruct tls; +SilcTls tls_ptr = NULL; + +SilcTls silc_thread_tls_init(void) +{ + if (silc_thread_get_tls()) + return silc_thread_get_tls(); + + tls_ptr = &tls; + memset(tls_ptr, 0, sizeof(*tls_ptr)); + return tls_ptr; +} + +SilcTls silc_thread_get_tls(void) +{ + return tls_ptr; +} + +#endif diff --git a/lib/silcutil/win32/silcwin32thread.c b/lib/silcutil/win32/silcwin32thread.c index 15b1ca83..cdcd50c0 100644 --- a/lib/silcutil/win32/silcwin32thread.c +++ b/lib/silcutil/win32/silcwin32thread.c @@ -32,8 +32,6 @@ typedef struct { SilcBool waitable; } *SilcWin32Thread; -static DWORD silc_thread_tls; - /* Actual routine that is called by WIN32 when the thread is created. We will call the start_func from here. When this returns the thread is destroyed. */ @@ -41,13 +39,16 @@ static DWORD silc_thread_tls; unsigned __stdcall silc_thread_win32_start(void *context) { SilcWin32Thread thread = (SilcWin32Thread)context; + SilcTls tls; - silc_thread_tls = TlsAlloc(); - if (silc_thread_tls != TLS_OUT_OF_INDEXES) - TlsSetValue(silc_thread_tls, context); + tls = silc_thread_tls_init(); + if (tls) + tls->platform_context = thread; silc_thread_exit(thread->start_func(thread->context)); + silc_free(tls); + return 0; } #endif @@ -62,6 +63,8 @@ SilcThread silc_thread_create(SilcThreadStart start_func, void *context, SILC_LOG_DEBUG(("Creating new thread")); thread = silc_calloc(1, sizeof(*thread)); + if (!thread) + return NULL; thread->start_func = start_func; thread->context = context; thread->waitable = waitable; @@ -86,7 +89,8 @@ SilcThread silc_thread_create(SilcThreadStart start_func, void *context, void silc_thread_exit(void *exit_value) { #ifdef SILC_THREADS - SilcWin32Thread thread = TlsGetValue(silc_thread_tls); + SilcTls tls = silc_thread_get_tls(); + SilcWin32Thread thread = tls->platform_context; if (thread) { /* If the thread is waitable the memory is freed only in silc_thread_wait @@ -95,7 +99,6 @@ void silc_thread_exit(void *exit_value) silc_free(thread); } - TlsFree(silc_thread_tls); _endthreadex(0); #endif } @@ -103,17 +106,20 @@ void silc_thread_exit(void *exit_value) SilcThread silc_thread_self(void) { #ifdef SILC_THREADS - SilcWin32Thread self = TlsGetValue(silc_thread_tls); + SilcTls tls = silc_thread_get_tls(); + SilcWin32Thread self = tls->platform_context; if (!self) { /* This should only happen for the main thread. */ HANDLE handle = GetCurrentThread (); HANDLE process = GetCurrentProcess (); self = silc_calloc(1, sizeof(*self)); - DuplicateHandle(process, handle, process, - &self->thread, 0, FALSE, - DUPLICATE_SAME_ACCESS); - TlsSetValue(silc_thread_tls, self); + if (self) { + DuplicateHandle(process, handle, process, + &self->thread, 0, FALSE, + DUPLICATE_SAME_ACCESS); + tls->platform_context = self; + } } return (SilcThread)self; @@ -391,3 +397,65 @@ SilcBool silc_cond_timedwait(SilcCond cond, SilcMutex mutex, #endif /* SILC_THREADS*/ return TRUE; } + +/************************** Thread-local Storage ****************************/ + +#ifdef SILC_THREADS + +static DWORD silc_tls; +SilcBool silc_tls_set = FALSE; + +SilcTls silc_thread_tls_init(void) +{ + SilcTls tls; + + if (!silc_tls_set) { + silc_tls = TlsAlloc(); + if (silc_tls == TLS_OUT_OF_INDEXES) { + SILC_LOG_ERROR(("Error creating Thread-local storage")); + return NULL; + } + + silc_tls_set = TRUE; + } + + if (silc_thread_get_tls()) + return silc_thread_get_tls(); + + /* Allocate Tls for the thread */ + tls = silc_calloc(1, sizeof(*tls)); + if (!tls) { + SILC_LOG_ERROR(("Error allocating Thread-local storage")); + return NULL; + } + + TlsSetValue(silc_tls, tls); + return tls; +} + +SilcTls silc_thread_get_tls(void) +{ + return (SilcTls)TlsGetValue(silc_tls); +} + +#else + +SilcTlsStruct tls; +SilcTls tls_ptr = NULL; + +SilcTls silc_thread_tls_init(void) +{ + if (silc_thread_get_tls()) + return silc_thread_get_tls(); + + tls_ptr = &tls; + memset(tls_ptr, 0, sizeof(*tls_ptr)); + return tls_ptr; +} + +SilcTls silc_thread_get_tls(void) +{ + return tls_ptr; +} + +#endif /* SILC_THREADS */