Fix broken smb_thread_once function
[sfrench/samba-autobuild/.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
108 int smb_thread_once(smb_thread_once_t *ponce, void (*init_fn)(void))
109 {
110         int ret;
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                 smb_panic("error locking 'once'");
115         }
116
117         /*
118          * See if another thread got here after we tested it initially but
119          * before we got our lock.
120          */
121         if (! *ponce) {
122                 /* Nope, we need to run the initialization function */
123                 (*init_fn)();
124         }
125
126         /* Unlock the mutex */
127         if ((ret = SMB_THREAD_LOCK(once_mutex, SMB_THREAD_UNLOCK)) != 0) {
128                 smb_panic("error unlocking 'once'");
129         }
130
131         return 0;
132 }
133
134
135 #if 0
136 /* Test. - pthread implementations. */
137 #include <pthread.h>
138
139 #ifdef malloc
140 #undef malloc
141 #endif
142
143 SMB_THREADS_DEF_PTHREAD_IMPLEMENTATION(tf);
144
145 static smb_thread_once_t ot = SMB_THREAD_ONCE_INIT;
146 void *pkey = NULL;
147
148 static void init_fn(void)
149 {
150         int ret;
151
152         if (!global_tfp) {
153                 /* Non-thread safe init case. */
154                 if (ot) {
155                         return;
156                 }
157                 ot = true;
158         }
159
160         if ((ret = SMB_THREAD_CREATE_TLS("test_tls", pkey)) != 0) {
161                 printf("Create tls once error: %d\n", ret);
162         }
163 }
164
165 /* Test function. */
166 int test_threads(void)
167 {
168         int ret;
169         void *plock = NULL;
170         smb_thread_set_functions(&tf);
171
172         SMB_THREAD_ONCE(&ot, init_fn);
173
174         if ((ret = SMB_THREAD_CREATE_MUTEX("test", plock)) != 0) {
175                 printf("Create lock error: %d\n", ret);
176         }
177         if ((ret = SMB_THREAD_LOCK(plock, SMB_THREAD_LOCK)) != 0) {
178                 printf("lock error: %d\n", ret);
179         }
180         if ((ret = SMB_THREAD_LOCK(plock, SMB_THREAD_UNLOCK)) != 0) {
181                 printf("unlock error: %d\n", ret);
182         }
183         SMB_THREAD_DESTROY_MUTEX(plock);
184         SMB_THREAD_DESTROY_TLS(pkey);
185
186         return 0;
187 }
188 #endif