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 && !resource_types_table[j].done && resource_types_table[j].dtor) {
192 resource_types_table[j].dtor(p->storage[j]);
193 }
194 if (!resource_types_table[j].fast_offset) {
195 free(p->storage[j]);
196 }
197 }
198 }
199 free(p->storage);
200 free(p);
201 p = next_p;
202 }
203 }
204 free(tsrm_tls_table);
205 free(resource_types_table);
206 tsrm_mutex_free(tsmm_mutex);
207 tsrm_mutex_free(tsrm_env_mutex);
208 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Shutdown TSRM"));
209 if (tsrm_error_file!=stderr) {
210 fclose(tsrm_error_file);
211 }
212 #ifdef TSRM_WIN32
213 TlsFree(tls_key);
214 #else
215 pthread_setspecific(tls_key, 0);
216 pthread_key_delete(tls_key);
217 #endif
218 if (tsrm_shutdown_handler) {
219 tsrm_shutdown_handler();
220 }
221 tsrm_new_thread_begin_handler = NULL;
222 tsrm_new_thread_end_handler = NULL;
223 tsrm_shutdown_handler = NULL;
224
225 tsrm_reserved_pos = 0;
226 tsrm_reserved_size = 0;
227 }/*}}}*/
228
229 /* {{{ */
230 /* environ lock api */
tsrm_env_lock(void)231 TSRM_API void tsrm_env_lock(void) {
232 tsrm_mutex_lock(tsrm_env_mutex);
233 }
234
tsrm_env_unlock(void)235 TSRM_API void tsrm_env_unlock(void) {
236 tsrm_mutex_unlock(tsrm_env_mutex);
237 } /* }}} */
238
239 /* enlarge the arrays for the already active threads */
tsrm_update_active_threads(void)240 static void tsrm_update_active_threads(void)
241 {/*{{{*/
242 int i;
243
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 if (resource_types_table[j].fast_offset) {
254 p->storage[j] = (void *) (((char*)p) + resource_types_table[j].fast_offset);
255 } else {
256 p->storage[j] = (void *) malloc(resource_types_table[j].size);
257 }
258 if (resource_types_table[j].ctor) {
259 resource_types_table[j].ctor(p->storage[j]);
260 }
261 }
262 p->count = id_count;
263 }
264 p = p->next;
265 }
266 }
267 }/*}}}*/
268
269
270 /* 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)271 TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
272 {/*{{{*/
273 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size));
274
275 tsrm_mutex_lock(tsmm_mutex);
276
277 /* obtain a resource id */
278 *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
279 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));
280
281 /* store the new resource type in the resource sizes table */
282 if (resource_types_table_size < id_count) {
283 tsrm_resource_type *_tmp;
284 _tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
285 if (!_tmp) {
286 tsrm_mutex_unlock(tsmm_mutex);
287 TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
288 *rsrc_id = 0;
289 return 0;
290 }
291 resource_types_table = _tmp;
292 resource_types_table_size = id_count;
293 }
294 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
295 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
296 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
297 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = 0;
298 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
299
300 tsrm_update_active_threads();
301 tsrm_mutex_unlock(tsmm_mutex);
302
303 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
304 return *rsrc_id;
305 }/*}}}*/
306
307
308 /* Reserve space for fast thread-safe-resources */
tsrm_reserve(size_t size)309 TSRM_API void tsrm_reserve(size_t size)
310 {/*{{{*/
311 tsrm_reserved_pos = 0;
312 tsrm_reserved_size = TSRM_ALIGNED_SIZE(size);
313 }/*}}}*/
314
315
316 /* 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)317 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)
318 {/*{{{*/
319 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new fast resource id, %d bytes", size));
320
321 tsrm_mutex_lock(tsmm_mutex);
322
323 /* obtain a resource id */
324 *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
325 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));
326
327 size = TSRM_ALIGNED_SIZE(size);
328 if (tsrm_reserved_size - tsrm_reserved_pos < size) {
329 tsrm_mutex_unlock(tsmm_mutex);
330 TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate space for fast resource"));
331 *rsrc_id = 0;
332 *offset = 0;
333 return 0;
334 }
335
336 *offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos;
337 tsrm_reserved_pos += size;
338
339 /* store the new resource type in the resource sizes table */
340 if (resource_types_table_size < id_count) {
341 tsrm_resource_type *_tmp;
342 _tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
343 if (!_tmp) {
344 tsrm_mutex_unlock(tsmm_mutex);
345 TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
346 *rsrc_id = 0;
347 return 0;
348 }
349 resource_types_table = _tmp;
350 resource_types_table_size = id_count;
351 }
352 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
353 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
354 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
355 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = *offset;
356 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
357
358 tsrm_update_active_threads();
359 tsrm_mutex_unlock(tsmm_mutex);
360
361 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
362 return *rsrc_id;
363 }/*}}}*/
364
365
allocate_new_resource(tsrm_tls_entry ** thread_resources_ptr,THREAD_T thread_id)366 static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)
367 {/*{{{*/
368 int i;
369
370 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Creating data structures for thread %x", thread_id));
371 (*thread_resources_ptr) = (tsrm_tls_entry *) malloc(TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_size);
372 (*thread_resources_ptr)->storage = NULL;
373 if (id_count > 0) {
374 (*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count);
375 }
376 (*thread_resources_ptr)->count = id_count;
377 (*thread_resources_ptr)->thread_id = thread_id;
378 (*thread_resources_ptr)->next = NULL;
379
380 /* Set thread local storage to this new thread resources structure */
381 tsrm_tls_set(*thread_resources_ptr);
382 TSRMLS_CACHE = *thread_resources_ptr;
383
384 if (tsrm_new_thread_begin_handler) {
385 tsrm_new_thread_begin_handler(thread_id);
386 }
387 for (i=0; i<id_count; i++) {
388 if (resource_types_table[i].done) {
389 (*thread_resources_ptr)->storage[i] = NULL;
390 } else {
391 if (resource_types_table[i].fast_offset) {
392 (*thread_resources_ptr)->storage[i] = (void *) (((char*)(*thread_resources_ptr)) + resource_types_table[i].fast_offset);
393 } else {
394 (*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
395 }
396 if (resource_types_table[i].ctor) {
397 resource_types_table[i].ctor((*thread_resources_ptr)->storage[i]);
398 }
399 }
400 }
401
402 if (tsrm_new_thread_end_handler) {
403 tsrm_new_thread_end_handler(thread_id);
404 }
405
406 tsrm_mutex_unlock(tsmm_mutex);
407 }/*}}}*/
408
409
410 /* fetches the requested resource for the current thread */
ts_resource_ex(ts_rsrc_id id,THREAD_T * th_id)411 TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
412 {/*{{{*/
413 THREAD_T thread_id;
414 int hash_value;
415 tsrm_tls_entry *thread_resources;
416
417 if (!th_id) {
418 /* Fast path for looking up the resources for the current
419 * thread. Its used by just about every call to
420 * ts_resource_ex(). This avoids the need for a mutex lock
421 * and our hashtable lookup.
422 */
423 thread_resources = tsrm_tls_get();
424
425 if (thread_resources) {
426 TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for current thread %d", id, (long) thread_resources->thread_id));
427 /* Read a specific resource from the thread's resources.
428 * This is called outside of a mutex, so have to be aware about external
429 * changes to the structure as we read it.
430 */
431 TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
432 }
433 thread_id = tsrm_thread_id();
434 } else {
435 thread_id = *th_id;
436 }
437
438 TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for thread %ld", id, (long) thread_id));
439 tsrm_mutex_lock(tsmm_mutex);
440
441 hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
442 thread_resources = tsrm_tls_table[hash_value];
443
444 if (!thread_resources) {
445 allocate_new_resource(&tsrm_tls_table[hash_value], thread_id);
446 return ts_resource_ex(id, &thread_id);
447 } else {
448 do {
449 if (thread_resources->thread_id == thread_id) {
450 break;
451 }
452 if (thread_resources->next) {
453 thread_resources = thread_resources->next;
454 } else {
455 allocate_new_resource(&thread_resources->next, thread_id);
456 return ts_resource_ex(id, &thread_id);
457 /*
458 * thread_resources = thread_resources->next;
459 * break;
460 */
461 }
462 } while (thread_resources);
463 }
464 tsrm_mutex_unlock(tsmm_mutex);
465 /* Read a specific resource from the thread's resources.
466 * This is called outside of a mutex, so have to be aware about external
467 * changes to the structure as we read it.
468 */
469 TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
470 }/*}}}*/
471
472
473 /* frees all resources allocated for the current thread */
ts_free_thread(void)474 void ts_free_thread(void)
475 {/*{{{*/
476 tsrm_tls_entry *thread_resources;
477 int i;
478 THREAD_T thread_id = tsrm_thread_id();
479 int hash_value;
480 tsrm_tls_entry *last=NULL;
481
482 TSRM_ASSERT(!in_main_thread);
483
484 tsrm_mutex_lock(tsmm_mutex);
485 hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
486 thread_resources = tsrm_tls_table[hash_value];
487
488 while (thread_resources) {
489 if (thread_resources->thread_id == thread_id) {
490 for (i=0; i<thread_resources->count; i++) {
491 if (resource_types_table[i].dtor) {
492 resource_types_table[i].dtor(thread_resources->storage[i]);
493 }
494 }
495 for (i=0; i<thread_resources->count; i++) {
496 if (!resource_types_table[i].fast_offset) {
497 free(thread_resources->storage[i]);
498 }
499 }
500 free(thread_resources->storage);
501 if (last) {
502 last->next = thread_resources->next;
503 } else {
504 tsrm_tls_table[hash_value] = thread_resources->next;
505 }
506 tsrm_tls_set(0);
507 free(thread_resources);
508 break;
509 }
510 if (thread_resources->next) {
511 last = thread_resources;
512 }
513 thread_resources = thread_resources->next;
514 }
515 tsrm_mutex_unlock(tsmm_mutex);
516 }/*}}}*/
517
518 /* deallocates all occurrences of a given id */
ts_free_id(ts_rsrc_id id)519 void ts_free_id(ts_rsrc_id id)
520 {/*{{{*/
521 int i;
522 int j = TSRM_UNSHUFFLE_RSRC_ID(id);
523
524 tsrm_mutex_lock(tsmm_mutex);
525
526 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Freeing resource id %d", id));
527
528 if (tsrm_tls_table) {
529 for (i=0; i<tsrm_tls_table_size; i++) {
530 tsrm_tls_entry *p = tsrm_tls_table[i];
531
532 while (p) {
533 if (p->count > j && p->storage[j]) {
534 if (resource_types_table && resource_types_table[j].dtor) {
535 resource_types_table[j].dtor(p->storage[j]);
536 }
537 if (!resource_types_table[j].fast_offset) {
538 free(p->storage[j]);
539 }
540 p->storage[j] = NULL;
541 }
542 p = p->next;
543 }
544 }
545 }
546 resource_types_table[j].done = 1;
547
548 tsrm_mutex_unlock(tsmm_mutex);
549
550 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully freed resource id %d", id));
551 }/*}}}*/
552
553
554 /*
555 * Utility Functions
556 */
557
558 /* Obtain the current thread id */
tsrm_thread_id(void)559 TSRM_API THREAD_T tsrm_thread_id(void)
560 {/*{{{*/
561 #ifdef TSRM_WIN32
562 return GetCurrentThreadId();
563 #else
564 return pthread_self();
565 #endif
566 }/*}}}*/
567
568
569 /* Allocate a mutex */
tsrm_mutex_alloc(void)570 TSRM_API MUTEX_T tsrm_mutex_alloc(void)
571 {/*{{{*/
572 MUTEX_T mutexp;
573 #ifdef TSRM_WIN32
574 mutexp = malloc(sizeof(CRITICAL_SECTION));
575 InitializeCriticalSection(mutexp);
576 #else
577 mutexp = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
578 pthread_mutex_init(mutexp,NULL);
579 #endif
580 #ifdef THR_DEBUG
581 printf("Mutex created thread: %d\n",mythreadid());
582 #endif
583 return( mutexp );
584 }/*}}}*/
585
586
587 /* Free a mutex */
tsrm_mutex_free(MUTEX_T mutexp)588 TSRM_API void tsrm_mutex_free(MUTEX_T mutexp)
589 {/*{{{*/
590 if (mutexp) {
591 #ifdef TSRM_WIN32
592 DeleteCriticalSection(mutexp);
593 free(mutexp);
594 #else
595 pthread_mutex_destroy(mutexp);
596 free(mutexp);
597 #endif
598 }
599 #ifdef THR_DEBUG
600 printf("Mutex freed thread: %d\n",mythreadid());
601 #endif
602 }/*}}}*/
603
604
605 /*
606 Lock a mutex.
607 A return value of 0 indicates success
608 */
tsrm_mutex_lock(MUTEX_T mutexp)609 TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp)
610 {/*{{{*/
611 TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex locked thread: %ld", tsrm_thread_id()));
612 #ifdef TSRM_WIN32
613 EnterCriticalSection(mutexp);
614 return 0;
615 #else
616 return pthread_mutex_lock(mutexp);
617 #endif
618 }/*}}}*/
619
620
621 /*
622 Unlock a mutex.
623 A return value of 0 indicates success
624 */
tsrm_mutex_unlock(MUTEX_T mutexp)625 TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp)
626 {/*{{{*/
627 TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex unlocked thread: %ld", tsrm_thread_id()));
628 #ifdef TSRM_WIN32
629 LeaveCriticalSection(mutexp);
630 return 0;
631 #else
632 return pthread_mutex_unlock(mutexp);
633 #endif
634 }/*}}}*/
635
636 /*
637 Changes the signal mask of the calling thread
638 */
639 #ifdef HAVE_SIGPROCMASK
tsrm_sigmask(int how,const sigset_t * set,sigset_t * oldset)640 TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset)
641 {/*{{{*/
642 TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Changed sigmask in thread: %ld", tsrm_thread_id()));
643
644 return pthread_sigmask(how, set, oldset);
645 }/*}}}*/
646 #endif
647
648
tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler)649 TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler)
650 {/*{{{*/
651 void *retval = (void *) tsrm_new_thread_begin_handler;
652
653 tsrm_new_thread_begin_handler = new_thread_begin_handler;
654 return retval;
655 }/*}}}*/
656
657
tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler)658 TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler)
659 {/*{{{*/
660 void *retval = (void *) tsrm_new_thread_end_handler;
661
662 tsrm_new_thread_end_handler = new_thread_end_handler;
663 return retval;
664 }/*}}}*/
665
666
tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler)667 TSRM_API void *tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler)
668 {/*{{{*/
669 void *retval = (void *) tsrm_shutdown_handler;
670
671 tsrm_shutdown_handler = shutdown_handler;
672 return retval;
673 }/*}}}*/
674
675
676 /*
677 * Debug support
678 */
679
680 #ifdef TSRM_DEBUG
tsrm_error(int level,const char * format,...)681 int tsrm_error(int level, const char *format, ...)
682 {/*{{{*/
683 if (level<=tsrm_error_level) {
684 va_list args;
685 int size;
686
687 fprintf(tsrm_error_file, "TSRM: ");
688 va_start(args, format);
689 size = vfprintf(tsrm_error_file, format, args);
690 va_end(args);
691 fprintf(tsrm_error_file, "\n");
692 fflush(tsrm_error_file);
693 return size;
694 } else {
695 return 0;
696 }
697 }/*}}}*/
698 #endif
699
700
tsrm_error_set(int level,const char * debug_filename)701 void tsrm_error_set(int level, const char *debug_filename)
702 {/*{{{*/
703 tsrm_error_level = level;
704
705 #ifdef TSRM_DEBUG
706 if (tsrm_error_file!=stderr) { /* close files opened earlier */
707 fclose(tsrm_error_file);
708 }
709
710 if (debug_filename) {
711 tsrm_error_file = fopen(debug_filename, "w");
712 if (!tsrm_error_file) {
713 tsrm_error_file = stderr;
714 }
715 } else {
716 tsrm_error_file = stderr;
717 }
718 #endif
719 }/*}}}*/
720
tsrm_get_ls_cache(void)721 TSRM_API void *tsrm_get_ls_cache(void)
722 {/*{{{*/
723 return tsrm_tls_get();
724 }/*}}}*/
725
726 /* Returns offset of tsrm_ls_cache slot from Thread Control Block address */
tsrm_get_ls_cache_tcb_offset(void)727 TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void)
728 {/*{{{*/
729 #if defined(__APPLE__) && defined(__x86_64__)
730 // TODO: Implement support for fast JIT ZTS code ???
731 return 0;
732 #elif defined(__x86_64__) && defined(__GNUC__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__)
733 size_t ret;
734
735 asm ("movq _tsrm_ls_cache@gottpoff(%%rip),%0"
736 : "=r" (ret));
737 return ret;
738 #elif defined(__i386__) && defined(__GNUC__) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
739 size_t ret;
740
741 asm ("leal _tsrm_ls_cache@ntpoff,%0"
742 : "=r" (ret));
743 return ret;
744 #else
745 return 0;
746 #endif
747 }/*}}}*/
748
tsrm_is_main_thread(void)749 TSRM_API uint8_t tsrm_is_main_thread(void)
750 {/*{{{*/
751 return in_main_thread;
752 }/*}}}*/
753
tsrm_is_shutdown(void)754 TSRM_API uint8_t tsrm_is_shutdown(void)
755 {/*{{{*/
756 return is_thread_shutdown;
757 }/*}}}*/
758
tsrm_api_name(void)759 TSRM_API const char *tsrm_api_name(void)
760 {/*{{{*/
761 #ifdef TSRM_WIN32
762 return "Windows Threads";
763 #else
764 return "POSIX Threads";
765 #endif
766 }/*}}}*/
767
768 #endif /* ZTS */
769