Added SILC Tls API for Thread-local storage. Added SilcTls
authorPekka Riikonen <priikone@silcnet.org>
Sat, 15 Dec 2007 18:03:49 +0000 (18:03 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 15 Dec 2007 18:03:49 +0000 (18:03 +0000)
structure that is now in all threads' Tls.

CHANGES.RUNTIME
TODO
configure.ad
lib/silcutil/silcthread.c
lib/silcutil/silcthread.h
lib/silcutil/symbian/silcsymbianthread.cpp
lib/silcutil/tests/test_silcthread.c
lib/silcutil/unix/silcunixthread.c
lib/silcutil/win32/silcwin32thread.c

index 9d7a6ffc0b9ccae3bb3db62682fdc41af07c6dcd..119de24d2a52f5583cb084df051196f0f93b8b66 100644 (file)
@@ -1,3 +1,10 @@
+Sat Dec 15 19:59:53 EET 2007  Pekka Riikonen <priikone@silcnet.org>
+
+       * 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 <priikone@silcnet.org>
 
        * Clear the locked flag before unlocking mutex, not after.
diff --git a/TODO b/TODO
index be0f0e5f20d3bc11d5b37a07bc46ef4f7171af94..20ff6e95956a77a905161ecdddcb11fb33143e0c 100644 (file)
--- 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.
index bb21ee683ed5f8b8f9004470c06821ebad9d258e..0e53cfa9d44d7b26b45f2869eea5c7f6e4945dff 100644 (file)
@@ -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,
index 794259c78dc11eab00bd71d39679053c0dabc988..3ada012c14e86c3c264c0f275a0476fbd1f2d523 100644 (file)
@@ -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;
+}
index c439b38db611f64f1f85ba643b1b3e53f953fd59..5afc0a03201cced36b4b3cd48fd75fcb81c830b8 100644 (file)
@@ -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
index 10d7142ac06ae2eeeb669bf1cba78ea117f486c3..05a2e893b294a22ed8b72e20ad838e0dc35dda49 100644 (file)
@@ -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" */
index d59ed4e043066bea2b2a6e3e131e8def680aa8db..19315701ad8697860d1ada1551d8aa1fbd9ea3b6 100644 (file)
@@ -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);
 }
 
index 2415a380e31eb3bb21509ff4be74b9c58ce07d2b..9ffb365c471e560d52d55fa968759b9cf476e713 100644 (file)
 
 /**************************** 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
index 15b1ca83d98424a86cb41ec949a4c1a5abd0c1c1..cdcd50c0df9a0b32ee87af27387f3beead8e2660 100644 (file)
@@ -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 */