Replace external thread "once" with an internal implementation
authorDerrell Lipman <derrell@dworkin.(none)>
Mon, 11 May 2009 01:55:23 +0000 (21:55 -0400)
committerDerrell Lipman <derrell@dworkin.(none)>
Mon, 11 May 2009 02:45:12 +0000 (22:45 -0400)
Jeremy, please check...

- I'm in the process of providing an interface in libsmbclient to the
  recently-added threading capabilities. In the process, I discovered that
  different thread implementations have varying types for the variable passed
  to the thread_impl_once() function. pthreads, for example, uses type
  pthread_once_t. Since Samba needs to internally declare these variables, it
  would need to know the exact type required by each thread implementation's
  function. After considering multiple methods of obtaining an appropriately
  sized variable, I decided that for the basic "once" functionality required
  by Samba, it would be much simpler to just implement our own "once"
  functionality. We don't require cancellation points et all. This commit adds
  an smb_thread_once() function that is implemented using an internal
  mutex. The mutex itself uses the implementation's create_mutex
  function. This eliminates the need for the user to provide a smb_thread_once
  function pointer and the entire issue of that function's first parameter.

Derrell

lib/util/smb_threads.c
lib/util/smb_threads.h
lib/util/smb_threads_internal.h

index 1998211d40467a8335e57731a9b370008fb696d4..8cddbf01762df98b167ba1cf73d22b4142fcf0ac 100644 (file)
@@ -39,6 +39,13 @@ const struct smb_thread_functions *global_tfp;
 
 void **global_lock_array;
 
+/*********************************************************
+ Mutex used for our internal "once" function
+*********************************************************/
+
+void *once_mutex = NULL;
+
+
 /*********************************************************
  Function to set the locking primitives used by libsmbclient.
 *********************************************************/
@@ -80,9 +87,64 @@ int smb_thread_set_functions(const struct smb_thread_functions *tf)
                SAFE_FREE(name);
        }
 
+        /* Create the mutex we'll use for our "once" function */
+       if (SMB_THREAD_CREATE_MUTEX("smb_once", once_mutex) != 0) {
+               smb_panic("smb_thread_set_functions: failed to create 'once' mutex");
+       }
+
        return 0;
 }
 
+/*******************************************************************
+ Call a function only once. We implement this ourselves
+ using our own mutex rather than using the thread implementation's
+ *_once() function because each implementation has its own
+ type for the variable which keeps track of whether the function
+ has been called, and there's no easy way to allocate the correct
+ size variable in code internal to Samba without knowing the
+ implementation's "once" type.
+********************************************************************/
+void smb_thread_once(smb_thread_once_t *ponce, void (*init_fn)(void))
+{
+        int ret;
+        int need_func_call;
+
+        /* Lock our "once" mutex in order to test and initialize ponce */
+       if ((ret = SMB_THREAD_LOCK(once_mutex, SMB_THREAD_LOCK)) != 0) {
+               DEBUG(0, ("error locking 'once': %d\n", ret));
+       }
+
+        /* Store whether we're going to need to issue the function call */
+        need_func_call = ! *ponce;
+
+        /*
+         * See if another thread got here after we tested it initially but
+         * before we got our lock.
+         */
+        if (need_func_call) {
+                /*
+                 * Nope, we still need to issue the call. Set the "once"
+                 * variable to true now so we can unlock the mutex. (We don't
+                 * want to leave it locked during the call to the
+                 * initialization function in case there's yet another "once"
+                 * function needed to be called from therein.)
+                 */
+                *ponce = true;
+        }
+
+        /* Unlock the mutex */
+       if ((ret = SMB_THREAD_LOCK(once_mutex, SMB_THREAD_UNLOCK)) != 0) {
+               DEBUG(0, ("error unlocking 'once': %d\n", ret));
+       }
+
+        /* Finally, if we need to call the user-provided function, ... */
+        if (need_func_call) {
+                /* ... then do so now. */
+                (*init_fn)();
+        }
+}
+
+
 #if 0
 /* Test. - pthread implementations. */
 #include <pthread.h>
