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 #ifdef ZEND_DEBUG
21 # include <assert.h>
22 # define TSRM_ASSERT assert
23 #else
24 # define TSRM_ASSERT
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 #if 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 #if defined(GNUPTH)
106 static pth_key_t tls_key;
107 # define tsrm_tls_set(what) pth_key_setdata(tls_key, (void*)(what))
108 # define tsrm_tls_get() pth_key_getdata(tls_key)
109
110 #elif defined(PTHREADS)
111 /* Thread local storage */
112 static pthread_key_t tls_key;
113 # define tsrm_tls_set(what) pthread_setspecific(tls_key, (void*)(what))
114 # define tsrm_tls_get() pthread_getspecific(tls_key)
115
116 #elif defined(TSRM_ST)
117 static int tls_key;
118 # define tsrm_tls_set(what) st_thread_setspecific(tls_key, (void*)(what))
119 # define tsrm_tls_get() st_thread_getspecific(tls_key)
120
121 #elif defined(TSRM_WIN32)
122 static DWORD tls_key;
123 # define tsrm_tls_set(what) TlsSetValue(tls_key, (void*)(what))
124 # define tsrm_tls_get() TlsGetValue(tls_key)
125
126 #else
127 # define tsrm_tls_set(what)
128 # define tsrm_tls_get() NULL
129 # warning tsrm_set_interpreter_context is probably broken on this platform
130 #endif
131
132 TSRM_TLS uint8_t in_main_thread = 0;
133 TSRM_TLS uint8_t is_thread_shutdown = 0;
134
135 /* Startup TSRM (call once for the entire process) */
tsrm_startup(int expected_threads,int expected_resources,int debug_level,char * debug_filename)136 TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename)
137 {/*{{{*/
138 #if defined(GNUPTH)
139 pth_init();
140 pth_key_create(&tls_key, 0);
141 #elif defined(PTHREADS)
142 pthread_key_create( &tls_key, 0 );
143 #elif defined(TSRM_ST)
144 st_init();
145 st_key_create(&tls_key, 0);
146 #elif defined(TSRM_WIN32)
147 tls_key = TlsAlloc();
148 #endif
149
150 /* ensure singleton */
151 in_main_thread = 1;
152 is_thread_shutdown = 0;
153
154 tsrm_error_file = stderr;
155 tsrm_error_set(debug_level, debug_filename);
156 tsrm_tls_table_size = expected_threads;
157
158 tsrm_tls_table = (tsrm_tls_entry **) calloc(tsrm_tls_table_size, sizeof(tsrm_tls_entry *));
159 if (!tsrm_tls_table) {
160 TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate TLS table"));
161 is_thread_shutdown = 1;
162 return 0;
163 }
164 id_count=0;
165
166 resource_types_table_size = expected_resources;
167 resource_types_table = (tsrm_resource_type *) calloc(resource_types_table_size, sizeof(tsrm_resource_type));
168 if (!resource_types_table) {
169 TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate resource types table"));
170 is_thread_shutdown = 1;
171 free(tsrm_tls_table);
172 return 0;
173 }
174
175 tsmm_mutex = tsrm_mutex_alloc();
176
177 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Started up TSRM, %d expected threads, %d expected resources", expected_threads, expected_resources));
178
179 tsrm_reserved_pos = 0;
180 tsrm_reserved_size = 0;
181
182 tsrm_env_mutex = tsrm_mutex_alloc();
183
184 return 1;
185 }/*}}}*/
186
187
188 /* Shutdown TSRM (call once for the entire process) */
tsrm_shutdown(void)189 TSRM_API void tsrm_shutdown(void)
190 {/*{{{*/
191 int i;
192
193 if (is_thread_shutdown) {
194 /* shutdown must only occur once */
195 return;
196 }
197
198 is_thread_shutdown = 1;
199
200 if (!in_main_thread) {
201 /* only the main thread may shutdown tsrm */
202 return;
203 }
204
205 for (i=0; i<tsrm_tls_table_size; i++) {
206 tsrm_tls_entry *p = tsrm_tls_table[i], *next_p;
207
208 while (p) {
209 int j;
210
211 next_p = p->next;
212 for (j=0; j<p->count; j++) {
213 if (p->storage[j]) {
214 if (resource_types_table && !resource_types_table[j].done && resource_types_table[j].dtor) {
215 resource_types_table[j].dtor(p->storage[j]);
216 }
217 if (!resource_types_table[j].fast_offset) {
218 free(p->storage[j]);
219 }
220 }
221 }
222 free(p->storage);
223 free(p);
224 p = next_p;
225 }
226 }
227 free(tsrm_tls_table);
228 free(resource_types_table);
229 tsrm_mutex_free(tsmm_mutex);
230 tsrm_mutex_free(tsrm_env_mutex);
231 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Shutdown TSRM"));
232 if (tsrm_error_file!=stderr) {
233 fclose(tsrm_error_file);
234 }
235 #if defined(GNUPTH)
236 pth_kill();
237 #elif defined(PTHREADS)
238 pthread_setspecific(tls_key, 0);
239 pthread_key_delete(tls_key);
240 #elif defined(TSRM_WIN32)
241 TlsFree(tls_key);
242 #endif
243 if (tsrm_shutdown_handler) {
244 tsrm_shutdown_handler();
245 }
246 tsrm_new_thread_begin_handler = NULL;
247 tsrm_new_thread_end_handler = NULL;
248 tsrm_shutdown_handler = NULL;
249
250 tsrm_reserved_pos = 0;
251 tsrm_reserved_size = 0;
252 }/*}}}*/
253
254 /* {{{ */
255 /* environ lock api */
tsrm_env_lock()256 TSRM_API void tsrm_env_lock() {
257 tsrm_mutex_lock(tsrm_env_mutex);
258 }
259
tsrm_env_unlock()260 TSRM_API void tsrm_env_unlock() {
261 tsrm_mutex_unlock(tsrm_env_mutex);
262 } /* }}} */
263
264 /* enlarge the arrays for the already active threads */
tsrm_update_active_threads(void)265 static void tsrm_update_active_threads(void)
266 {/*{{{*/
267 int i;
268
269 for (i=0; i<tsrm_tls_table_size; i++) {
270 tsrm_tls_entry *p = tsrm_tls_table[i];
271
272 while (p) {
273 if (p->count < id_count) {
274 int j;
275
276 p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count);
277 for (j=p->count; j<id_count; j++) {
278 if (resource_types_table[j].fast_offset) {
279 p->storage[j] = (void *) (((char*)p) + resource_types_table[j].fast_offset);
280 } else {
281 p->storage[j] = (void *) malloc(resource_types_table[j].size);
282 }
283 if (resource_types_table[j].ctor) {
284 resource_types_table[j].ctor(p->storage[j]);
285 }
286 }
287 p->count = id_count;
288 }
289 p = p->next;
290 }
291 }
292 }/*}}}*/
293
294
295 /* 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)296 TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
297 {/*{{{*/
298 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size));
299
300 tsrm_mutex_lock(tsmm_mutex);
301
302 /* obtain a resource id */
303 *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
304 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));
305
306 /* store the new resource type in the resource sizes table */
307 if (resource_types_table_size < id_count) {
308 tsrm_resource_type *_tmp;
309 _tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
310 if (!_tmp) {
311 tsrm_mutex_unlock(tsmm_mutex);
312 TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
313 *rsrc_id = 0;
314 return 0;
315 }
316 resource_types_table = _tmp;
317 resource_types_table_size = id_count;
318 }
319 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
320 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
321 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
322 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = 0;
323 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
324
325 tsrm_update_active_threads();
326 tsrm_mutex_unlock(tsmm_mutex);
327
328 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
329 return *rsrc_id;
330 }/*}}}*/
331
332
333 /* Reserve space for fast thread-safe-resources */
tsrm_reserve(size_t size)334 TSRM_API void tsrm_reserve(size_t size)
335 {/*{{{*/
336 tsrm_reserved_pos = 0;
337 tsrm_reserved_size = TSRM_ALIGNED_SIZE(size);
338 }/*}}}*/
339
340
341 /* 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)342 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)
343 {/*{{{*/
344 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new fast resource id, %d bytes", size));
345
346 tsrm_mutex_lock(tsmm_mutex);
347
348 /* obtain a resource id */
349 *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
350 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));
351
352 size = TSRM_ALIGNED_SIZE(size);
353 if (tsrm_reserved_size - tsrm_reserved_pos < size) {
354 tsrm_mutex_unlock(tsmm_mutex);
355 TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate space for fast resource"));
356 *rsrc_id = 0;
357 *offset = 0;
358 return 0;
359 }
360
361 *offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos;
362 tsrm_reserved_pos += size;
363
364 /* store the new resource type in the resource sizes table */
365 if (resource_types_table_size < id_count) {
366 tsrm_resource_type *_tmp;
367 _tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
368 if (!_tmp) {
369 tsrm_mutex_unlock(tsmm_mutex);
370 TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
371 *rsrc_id = 0;
372 return 0;
373 }
374 resource_types_table = _tmp;
375 resource_types_table_size = id_count;
376 }
377 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
378 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
379 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
380 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = *offset;
381 resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
382
383 tsrm_update_active_threads();
384 tsrm_mutex_unlock(tsmm_mutex);
385
386 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
387 return *rsrc_id;
388 }/*}}}*/
389
390
allocate_new_resource(tsrm_tls_entry ** thread_resources_ptr,THREAD_T thread_id)391 static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)
392 {/*{{{*/
393 int i;
394
395 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Creating data structures for thread %x", thread_id));
396 (*thread_resources_ptr) = (tsrm_tls_entry *) malloc(TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_size);
397 (*thread_resources_ptr)->storage = NULL;
398 if (id_count > 0) {
399 (*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count);
400 }
401 (*thread_resources_ptr)->count = id_count;
402 (*thread_resources_ptr)->thread_id = thread_id;
403 (*thread_resources_ptr)->next = NULL;
404
405 /* Set thread local storage to this new thread resources structure */
406 tsrm_tls_set(*thread_resources_ptr);
407 TSRMLS_CACHE = *thread_resources_ptr;
408
409 if (tsrm_new_thread_begin_handler) {
410 tsrm_new_thread_begin_handler(thread_id);
411 }
412 for (i=0; i<id_count; i++) {
413 if (resource_types_table[i].done) {
414 (*thread_resources_ptr)->storage[i] = NULL;
415 } else {
416 if (resource_types_table[i].fast_offset) {
417 (*thread_resources_ptr)->storage[i] = (void *) (((char*)(*thread_resources_ptr)) + resource_types_table[i].fast_offset);
418 } else {
419 (*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
420 }
421 if (resource_types_table[i].ctor) {
422 resource_types_table[i].ctor((*thread_resources_ptr)->storage[i]);
423 }
424 }
425 }
426
427 if (tsrm_new_thread_end_handler) {
428 tsrm_new_thread_end_handler(thread_id);
429 }
430
431 tsrm_mutex_unlock(tsmm_mutex);
432 }/*}}}*/
433
434
435 /* fetches the requested resource for the current thread */
ts_resource_ex(ts_rsrc_id id,THREAD_T * th_id)436 TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
437 {/*{{{*/
438 THREAD_T thread_id;
439 int hash_value;
440 tsrm_tls_entry *thread_resources;
441
442 if (!th_id) {
443 /* Fast path for looking up the resources for the current
444 * thread. Its used by just about every call to
445 * ts_resource_ex(). This avoids the need for a mutex lock
446 * and our hashtable lookup.
447 */
448 thread_resources = tsrm_tls_get();
449
450 if (thread_resources) {
451 TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for current thread %d", id, (long) thread_resources->thread_id));
452 /* Read a specific resource from the thread's resources.
453 * This is called outside of a mutex, so have to be aware about external
454 * changes to the structure as we read it.
455 */
456 TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
457 }
458 thread_id = tsrm_thread_id();
459 } else {
460 thread_id = *th_id;
461 }
462
463 TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for thread %ld", id, (long) thread_id));
464 tsrm_mutex_lock(tsmm_mutex);
465
466 hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
467 thread_resources = tsrm_tls_table[hash_value];
468
469 if (!thread_resources) {
470 allocate_new_resource(&tsrm_tls_table[hash_value], thread_id);
471 return ts_resource_ex(id, &thread_id);
472 } else {
473 do {
474 if (thread_resources->thread_id == thread_id) {
475 break;
476 }
477 if (thread_resources->next) {
478 thread_resources = thread_resources->next;
479 } else {
480 allocate_new_resource(&thread_resources->next, thread_id);
481 return ts_resource_ex(id, &thread_id);
482 /*
483 * thread_resources = thread_resources->next;
484 * break;
485 */
486 }
487 } while (thread_resources);
488 }
489 tsrm_mutex_unlock(tsmm_mutex);
490 /* Read a specific resource from the thread's resources.
491 * This is called outside of a mutex, so have to be aware about external
492 * changes to the structure as we read it.
493 */
494 TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
495 }/*}}}*/
496
497 /* frees an interpreter context. You are responsible for making sure that
498 * it is not linked into the TSRM hash, and not marked as the current interpreter */
tsrm_free_interpreter_context(void * context)499 void tsrm_free_interpreter_context(void *context)
500 {/*{{{*/
501 tsrm_tls_entry *next, *thread_resources = (tsrm_tls_entry*)context;
502 int i;
503
504 while (thread_resources) {
505 next = thread_resources->next;
506
507 for (i=0; i<thread_resources->count; i++) {
508 if (resource_types_table[i].dtor) {
509 resource_types_table[i].dtor(thread_resources->storage[i]);
510 }
511 }
512 for (i=0; i<thread_resources->count; i++) {
513 if (!resource_types_table[i].fast_offset) {
514 free(thread_resources->storage[i]);
515 }
516 }
517 free(thread_resources->storage);
518 free(thread_resources);
519 thread_resources = next;
520 }
521 }/*}}}*/
522
tsrm_set_interpreter_context(void * new_ctx)523 void *tsrm_set_interpreter_context(void *new_ctx)
524 {/*{{{*/
525 tsrm_tls_entry *current;
526
527 current = tsrm_tls_get();
528
529 /* TODO: unlink current from the global linked list, and replace it
530 * it with the new context, protected by mutex where/if appropriate */
531
532 /* Set thread local storage to this new thread resources structure */
533 tsrm_tls_set(new_ctx);
534
535 /* return old context, so caller can restore it when they're done */
536 return current;
537 }/*}}}*/
538
539
540 /* allocates a new interpreter context */
tsrm_new_interpreter_context(void)541 void *tsrm_new_interpreter_context(void)
542 {/*{{{*/
543 tsrm_tls_entry *new_ctx, *current;
544 THREAD_T thread_id;
545
546 thread_id = tsrm_thread_id();
547 tsrm_mutex_lock(tsmm_mutex);
548
549 current = tsrm_tls_get();
550
551 allocate_new_resource(&new_ctx, thread_id);
552
553 /* switch back to the context that was in use prior to our creation
554 * of the new one */
555 return tsrm_set_interpreter_context(current);
556 }/*}}}*/
557
558
559 /* frees all resources allocated for the current thread */
ts_free_thread(void)560 void ts_free_thread(void)
561 {/*{{{*/
562 tsrm_tls_entry *thread_resources;
563 int i;
564 THREAD_T thread_id = tsrm_thread_id();
565 int hash_value;
566 tsrm_tls_entry *last=NULL;
567
568 TSRM_ASSERT(!in_main_thread);
569
570 tsrm_mutex_lock(tsmm_mutex);
571 hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
572 thread_resources = tsrm_tls_table[hash_value];
573
574 while (thread_resources) {
575 if (thread_resources->thread_id == thread_id) {
576 for (i=0; i<thread_resources->count; i++) {
577 if (resource_types_table[i].dtor) {
578 resource_types_table[i].dtor(thread_resources->storage[i]);
579 }
580 }
581 for (i=0; i<thread_resources->count; i++) {
582 if (!resource_types_table[i].fast_offset) {
583 free(thread_resources->storage[i]);
584 }
585 }
586 free(thread_resources->storage);
587 if (last) {
588 last->next = thread_resources->next;
589 } else {
590 tsrm_tls_table[hash_value] = thread_resources->next;
591 }
592 tsrm_tls_set(0);
593 free(thread_resources);
594 break;
595 }
596 if (thread_resources->next) {
597 last = thread_resources;
598 }
599 thread_resources = thread_resources->next;
600 }
601 tsrm_mutex_unlock(tsmm_mutex);
602 }/*}}}*/
603
604 /* deallocates all occurrences of a given id */
ts_free_id(ts_rsrc_id id)605 void ts_free_id(ts_rsrc_id id)
606 {/*{{{*/
607 int i;
608 int j = TSRM_UNSHUFFLE_RSRC_ID(id);
609
610 tsrm_mutex_lock(tsmm_mutex);
611
612 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Freeing resource id %d", id));
613
614 if (tsrm_tls_table) {
615 for (i=0; i<tsrm_tls_table_size; i++) {
616 tsrm_tls_entry *p = tsrm_tls_table[i];
617
618 while (p) {
619 if (p->count > j && p->storage[j]) {
620 if (resource_types_table && resource_types_table[j].dtor) {
621 resource_types_table[j].dtor(p->storage[j]);
622 }
623 if (!resource_types_table[j].fast_offset) {
624 free(p->storage[j]);
625 }
626 p->storage[j] = NULL;
627 }
628 p = p->next;
629 }
630 }
631 }
632 resource_types_table[j].done = 1;
633
634 tsrm_mutex_unlock(tsmm_mutex);
635
636 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully freed resource id %d", id));
637 }/*}}}*/
638
639
640
641
642 /*
643 * Utility Functions
644 */
645
646 /* Obtain the current thread id */
tsrm_thread_id(void)647 TSRM_API THREAD_T tsrm_thread_id(void)
648 {/*{{{*/
649 #ifdef TSRM_WIN32
650 return GetCurrentThreadId();
651 #elif defined(GNUPTH)
652 return pth_self();
653 #elif defined(PTHREADS)
654 return pthread_self();
655 #elif defined(TSRM_ST)
656 return st_thread_self();
657 #endif
658 }/*}}}*/
659
660
661 /* Allocate a mutex */
tsrm_mutex_alloc(void)662 TSRM_API MUTEX_T tsrm_mutex_alloc(void)
663 {/*{{{*/
664 MUTEX_T mutexp;
665 #ifdef TSRM_WIN32
666 mutexp = malloc(sizeof(CRITICAL_SECTION));
667 InitializeCriticalSection(mutexp);
668 #elif defined(GNUPTH)
669 mutexp = (MUTEX_T) malloc(sizeof(*mutexp));
670 pth_mutex_init(mutexp);
671 #elif defined(PTHREADS)
672 mutexp = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
673 pthread_mutex_init(mutexp,NULL);
674 #elif defined(TSRM_ST)
675 mutexp = st_mutex_new();
676 #endif
677 #ifdef THR_DEBUG
678 printf("Mutex created thread: %d\n",mythreadid());
679 #endif
680 return( mutexp );
681 }/*}}}*/
682
683
684 /* Free a mutex */
tsrm_mutex_free(MUTEX_T mutexp)685 TSRM_API void tsrm_mutex_free(MUTEX_T mutexp)
686 {/*{{{*/
687 if (mutexp) {
688 #ifdef TSRM_WIN32
689 DeleteCriticalSection(mutexp);
690 free(mutexp);
691 #elif defined(GNUPTH)
692 free(mutexp);
693 #elif defined(PTHREADS)
694 pthread_mutex_destroy(mutexp);
695 free(mutexp);
696 #elif defined(TSRM_ST)
697 st_mutex_destroy(mutexp);
698 #endif
699 }
700 #ifdef THR_DEBUG
701 printf("Mutex freed thread: %d\n",mythreadid());
702 #endif
703 }/*}}}*/
704
705
706 /*
707 Lock a mutex.
708 A return value of 0 indicates success
709 */
tsrm_mutex_lock(MUTEX_T mutexp)710 TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp)
711 {/*{{{*/
712 TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex locked thread: %ld", tsrm_thread_id()));
713 #ifdef TSRM_WIN32
714 EnterCriticalSection(mutexp);
715 return 0;
716 #elif defined(GNUPTH)
717 if (pth_mutex_acquire(mutexp, 0, NULL)) {
718 return 0;
719 }
720 return -1;
721 #elif defined(PTHREADS)
722 return pthread_mutex_lock(mutexp);
723 #elif defined(TSRM_ST)
724 return st_mutex_lock(mutexp);
725 #endif
726 }/*}}}*/
727
728
729 /*
730 Unlock a mutex.
731 A return value of 0 indicates success
732 */
tsrm_mutex_unlock(MUTEX_T mutexp)733 TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp)
734 {/*{{{*/
735 TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex unlocked thread: %ld", tsrm_thread_id()));
736 #ifdef TSRM_WIN32
737 LeaveCriticalSection(mutexp);
738 return 0;
739 #elif defined(GNUPTH)
740 if (pth_mutex_release(mutexp)) {
741 return 0;
742 }
743 return -1;
744 #elif defined(PTHREADS)
745 return pthread_mutex_unlock(mutexp);
746 #elif defined(TSRM_ST)
747 return st_mutex_unlock(mutexp);
748 #endif
749 }/*}}}*/
750
751 /*
752 Changes the signal mask of the calling thread
753 */
754 #ifdef HAVE_SIGPROCMASK
tsrm_sigmask(int how,const sigset_t * set,sigset_t * oldset)755 TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset)
756 {/*{{{*/
757 TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Changed sigmask in thread: %ld", tsrm_thread_id()));
758 /* TODO: add support for other APIs */
759 #ifdef PTHREADS
760 return pthread_sigmask(how, set, oldset);
761 #else
762 return sigprocmask(how, set, oldset);
763 #endif
764 }/*}}}*/
765 #endif
766
767
tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler)768 TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler)
769 {/*{{{*/
770 void *retval = (void *) tsrm_new_thread_begin_handler;
771
772 tsrm_new_thread_begin_handler = new_thread_begin_handler;
773 return retval;
774 }/*}}}*/
775
776
tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler)777 TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler)
778 {/*{{{*/
779 void *retval = (void *) tsrm_new_thread_end_handler;
780
781 tsrm_new_thread_end_handler = new_thread_end_handler;
782 return retval;
783 }/*}}}*/
784
785
tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler)786 TSRM_API void *tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler)
787 {/*{{{*/
788 void *retval = (void *) tsrm_shutdown_handler;
789
790 tsrm_shutdown_handler = shutdown_handler;
791 return retval;
792 }/*}}}*/
793
794
795 /*
796 * Debug support
797 */
798
799 #if TSRM_DEBUG
tsrm_error(int level,const char * format,...)800 int tsrm_error(int level, const char *format, ...)
801 {/*{{{*/
802 if (level<=tsrm_error_level) {
803 va_list args;
804 int size;
805
806 fprintf(tsrm_error_file, "TSRM: ");
807 va_start(args, format);
808 size = vfprintf(tsrm_error_file, format, args);
809 va_end(args);
810 fprintf(tsrm_error_file, "\n");
811 fflush(tsrm_error_file);
812 return size;
813 } else {
814 return 0;
815 }
816 }/*}}}*/
817 #endif
818
819
tsrm_error_set(int level,char * debug_filename)820 void tsrm_error_set(int level, char *debug_filename)
821 {/*{{{*/
822 tsrm_error_level = level;
823
824 #if TSRM_DEBUG
825 if (tsrm_error_file!=stderr) { /* close files opened earlier */
826 fclose(tsrm_error_file);
827 }
828
829 if (debug_filename) {
830 tsrm_error_file = fopen(debug_filename, "w");
831 if (!tsrm_error_file) {
832 tsrm_error_file = stderr;
833 }
834 } else {
835 tsrm_error_file = stderr;
836 }
837 #endif
838 }/*}}}*/
839
tsrm_get_ls_cache(void)840 TSRM_API void *tsrm_get_ls_cache(void)
841 {/*{{{*/
842 return tsrm_tls_get();
843 }/*}}}*/
844
tsrm_is_main_thread(void)845 TSRM_API uint8_t tsrm_is_main_thread(void)
846 {/*{{{*/
847 return in_main_thread;
848 }/*}}}*/
849
tsrm_is_shutdown(void)850 TSRM_API uint8_t tsrm_is_shutdown(void)
851 {/*{{{*/
852 return is_thread_shutdown;
853 }/*}}}*/
854
tsrm_api_name(void)855 TSRM_API const char *tsrm_api_name(void)
856 {/*{{{*/
857 #if defined(GNUPTH)
858 return "GNU Pth";
859 #elif defined(PTHREADS)
860 return "POSIX Threads";
861 #elif defined(TSRM_ST)
862 return "State Threads";
863 #elif defined(TSRM_WIN32)
864 return "Windows Threads";
865 #else
866 return "Unknown";
867 #endif
868 }/*}}}*/
869
870 #endif /* ZTS */
871