util: added TYPESAFE_QSORT() macro
[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 static 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,
109                     void (*init_fn)(void *pdata),
110                     void *pdata)
111 {
112         int ret;
113
114         /* Lock our "once" mutex in order to test and initialize ponce */
115         if (SMB_THREAD_LOCK(once_mutex) != 0) {
116                 smb_panic("error locking 'once'");
117         }
118
119         /* Keep track of whether we ran their init function */
120         ret = ! *ponce;
121
122         /*
123          * See if another thread got here after we tested it initially but
124          * before we got our lock.
125          */
126         if (! *ponce) {
127                 /* Nope, we need to run the initialization function */
128                 (*init_fn)(pdata);
129
130                 /* Now we can indicate that the function has been run */
131                 *ponce = true;
132         }
133
134         /* Unlock the mutex */
135         if (SMB_THREAD_UNLOCK(once_mutex) != 0) {
136                 smb_panic("error unlocking 'once'");
137         }
138         
139         /*
140          * Tell 'em whether we ran their init function. If they passed a data
141          * pointer to the init function and the init function could change
142          * something in the pointed-to data, this will tell them whether that
143          * data is valid or not.
144          */
145         return ret;
146 }
147
148
149 #if 0
150 /* Test. - pthread implementations. */
151 #include <pthread.h>
152
153 #ifdef malloc
154 #undef malloc
155 #endif
156
157 SMB_THREADS_DEF_PTHREAD_IMPLEMENTATION(tf);
158
159 static smb_thread_once_t ot = SMB_THREAD_ONCE_INIT;
160 void *pkey = NULL;
161
162 static void init_fn(void)
163 {
164         int ret;
165
166         if (!global_tfp) {
167                 /* Non-thread safe init case. */
168                 if (ot) {
169                         return;
170                 }
171                 ot = true;
172         }
173
174         if ((ret = SMB_THREAD_CREATE_TLS("test_tls", pkey)) != 0) {
175                 printf("Create tls once error: %d\n", ret);
176         }
177 }
178
179 /* Test function. */
180 int test_threads(void)
181 {
182         int ret;
183         void *plock = NULL;
184         smb_thread_set_functions(&tf);
185
186         SMB_THREAD_ONCE(&ot, init_fn);
187
188         if ((ret = SMB_THREAD_CREATE_MUTEX("test", plock)) != 0) {
189                 printf("Create lock error: %d\n", ret);
190         }
191         if ((ret = SMB_THREAD_LOCK(plock, SMB_THREAD_LOCK)) != 0) {
192                 printf("lock error: %d\n", ret);
193         }
194         if ((ret = SMB_THREAD_LOCK(plock, SMB_THREAD_UNLOCK)) != 0) {
195                 printf("unlock error: %d\n", ret);
196         }
197         SMB_THREAD_DESTROY_MUTEX(plock);
198         SMB_THREAD_DESTROY_TLS(pkey);
199
200         return 0;
201 }
202 #endif