@@ -128,7 +190,7 @@ int test_threads(void)
        if ((ret = SMB_THREAD_LOCK(plock, SMB_THREAD_LOCK)) != 0) {
                printf("lock error: %d\n", ret);
        }
-       if ((SMB_THREAD_LOCK(plock, SMB_THREAD_UNLOCK)) != 0) {
+       if ((ret = SMB_THREAD_LOCK(plock, SMB_THREAD_UNLOCK)) != 0) {
                printf("unlock error: %d\n", ret);
        }
        SMB_THREAD_DESTROY_MUTEX(plock);
index 4443c3eae43b30dd2bc64aeb86bd843524dad638..f1a39a6e281894655951dfcb55fe585c1962776c 100644 (file)
 #ifndef _smb_threads_h_
 #define _smb_threads_h_
 
-/* Data types needed for smb_thread_once call. */
-
-#if defined(HAVE_PTHREAD_H)
-#include <pthread.h>
-#define smb_thread_once_t pthread_once_t
-#define SMB_THREAD_ONCE_INIT PTHREAD_ONCE_INIT
-#define SMB_THREAD_ONCE_IS_INITIALIZED(val) (true)
-#define SMB_THREAD_ONCE_INITIALIZE(val)
-#else
-#define smb_thread_once_t bool
+typedef bool smb_thread_once_t;
 #define SMB_THREAD_ONCE_INIT false
 #define SMB_THREAD_ONCE_IS_INITIALIZED(val) ((val) == true)
 #define SMB_THREAD_ONCE_INITIALIZE(val) ((val) = true)
-#endif
 
 enum smb_thread_lock_type {
        SMB_THREAD_LOCK = 1,
@@ -50,9 +40,6 @@ struct smb_thread_functions {
        int (*lock_mutex)(void *plock, enum smb_thread_lock_type lock_type,
                        const char *location);
 
-       /* Once initialization. */
-       int (*smb_thread_once)(smb_thread_once_t *p_once, void (*init_fn)(void));
-
        /* Thread local storage. */
        int (*create_tls)(const char *keyname,
                        void **ppkey,
@@ -64,6 +51,7 @@ struct smb_thread_functions {
 };
 
 int smb_thread_set_functions(const struct smb_thread_functions *tf);
+void smb_thread_once(smb_thread_once_t *ponce, void (*init_fn)(void));
 
 extern const struct smb_thread_functions *global_tfp;
 
@@ -97,11 +85,6 @@ static int smb_lock_pthread(void *plock, enum smb_thread_lock_type lock_type, co
        } \
 } \
  \
-static int smb_thread_once_pthread(smb_thread_once_t *p_once, void (*init_fn)(void)) \
-{ \
-       return pthread_once(p_once, init_fn); \
-} \
- \
 static int smb_create_tls_pthread(const char *keyname, void **ppkey, const char *location) \
 { \
        int ret; \
@@ -142,7 +125,6 @@ static const struct smb_thread_functions (tf) = { \
                        smb_create_mutex_pthread, \
                        smb_destroy_mutex_pthread, \
                        smb_lock_pthread, \
-                       smb_thread_once_pthread, \
                        smb_create_tls_pthread, \
                        smb_destroy_tls_pthread, \
                        smb_set_tls_pthread, \
index b7e862af72d8325e66f161ddea7a0bf47627dc03..0260934e158b6e1e76b985febb56b29628053f04 100644 (file)
@@ -34,7 +34,7 @@
        (global_tfp ? global_tfp->lock_mutex((plock), (type), __location__) : 0)
 
 #define SMB_THREAD_ONCE(ponce, init_fn) \
-       (global_tfp ? global_tfp->smb_thread_once((ponce), (init_fn)) : ((init_fn()), 0))
+       (global_tfp ? smb_thread_once((ponce), (init_fn)) : ((init_fn()), 0))
 
 #define SMB_THREAD_CREATE_TLS(keyname, key) \
        (global_tfp ? global_tfp->create_tls((keyname), &(key), __location__) : 0)