Provide a libsmbclient interface for programs requiring threads
[ira/wip.git] / lib / util / smb_threads.c
1 /*
2    Unix SMB/CIFS implementation.
3    SMB client library implementation (thread interface functions).
4    Copyright (C) Jeremy Allison, 2009.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21  * This code is based in the ideas in openssl
22  * but somewhat simpler and expended to include
23  * thread local storage.
24  */
25
26 #include "includes.h"
27 #include "smb_threads.h"
28
29 /*********************************************************
30  Functions to vector the locking primitives used internally
31  by libsmbclient.
32 *********************************************************/
33
34 const struct smb_thread_functions *global_tfp;
35
36 /*********************************************************
37  Dynamic lock array.
38 *********************************************************/
39
40 void **global_lock_array;
41
42 /*********************************************************
43  Mutex used for our internal "once" function
44 *********************************************************/
45
46 void *once_mutex = NULL;
47
48
49 /*********************************************************
50  Function to set the locking primitives used by libsmbclient.
51 *********************************************************/
52
53 int smb_thread_set_functions(const struct smb_thread_functions *tf)
54 {
55         int i;
56
57         global_tfp = tf;
58
59 #if defined(PARANOID_MALLOC_CHECKER)
60 #ifdef malloc
61 #undef malloc
62 #endif
63 #endif
64
65         /* Here we initialize any static locks we're using. */
66         global_lock_array = (void **)malloc(sizeof(void *) *NUM_GLOBAL_LOCKS);
67
68 #if defined(PARANOID_MALLOC_CHECKER)
69 #define malloc(s) __ERROR_DONT_USE_MALLOC_DIRECTLY
70 #endif
71
72         if (global_lock_array == NULL) {
73                 return ENOMEM;
74         }
75
76         for (i = 0; i < NUM_GLOBAL_LOCKS; i++) {
77                 char *name = NULL;
78                 if (asprintf(&name, "global_lock_%d", i) == -1) {
79                         SAFE_FREE(global_lock_array);
80                         return ENOMEM;
81                 }
82                 if (global_tfp->create_mutex(name,
83                                 &global_lock_array[i],
84                                 __location__)) {
85                         smb_panic("smb_thread_set_functions: create mutexes failed");
86                 }
87                 SAFE_FREE(name);
88         }
89
90         /* Create the mutex we'll use for our "once" function */
91         if (SMB_THREAD_CREATE_MUTEX("smb_once", once_mutex) != 0) {
92                 smb_panic("smb_thread_set_functions: failed to create 'once' mutex");
93         }
94
95         return 0;
96 }
97
98 /*******************************************************************
99  Call a function only once. We implement this ourselves
100  using our own mutex rather than using the thread implementation's
101  *_once() function because each implementation has its own
102  type for the variable which keeps track of whether the function
103  has been called, and there's no easy way to allocate the correct
104  size variable in code internal to Samba without knowing the
105  implementation's "once" type.
106 ********************************************************************/
107 void smb_thread_once(smb_thread_once_t *ponce, void (*init_fn)(void))
108 {
109         int ret;
110         int need_func_call;
111
112         /* Lock our "once" mutex in order to test and initialize ponce */
113         if ((ret = SMB_THREAD_LOCK(once_mutex, SMB_THREAD_LOCK)) != 0) {
114                 DEBUG(0, ("error locking 'once': %d\n", ret));
115         }
116
117         /* Store whether we're going to need to issue the function call */
118         need_func_call = ! *ponce;
119
120         /*
121          * See if another thread got here after we tested it initially but
122          * before we got our lock.
123          */
124         if (need_func_call) {
125                 /*
126                  * Nope, we still need to issue the call. Set the "once"
127                  * variable to true now so we can unlock the mutex. (We don't
128                  * want to leave it locked during the call to the
129                  * initialization function in case there's yet another "once"
130                  * function needed to be called from therein.)
131                  */
132                 *ponce = true;
133         }
134
135         /* Unlock the mutex */
136         if ((ret = SMB_THREAD_LOCK(once_mutex, SMB_THREAD_UNLOCK)) != 0) {
137                 DEBUG(0, ("error unlocking 'once': %d\n", ret));
138         }
139
140         /* Finally, if we need to call the user-provided function, ... */
141         if (need_func_call) {
142                 /* ... then do so now. */
143                 (*init_fn)();
144         }
145 }
146
147
148 #if 0
149 /* Test. - pthread implementations. */
150 #include <pthread.h>
151
152 #ifdef malloc
153 #undef malloc
154 #endif
155
156 SMB_THREADS_DEF_PTHREAD_IMPLEMENTATION(tf);
157
158 static smb_thread_once_t ot = SMB_THREAD_ONCE_INIT;
159 void *pkey = NULL;
160
161 static void init_fn(void)
162 {
163         int ret;
164
165         if (!global_tfp) {
166                 /* Non-thread safe init case. */
167                 if (ot) {
168                         return;
169                 }
170                 ot = true;
171         }
172
173         if ((ret = SMB_THREAD_CREATE_TLS("test_tls", pkey)) != 0) {
174                 printf("Create tls once error: %d\n", ret);
175         }
176 }
177
178 /* Test function. */
179 int test_threads(void)
180 {
181         int ret;
182         void *plock = NULL;
183         smb_thread_set_functions(&tf);
184
185         SMB_THREAD_ONCE(&ot, init_fn);
186
187         if ((ret = SMB_THREAD_CREATE_MUTEX("test", plock)) != 0) {
188                 printf("Create lock error: %d\n", ret);
189         }
190         if ((ret = SMB_THREAD_LOCK(plock, SMB_THREAD_LOCK)) != 0) {
191                 printf("lock error: %d\n", ret);
192         }
193         if ((ret = SMB_THREAD_LOCK(plock, SMB_THREAD_UNLOCK)) != 0) {
194                 printf("unlock error: %d\n", ret);
195         }
196         SMB_THREAD_DESTROY_MUTEX(plock);
197         SMB_THREAD_DESTROY_TLS(pkey);
198
199         return 0;
200 }
201 #endif