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