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