1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | Thread Safe Resource Manager | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1999-2011, Andi Gutmans, Sascha Schumann, Zeev Suraski | |
6 | | This source file is subject to the TSRM license, that is bundled | |
7 | | with this package in the file LICENSE | |
8 | +----------------------------------------------------------------------+ |
9 | | Authors: Zeev Suraski <zeev@zend.com> | |
10 | +----------------------------------------------------------------------+ |
11 | */ |
12 | |
13 | #include "TSRM.h" |
14 | |
15 | #ifdef ZTS |
16 | |
17 | #include <stdio.h> |
18 | |
19 | #if HAVE_STDARG_H |
20 | #include <stdarg.h> |
21 | #endif |
22 | |
23 | typedef struct _tsrm_tls_entry tsrm_tls_entry; |
24 | |
25 | struct _tsrm_tls_entry { |
26 | void **storage; |
27 | int count; |
28 | THREAD_T thread_id; |
29 | tsrm_tls_entry *next; |
30 | }; |
31 | |
32 | |
33 | typedef struct { |
34 | size_t size; |
35 | ts_allocate_ctor ctor; |
36 | ts_allocate_dtor dtor; |
37 | int done; |
38 | } tsrm_resource_type; |
39 | |
40 | |
41 | /* The memory manager table */ |
42 | static tsrm_tls_entry **tsrm_tls_table=NULL; |
43 | static int tsrm_tls_table_size; |
44 | static ts_rsrc_id id_count; |
45 | |
46 | /* The resource sizes table */ |
47 | static tsrm_resource_type *resource_types_table=NULL; |
48 | static int resource_types_table_size; |
49 | |
50 | |
51 | static MUTEX_T tsmm_mutex; /* thread-safe memory manager mutex */ |
52 | |
53 | /* New thread handlers */ |
54 | static tsrm_thread_begin_func_t tsrm_new_thread_begin_handler; |
55 | static tsrm_thread_end_func_t tsrm_new_thread_end_handler; |
56 | |
57 | /* Debug support */ |
58 | int tsrm_error(int level, const char *format, ...); |
59 | |
60 | /* Read a resource from a thread's resource storage */ |
61 | static int tsrm_error_level; |
62 | static FILE *tsrm_error_file; |
63 | |
64 | #if TSRM_DEBUG |
65 | #define TSRM_ERROR(args) tsrm_error args |
66 | #define TSRM_SAFE_RETURN_RSRC(array, offset, range) \ |
67 | { \ |
68 | int unshuffled_offset = TSRM_UNSHUFFLE_RSRC_ID(offset); \ |
69 | \ |
70 | if (offset==0) { \ |
71 | return &array; \ |
72 | } else if ((unshuffled_offset)>=0 && (unshuffled_offset)<(range)) { \ |
73 | TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Successfully fetched resource id %d for thread id %ld - 0x%0.8X", \ |
74 | unshuffled_offset, (long) thread_resources->thread_id, array[unshuffled_offset])); \ |
75 | return array[unshuffled_offset]; \ |
76 | } else { \ |
77 | TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Resource id %d is out of range (%d..%d)", \ |
78 | unshuffled_offset, TSRM_SHUFFLE_RSRC_ID(0), TSRM_SHUFFLE_RSRC_ID(thread_resources->count-1))); \ |
79 | return NULL; \ |
80 | } \ |
81 | } |
82 | #else |
83 | #define TSRM_ERROR(args) |
84 | #define TSRM_SAFE_RETURN_RSRC(array, offset, range) \ |
85 | if (offset==0) { \ |
86 | return &array; \ |
87 | } else { \ |
88 | return array[TSRM_UNSHUFFLE_RSRC_ID(offset)]; \ |
89 | } |
90 | #endif |
91 | |
92 | #if defined(PTHREADS) |
93 | /* Thread local storage */ |
94 | static pthread_key_t tls_key; |
95 | # define tsrm_tls_set(what) pthread_setspecific(tls_key, (void*)(what)) |
96 | # define tsrm_tls_get() pthread_getspecific(tls_key) |
97 | |
98 | #elif defined(TSRM_ST) |
99 | static int tls_key; |
100 | # define tsrm_tls_set(what) st_thread_setspecific(tls_key, (void*)(what)) |
101 | # define tsrm_tls_get() st_thread_getspecific(tls_key) |
102 | |
103 | #elif defined(TSRM_WIN32) |
104 | static DWORD tls_key; |
105 | # define tsrm_tls_set(what) TlsSetValue(tls_key, (void*)(what)) |
106 | # define tsrm_tls_get() TlsGetValue(tls_key) |
107 | |
108 | #elif defined(BETHREADS) |
109 | static int32 tls_key; |
110 | # define tsrm_tls_set(what) tls_set(tls_key, (void*)(what)) |
111 | # define tsrm_tls_get() (tsrm_tls_entry*)tls_get(tls_key) |
112 | |
113 | #else |
114 | # define tsrm_tls_set(what) |
115 | # define tsrm_tls_get() NULL |
116 | # warning tsrm_set_interpreter_context is probably broken on this platform |
117 | #endif |
118 | |
119 | /* Startup TSRM (call once for the entire process) */ |
120 | TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename) |
121 | { |
122 | #if defined(GNUPTH) |
123 | pth_init(); |
124 | #elif defined(PTHREADS) |
125 | pthread_key_create( &tls_key, 0 ); |
126 | #elif defined(TSRM_ST) |
127 | st_init(); |
128 | st_key_create(&tls_key, 0); |
129 | #elif defined(TSRM_WIN32) |
130 | tls_key = TlsAlloc(); |
131 | #elif defined(BETHREADS) |
132 | tls_key = tls_allocate(); |
133 | #endif |
134 | |
135 | tsrm_error_file = stderr; |
136 | tsrm_error_set(debug_level, debug_filename); |
137 | tsrm_tls_table_size = expected_threads; |
138 | |
139 | tsrm_tls_table = (tsrm_tls_entry **) calloc(tsrm_tls_table_size, sizeof(tsrm_tls_entry *)); |
140 | if (!tsrm_tls_table) { |
141 | TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate TLS table" )); |
142 | return 0; |
143 | } |
144 | id_count=0; |
145 | |
146 | resource_types_table_size = expected_resources; |
147 | resource_types_table = (tsrm_resource_type *) calloc(resource_types_table_size, sizeof(tsrm_resource_type)); |
148 | if (!resource_types_table) { |
149 | TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate resource types table" )); |
150 | free(tsrm_tls_table); |
151 | tsrm_tls_table = NULL; |
152 | return 0; |
153 | } |
154 | |
155 | tsmm_mutex = tsrm_mutex_alloc(); |
156 | |
157 | tsrm_new_thread_begin_handler = tsrm_new_thread_end_handler = NULL; |
158 | |
159 | TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Started up TSRM, %d expected threads, %d expected resources" , expected_threads, expected_resources)); |
160 | return 1; |
161 | } |
162 | |
163 | |
164 | /* Shutdown TSRM (call once for the entire process) */ |
165 | TSRM_API void tsrm_shutdown(void) |
166 | { |
167 | int i; |
168 | |
169 | if (tsrm_tls_table) { |
170 | for (i=0; i<tsrm_tls_table_size; i++) { |
171 | tsrm_tls_entry *p = tsrm_tls_table[i], *next_p; |
172 | |
173 | while (p) { |
174 | int j; |
175 | |
176 | next_p = p->next; |
177 | for (j=0; j<p->count; j++) { |
178 | if (p->storage[j]) { |
179 | if (resource_types_table && !resource_types_table[j].done && resource_types_table[j].dtor) { |
180 | resource_types_table[j].dtor(p->storage[j], &p->storage); |
181 | } |
182 | free(p->storage[j]); |
183 | } |
184 | } |
185 | free(p->storage); |
186 | free(p); |
187 | p = next_p; |
188 | } |
189 | } |
190 | free(tsrm_tls_table); |
191 | tsrm_tls_table = NULL; |
192 | } |
193 | if (resource_types_table) { |
194 | free(resource_types_table); |
195 | resource_types_table=NULL; |
196 | } |
197 | tsrm_mutex_free(tsmm_mutex); |
198 | tsmm_mutex = NULL; |
199 | TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Shutdown TSRM" )); |
200 | if (tsrm_error_file!=stderr) { |
201 | fclose(tsrm_error_file); |
202 | } |
203 | #if defined(GNUPTH) |
204 | pth_kill(); |
205 | #elif defined(PTHREADS) |
206 | pthread_setspecific(tls_key, 0); |
207 | pthread_key_delete(tls_key); |
208 | #elif defined(TSRM_WIN32) |
209 | TlsFree(tls_key); |
210 | #endif |
211 | } |
212 | |
213 | |
214 | /* allocates a new thread-safe-resource id */ |
215 | TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) |
216 | { |
217 | int i; |
218 | |
219 | TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes" , size)); |
220 | |
221 | tsrm_mutex_lock(tsmm_mutex); |
222 | |
223 | /* obtain a resource id */ |
224 | *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++); |
225 | TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d" , *rsrc_id)); |
226 | |
227 | /* store the new resource type in the resource sizes table */ |
228 | if (resource_types_table_size < id_count) { |
229 | resource_types_table = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count); |
230 | if (!resource_types_table) { |
231 | tsrm_mutex_unlock(tsmm_mutex); |
232 | TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource" )); |
233 | *rsrc_id = 0; |
234 | return 0; |
235 | } |
236 | resource_types_table_size = id_count; |
237 | } |
238 | resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size; |
239 | resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor; |
240 | resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor; |
241 | resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0; |
242 | |
243 | /* enlarge the arrays for the already active threads */ |
244 | for (i=0; i<tsrm_tls_table_size; i++) { |
245 | tsrm_tls_entry *p = tsrm_tls_table[i]; |
246 | |
247 | while (p) { |
248 | if (p->count < id_count) { |
249 | int j; |
250 | |
251 | p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count); |
252 | for (j=p->count; j<id_count; j++) { |
253 | p->storage[j] = (void *) malloc(resource_types_table[j].size); |
254 | if (resource_types_table[j].ctor) { |
255 | resource_types_table[j].ctor(p->storage[j], &p->storage); |
256 | } |
257 | } |
258 | p->count = id_count; |
259 | } |
260 | p = p->next; |
261 | } |
262 | } |
263 | tsrm_mutex_unlock(tsmm_mutex); |
264 | |
265 | TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d" , *rsrc_id)); |
266 | return *rsrc_id; |
267 | } |
268 | |
269 | |
270 | static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id) |
271 | { |
272 | int i; |
273 | |
274 | TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Creating data structures for thread %x" , thread_id)); |
275 | (*thread_resources_ptr) = (tsrm_tls_entry *) malloc(sizeof(tsrm_tls_entry)); |
276 | (*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count); |
277 | (*thread_resources_ptr)->count = id_count; |
278 | (*thread_resources_ptr)->thread_id = thread_id; |
279 | (*thread_resources_ptr)->next = NULL; |
280 | |
281 | /* Set thread local storage to this new thread resources structure */ |
282 | tsrm_tls_set(*thread_resources_ptr); |
283 | |
284 | if (tsrm_new_thread_begin_handler) { |
285 | tsrm_new_thread_begin_handler(thread_id, &((*thread_resources_ptr)->storage)); |
286 | } |
287 | for (i=0; i<id_count; i++) { |
288 | if (resource_types_table[i].done) { |
289 | (*thread_resources_ptr)->storage[i] = NULL; |
290 | } else |
291 | { |
292 | (*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size); |
293 | if (resource_types_table[i].ctor) { |
294 | resource_types_table[i].ctor((*thread_resources_ptr)->storage[i], &(*thread_resources_ptr)->storage); |
295 | } |
296 | } |
297 | } |
298 | |
299 | if (tsrm_new_thread_end_handler) { |
300 | tsrm_new_thread_end_handler(thread_id, &((*thread_resources_ptr)->storage)); |
301 | } |
302 | |
303 | tsrm_mutex_unlock(tsmm_mutex); |
304 | } |
305 | |
306 | |
307 | /* fetches the requested resource for the current thread */ |
308 | TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) |
309 | { |
310 | THREAD_T thread_id; |
311 | int hash_value; |
312 | tsrm_tls_entry *thread_resources; |
313 | |
314 | #ifdef NETWARE |
315 | /* The below if loop is added for NetWare to fix an abend while unloading PHP |
316 | * when an Apache unload command is issued on the system console. |
317 | * While exiting from PHP, at the end for some reason, this function is called |
318 | * with tsrm_tls_table = NULL. When this happened, the server abends when |
319 | * tsrm_tls_table is accessed since it is NULL. |
320 | */ |
321 | if(tsrm_tls_table) { |
322 | #endif |
323 | if (!th_id) { |
324 | /* Fast path for looking up the resources for the current |
325 | * thread. Its used by just about every call to |
326 | * ts_resource_ex(). This avoids the need for a mutex lock |
327 | * and our hashtable lookup. |
328 | */ |
329 | thread_resources = tsrm_tls_get(); |
330 | |
331 | if (thread_resources) { |
332 | TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for current thread %d" , id, (long) thread_resources->thread_id)); |
333 | /* Read a specific resource from the thread's resources. |
334 | * This is called outside of a mutex, so have to be aware about external |
335 | * changes to the structure as we read it. |
336 | */ |
337 | TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count); |
338 | } |
339 | thread_id = tsrm_thread_id(); |
340 | } else { |
341 | thread_id = *th_id; |
342 | } |
343 | |
344 | TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for thread %ld" , id, (long) thread_id)); |
345 | tsrm_mutex_lock(tsmm_mutex); |
346 | |
347 | hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size); |
348 | thread_resources = tsrm_tls_table[hash_value]; |
349 | |
350 | if (!thread_resources) { |
351 | allocate_new_resource(&tsrm_tls_table[hash_value], thread_id); |
352 | return ts_resource_ex(id, &thread_id); |
353 | } else { |
354 | do { |
355 | if (thread_resources->thread_id == thread_id) { |
356 | break; |
357 | } |
358 | if (thread_resources->next) { |
359 | thread_resources = thread_resources->next; |
360 | } else { |
361 | allocate_new_resource(&thread_resources->next, thread_id); |
362 | return ts_resource_ex(id, &thread_id); |
363 | /* |
364 | * thread_resources = thread_resources->next; |
365 | * break; |
366 | */ |
367 | } |
368 | } while (thread_resources); |
369 | } |
370 | tsrm_mutex_unlock(tsmm_mutex); |
371 | /* Read a specific resource from the thread's resources. |
372 | * This is called outside of a mutex, so have to be aware about external |
373 | * changes to the structure as we read it. |
374 | */ |
375 | TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count); |
376 | #ifdef NETWARE |
377 | } /* if(tsrm_tls_table) */ |
378 | #endif |
379 | } |
380 | |
381 | /* frees an interpreter context. You are responsible for making sure that |
382 | * it is not linked into the TSRM hash, and not marked as the current interpreter */ |
383 | void tsrm_free_interpreter_context(void *context) |
384 | { |
385 | tsrm_tls_entry *next, *thread_resources = (tsrm_tls_entry*)context; |
386 | int i; |
387 | |
388 | while (thread_resources) { |
389 | next = thread_resources->next; |
390 | |
391 | for (i=0; i<thread_resources->count; i++) { |
392 | if (resource_types_table[i].dtor) { |
393 | resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage); |
394 | } |
395 | } |
396 | for (i=0; i<thread_resources->count; i++) { |
397 | free(thread_resources->storage[i]); |
398 | } |
399 | free(thread_resources->storage); |
400 | free(thread_resources); |
401 | thread_resources = next; |
402 | } |
403 | } |
404 | |
405 | void *tsrm_set_interpreter_context(void *new_ctx) |
406 | { |
407 | tsrm_tls_entry *current; |
408 | |
409 | current = tsrm_tls_get(); |
410 | |
411 | /* TODO: unlink current from the global linked list, and replace it |
412 | * it with the new context, protected by mutex where/if appropriate */ |
413 | |
414 | /* Set thread local storage to this new thread resources structure */ |
415 | tsrm_tls_set(new_ctx); |
416 | |
417 | /* return old context, so caller can restore it when they're done */ |
418 | return current; |
419 | } |
420 | |
421 | |
422 | /* allocates a new interpreter context */ |
423 | void *tsrm_new_interpreter_context(void) |
424 | { |
425 | tsrm_tls_entry *new_ctx, *current; |
426 | THREAD_T thread_id; |
427 | |
428 | thread_id = tsrm_thread_id(); |
429 | tsrm_mutex_lock(tsmm_mutex); |
430 | |
431 | current = tsrm_tls_get(); |
432 | |
433 | allocate_new_resource(&new_ctx, thread_id); |
434 | |
435 | /* switch back to the context that was in use prior to our creation |
436 | * of the new one */ |
437 | return tsrm_set_interpreter_context(current); |
438 | } |
439 | |
440 | |
441 | /* frees all resources allocated for the current thread */ |
442 | void ts_free_thread(void) |
443 | { |
444 | tsrm_tls_entry *thread_resources; |
445 | int i; |
446 | THREAD_T thread_id = tsrm_thread_id(); |
447 | int hash_value; |
448 | tsrm_tls_entry *last=NULL; |
449 | |
450 | tsrm_mutex_lock(tsmm_mutex); |
451 | hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size); |
452 | thread_resources = tsrm_tls_table[hash_value]; |
453 | |
454 | while (thread_resources) { |
455 | if (thread_resources->thread_id == thread_id) { |
456 | for (i=0; i<thread_resources->count; i++) { |
457 | if (resource_types_table[i].dtor) { |
458 | resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage); |
459 | } |
460 | } |
461 | for (i=0; i<thread_resources->count; i++) { |
462 | free(thread_resources->storage[i]); |
463 | } |
464 | free(thread_resources->storage); |
465 | if (last) { |
466 | last->next = thread_resources->next; |
467 | } else { |
468 | tsrm_tls_table[hash_value] = thread_resources->next; |
469 | } |
470 | tsrm_tls_set(0); |
471 | free(thread_resources); |
472 | break; |
473 | } |
474 | if (thread_resources->next) { |
475 | last = thread_resources; |
476 | } |
477 | thread_resources = thread_resources->next; |
478 | } |
479 | tsrm_mutex_unlock(tsmm_mutex); |
480 | } |
481 | |
482 | |
483 | /* frees all resources allocated for all threads except current */ |
484 | void ts_free_worker_threads(void) |
485 | { |
486 | tsrm_tls_entry *thread_resources; |
487 | int i; |
488 | THREAD_T thread_id = tsrm_thread_id(); |
489 | int hash_value; |
490 | tsrm_tls_entry *last=NULL; |
491 | |
492 | tsrm_mutex_lock(tsmm_mutex); |
493 | hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size); |
494 | thread_resources = tsrm_tls_table[hash_value]; |
495 | |
496 | while (thread_resources) { |
497 | if (thread_resources->thread_id != thread_id) { |
498 | for (i=0; i<thread_resources->count; i++) { |
499 | if (resource_types_table[i].dtor) { |
500 | resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage); |
501 | } |
502 | } |
503 | for (i=0; i<thread_resources->count; i++) { |
504 | free(thread_resources->storage[i]); |
505 | } |
506 | free(thread_resources->storage); |
507 | if (last) { |
508 | last->next = thread_resources->next; |
509 | } else { |
510 | tsrm_tls_table[hash_value] = thread_resources->next; |
511 | } |
512 | free(thread_resources); |
513 | if (last) { |
514 | thread_resources = last->next; |
515 | } else { |
516 | thread_resources = tsrm_tls_table[hash_value]; |
517 | } |
518 | } else { |
519 | if (thread_resources->next) { |
520 | last = thread_resources; |
521 | } |
522 | thread_resources = thread_resources->next; |
523 | } |
524 | } |
525 | tsrm_mutex_unlock(tsmm_mutex); |
526 | } |
527 | |
528 | |
529 | /* deallocates all occurrences of a given id */ |
530 | void ts_free_id(ts_rsrc_id id) |
531 | { |
532 | int i; |
533 | int j = TSRM_UNSHUFFLE_RSRC_ID(id); |
534 | |
535 | tsrm_mutex_lock(tsmm_mutex); |
536 | |
537 | TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Freeing resource id %d" , id)); |
538 | |
539 | if (tsrm_tls_table) { |
540 | for (i=0; i<tsrm_tls_table_size; i++) { |
541 | tsrm_tls_entry *p = tsrm_tls_table[i]; |
542 | |
543 | while (p) { |
544 | if (p->count > j && p->storage[j]) { |
545 | if (resource_types_table && resource_types_table[j].dtor) { |
546 | resource_types_table[j].dtor(p->storage[j], &p->storage); |
547 | } |
548 | free(p->storage[j]); |
549 | p->storage[j] = NULL; |
550 | } |
551 | p = p->next; |
552 | } |
553 | } |
554 | } |
555 | resource_types_table[j].done = 1; |
556 | |
557 | tsrm_mutex_unlock(tsmm_mutex); |
558 | |
559 | TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully freed resource id %d" , id)); |
560 | } |
561 | |
562 | |
563 | |
564 | |
565 | /* |
566 | * Utility Functions |
567 | */ |
568 | |
569 | /* Obtain the current thread id */ |
570 | TSRM_API THREAD_T tsrm_thread_id(void) |
571 | { |
572 | #ifdef TSRM_WIN32 |
573 | return GetCurrentThreadId(); |
574 | #elif defined(GNUPTH) |
575 | return pth_self(); |
576 | #elif defined(PTHREADS) |
577 | return pthread_self(); |
578 | #elif defined(NSAPI) |
579 | return systhread_current(); |
580 | #elif defined(PI3WEB) |
581 | return PIThread_getCurrent(); |
582 | #elif defined(TSRM_ST) |
583 | return st_thread_self(); |
584 | #elif defined(BETHREADS) |
585 | return find_thread(NULL); |
586 | #endif |
587 | } |
588 | |
589 | |
590 | /* Allocate a mutex */ |
591 | TSRM_API MUTEX_T tsrm_mutex_alloc(void) |
592 | { |
593 | MUTEX_T mutexp; |
594 | #ifdef TSRM_WIN32 |
595 | mutexp = malloc(sizeof(CRITICAL_SECTION)); |
596 | InitializeCriticalSection(mutexp); |
597 | #elif defined(GNUPTH) |
598 | mutexp = (MUTEX_T) malloc(sizeof(*mutexp)); |
599 | pth_mutex_init(mutexp); |
600 | #elif defined(PTHREADS) |
601 | mutexp = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); |
602 | pthread_mutex_init(mutexp,NULL); |
603 | #elif defined(NSAPI) |
604 | mutexp = crit_init(); |
605 | #elif defined(PI3WEB) |
606 | mutexp = PIPlatform_allocLocalMutex(); |
607 | #elif defined(TSRM_ST) |
608 | mutexp = st_mutex_new(); |
609 | #elif defined(BETHREADS) |
610 | mutexp = (beos_ben*)malloc(sizeof(beos_ben)); |
611 | mutexp->ben = 0; |
612 | mutexp->sem = create_sem(1, "PHP sempahore" ); |
613 | #endif |
614 | #ifdef THR_DEBUG |
615 | printf("Mutex created thread: %d\n" ,mythreadid()); |
616 | #endif |
617 | return( mutexp ); |
618 | } |
619 | |
620 | |
621 | /* Free a mutex */ |
622 | TSRM_API void tsrm_mutex_free(MUTEX_T mutexp) |
623 | { |
624 | if (mutexp) { |
625 | #ifdef TSRM_WIN32 |
626 | DeleteCriticalSection(mutexp); |
627 | free(mutexp); |
628 | #elif defined(GNUPTH) |
629 | free(mutexp); |
630 | #elif defined(PTHREADS) |
631 | pthread_mutex_destroy(mutexp); |
632 | free(mutexp); |
633 | #elif defined(NSAPI) |
634 | crit_terminate(mutexp); |
635 | #elif defined(PI3WEB) |
636 | PISync_delete(mutexp); |
637 | #elif defined(TSRM_ST) |
638 | st_mutex_destroy(mutexp); |
639 | #elif defined(BETHREADS) |
640 | delete_sem(mutexp->sem); |
641 | free(mutexp); |
642 | #endif |
643 | } |
644 | #ifdef THR_DEBUG |
645 | printf("Mutex freed thread: %d\n" ,mythreadid()); |
646 | #endif |
647 | } |
648 | |
649 | |
650 | /* |
651 | Lock a mutex. |
652 | A return value of 0 indicates success |
653 | */ |
654 | TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp) |
655 | { |
656 | TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex locked thread: %ld" , tsrm_thread_id())); |
657 | #ifdef TSRM_WIN32 |
658 | EnterCriticalSection(mutexp); |
659 | return 0; |
660 | #elif defined(GNUPTH) |
661 | if (pth_mutex_acquire(mutexp, 0, NULL)) { |
662 | return 0; |
663 | } |
664 | return -1; |
665 | #elif defined(PTHREADS) |
666 | return pthread_mutex_lock(mutexp); |
667 | #elif defined(NSAPI) |
668 | crit_enter(mutexp); |
669 | return 0; |
670 | #elif defined(PI3WEB) |
671 | return PISync_lock(mutexp); |
672 | #elif defined(TSRM_ST) |
673 | return st_mutex_lock(mutexp); |
674 | #elif defined(BETHREADS) |
675 | if (atomic_add(&mutexp->ben, 1) != 0) |
676 | return acquire_sem(mutexp->sem); |
677 | return 0; |
678 | #endif |
679 | } |
680 | |
681 | |
682 | /* |
683 | Unlock a mutex. |
684 | A return value of 0 indicates success |
685 | */ |
686 | TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp) |
687 | { |
688 | TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex unlocked thread: %ld" , tsrm_thread_id())); |
689 | #ifdef TSRM_WIN32 |
690 | LeaveCriticalSection(mutexp); |
691 | return 0; |
692 | #elif defined(GNUPTH) |
693 | if (pth_mutex_release(mutexp)) { |
694 | return 0; |
695 | } |
696 | return -1; |
697 | #elif defined(PTHREADS) |
698 | return pthread_mutex_unlock(mutexp); |
699 | #elif defined(NSAPI) |
700 | crit_exit(mutexp); |
701 | return 0; |
702 | #elif defined(PI3WEB) |
703 | return PISync_unlock(mutexp); |
704 | #elif defined(TSRM_ST) |
705 | return st_mutex_unlock(mutexp); |
706 | #elif defined(BETHREADS) |
707 | if (atomic_add(&mutexp->ben, -1) != 1) |
708 | return release_sem(mutexp->sem); |
709 | return 0; |
710 | #endif |
711 | } |
712 | |
713 | /* |
714 | Changes the signal mask of the calling thread |
715 | */ |
716 | #ifdef HAVE_SIGPROCMASK |
717 | TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset) |
718 | { |
719 | TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Changed sigmask in thread: %ld" , tsrm_thread_id())); |
720 | /* TODO: add support for other APIs */ |
721 | #ifdef PTHREADS |
722 | return pthread_sigmask(how, set, oldset); |
723 | #else |
724 | return sigprocmask(how, set, oldset); |
725 | #endif |
726 | } |
727 | #endif |
728 | |
729 | |
730 | TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler) |
731 | { |
732 | void *retval = (void *) tsrm_new_thread_begin_handler; |
733 | |
734 | tsrm_new_thread_begin_handler = new_thread_begin_handler; |
735 | return retval; |
736 | } |
737 | |
738 | |
739 | TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler) |
740 | { |
741 | void *retval = (void *) tsrm_new_thread_end_handler; |
742 | |
743 | tsrm_new_thread_end_handler = new_thread_end_handler; |
744 | return retval; |
745 | } |
746 | |
747 | |
748 | |
749 | /* |
750 | * Debug support |
751 | */ |
752 | |
753 | #if TSRM_DEBUG |
754 | int tsrm_error(int level, const char *format, ...) |
755 | { |
756 | if (level<=tsrm_error_level) { |
757 | va_list args; |
758 | int size; |
759 | |
760 | fprintf(tsrm_error_file, "TSRM: " ); |
761 | va_start(args, format); |
762 | size = vfprintf(tsrm_error_file, format, args); |
763 | va_end(args); |
764 | fprintf(tsrm_error_file, "\n" ); |
765 | fflush(tsrm_error_file); |
766 | return size; |
767 | } else { |
768 | return 0; |
769 | } |
770 | } |
771 | #endif |
772 | |
773 | |
774 | void tsrm_error_set(int level, char *debug_filename) |
775 | { |
776 | tsrm_error_level = level; |
777 | |
778 | #if TSRM_DEBUG |
779 | if (tsrm_error_file!=stderr) { /* close files opened earlier */ |
780 | fclose(tsrm_error_file); |
781 | } |
782 | |
783 | if (debug_filename) { |
784 | tsrm_error_file = fopen(debug_filename, "w" ); |
785 | if (!tsrm_error_file) { |
786 | tsrm_error_file = stderr; |
787 | } |
788 | } else { |
789 | tsrm_error_file = stderr; |
790 | } |
791 | #endif |
792 | } |
793 | |
794 | #endif /* ZTS */ |
795 | |