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