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