php-src/TSRM/TSRM.c

571 lines
14 KiB
C
Raw Normal View History

1999-04-23 19:00:02 +08:00
/*
+----------------------------------------------------------------------+
| Thread Safe Resource Manager |
+----------------------------------------------------------------------+
| Copyright (c) 1999, 2000, Andi Gutmans, Sascha Schumann, Zeev Suraski|
| This source file is subject to the TSRM license, that is bundled |
| with this package in the file LICENSE |
1999-04-23 19:00:02 +08:00
+----------------------------------------------------------------------+
| Authors: Zeev Suraski <zeev@zend.com> |
1999-04-23 19:00:02 +08:00
+----------------------------------------------------------------------+
*/
1999-04-21 07:58:02 +08:00
#include "TSRM.h"
#ifdef ZTS
1999-04-21 07:58:02 +08:00
#include <stdio.h>
#include <stdlib.h>
1999-04-21 07:58:02 +08:00
1999-04-24 17:01:30 +08:00
#if HAVE_STDARG_H
#include <stdarg.h>
#endif
1999-04-21 07:58:02 +08:00
typedef struct _tsrm_tls_entry tsrm_tls_entry;
struct _tsrm_tls_entry {
void **storage;
int count;
THREAD_T thread_id;
tsrm_tls_entry *next;
};
typedef struct {
size_t size;
ts_allocate_ctor ctor;
ts_allocate_dtor dtor;
1999-04-21 07:58:02 +08:00
} tsrm_resource_type;
#define TSRM_SHUFFLE_RSRC_ID(rsrc_id) ((rsrc_id)+1)
#define TSRM_UNSHUFFLE_RSRC_ID(rsrc_id) ((rsrc_id)-1)
1999-04-21 07:58:02 +08:00
/* The memory manager table */
static tsrm_tls_entry **tsrm_tls_table=NULL;
1999-04-21 07:58:02 +08:00
static int tsrm_tls_table_size;
static ts_rsrc_id id_count;
/* The resource sizes table */
static tsrm_resource_type *resource_types_table=NULL;
1999-04-21 07:58:02 +08:00
static int resource_types_table_size;
static MUTEX_T tsmm_mutex; /* thread-safe memory manager mutex */
1999-08-14 17:35:52 +08:00
/* New thread handlers */
static void (*tsrm_new_thread_begin_handler)();
static void (*tsrm_new_thread_end_handler)();
1999-04-21 07:58:02 +08:00
/* Debug support */
2000-11-18 10:41:14 +08:00
int tsrm_error(int level, const char *format, ...);
/* Read a resource from a thread's resource storage */
void *ts_resource_read( tsrm_tls_entry *thread_resources, ts_rsrc_id id );
static int tsrm_error_level;
2000-11-18 10:41:14 +08:00
static FILE *tsrm_error_file;
#if TSRM_DEBUG
#define TSRM_ERROR(a) tsrm_error a
#define TSRM_SAFE_ARRAY_OFFSET(array, offset, range) (((offset)>=0 && (offset)<(range)) ? array[offset] : NULL)
#else
#define TSRM_ERROR(a)
#define TSRM_SAFE_ARRAY_OFFSET(array, offset, range) array[offset]
#endif
1999-04-21 07:58:02 +08:00
#if defined(PTHREADS)
/* Thread local storage */
static pthread_key_t tls_key;
#elif defined(TSRM_ST)
static int tls_key;
#endif
1999-04-21 07:58:02 +08:00
/* Startup TSRM (call once for the entire process) */
2000-11-18 10:41:14 +08:00
TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename)
1999-04-21 07:58:02 +08:00
{
#if defined(GNUPTH)
pth_init();
#elif defined(PTHREADS)
pthread_key_create( &tls_key, 0 );
#elif defined(TSRM_ST)
st_init();
st_key_create(&tls_key, 0);
#endif
2000-11-18 10:41:14 +08:00
tsrm_error_file = stderr;
tsrm_error_set(debug_level, debug_filename);
1999-04-21 07:58:02 +08:00
tsrm_tls_table_size = expected_threads;
2000-11-18 10:41:14 +08:00
1999-04-21 07:58:02 +08:00
tsrm_tls_table = (tsrm_tls_entry **) calloc(tsrm_tls_table_size, sizeof(tsrm_tls_entry *));
if (!tsrm_tls_table) {
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate TLS table"));
1999-04-21 07:58:02 +08:00
return 0;
}
id_count=0;
resource_types_table_size = expected_resources;
resource_types_table = (tsrm_resource_type *) calloc(resource_types_table_size, sizeof(tsrm_resource_type));
if (!resource_types_table) {
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate resource types table"));
1999-04-21 07:58:02 +08:00
free(tsrm_tls_table);
tsrm_tls_table = NULL;
1999-04-21 07:58:02 +08:00
return 0;
}
tsmm_mutex = tsrm_mutex_alloc();
1999-08-14 17:35:52 +08:00
tsrm_new_thread_begin_handler = tsrm_new_thread_end_handler = NULL;
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Started up TSRM, %d expected threads, %d expected resources", expected_threads, expected_resources));
1999-04-21 07:58:02 +08:00
return 1;
}
/* Shutdown TSRM (call once for the entire process) */
TSRM_API void tsrm_shutdown(void)
1999-04-21 07:58:02 +08:00
{
int i;
if (tsrm_tls_table) {
for (i=0; i<tsrm_tls_table_size; i++) {
tsrm_tls_entry *p = tsrm_tls_table[i], *next_p;
while (p) {
int j;
next_p = p->next;
for (j=0; j<id_count; j++) {
free(p->storage[j]);
}
free(p->storage);
free(p);
p = next_p;
}
}
free(tsrm_tls_table);
tsrm_tls_table = NULL;
1999-04-21 07:58:02 +08:00
}
if (resource_types_table) {
free(resource_types_table);
resource_types_table=NULL;
1999-04-21 07:58:02 +08:00
}
tsrm_mutex_free(tsmm_mutex);
tsmm_mutex = NULL;
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Shutdown TSRM"));
2000-11-18 10:41:14 +08:00
if (tsrm_error_file!=stderr) {
fclose(tsrm_error_file);
}
#if defined(GNUPTH)
pth_kill();
#elif defined(PTHREADS)
2001-04-05 00:33:06 +08:00
pthread_key_delete( tls_key );
#endif
1999-04-21 07:58:02 +08:00
}
/* allocates a new thread-safe-resource id */
TSRM_API ts_rsrc_id ts_allocate_id(size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
1999-04-21 07:58:02 +08:00
{
ts_rsrc_id new_id;
int i;
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size));
1999-04-21 07:58:02 +08:00
tsrm_mutex_lock(tsmm_mutex);
/* obtain a resource id */
new_id = id_count++;
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", TSRM_SHUFFLE_RSRC_ID(new_id)));
1999-04-21 07:58:02 +08:00
/* store the new resource type in the resource sizes table */
if (resource_types_table_size < id_count) {
resource_types_table = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
if (!resource_types_table) {
tsrm_mutex_unlock(tsmm_mutex);
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
return 0;
1999-04-21 07:58:02 +08:00
}
resource_types_table_size = id_count;
}
resource_types_table[new_id].size = size;
resource_types_table[new_id].ctor = ctor;
resource_types_table[new_id].dtor = dtor;
/* enlarge the arrays for the already active threads */
for (i=0; i<tsrm_tls_table_size; i++) {
tsrm_tls_entry *p = tsrm_tls_table[i];
while (p) {
if (p->count < id_count) {
int j;
1999-04-24 17:01:30 +08:00
p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count);
1999-04-21 07:58:02 +08:00
for (j=p->count; j<id_count; j++) {
p->storage[j] = (void *) malloc(resource_types_table[j].size);
1999-04-23 19:00:02 +08:00
if (resource_types_table[j].ctor) {
resource_types_table[j].ctor(p->storage[j]);
}
1999-04-21 07:58:02 +08:00
}
p->count = id_count;
}
p = p->next;
}
}
tsrm_mutex_unlock(tsmm_mutex);
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", TSRM_SHUFFLE_RSRC_ID(new_id)));
return TSRM_SHUFFLE_RSRC_ID(new_id);
1999-04-21 07:58:02 +08:00
}
static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)
{
int i;
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Creating data structures for thread %x", thread_id));
1999-04-21 07:58:02 +08:00
(*thread_resources_ptr) = (tsrm_tls_entry *) malloc(sizeof(tsrm_tls_entry));
(*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count);
(*thread_resources_ptr)->count = id_count;
(*thread_resources_ptr)->thread_id = thread_id;
(*thread_resources_ptr)->next = NULL;
1999-10-10 21:30:03 +08:00
#if defined(PTHREADS)
2001-04-05 00:33:06 +08:00
/* Set thread local storage to this new thread resources structure */
pthread_setspecific( tls_key, (void *)*thread_resources_ptr );
#elif defined(TSRM_ST)
st_thread_setspecific(tls_key, (void *) *thread_resources_ptr);
#endif
1999-10-10 21:30:03 +08:00
if (tsrm_new_thread_begin_handler) {
tsrm_new_thread_begin_handler(thread_id);
}
1999-04-21 07:58:02 +08:00
for (i=0; i<id_count; i++) {
(*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
1999-04-23 19:00:02 +08:00
if (resource_types_table[i].ctor) {
resource_types_table[i].ctor((*thread_resources_ptr)->storage[i]);
}
1999-04-21 07:58:02 +08:00
}
tsrm_mutex_unlock(tsmm_mutex);
1999-08-14 17:35:52 +08:00
if (tsrm_new_thread_end_handler) {
tsrm_new_thread_end_handler(thread_id);
}
1999-04-21 07:58:02 +08:00
}
/* fetches the requested resource for the current thread */
TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
1999-04-21 07:58:02 +08:00
{
THREAD_T thread_id;
1999-04-21 07:58:02 +08:00
int hash_value;
tsrm_tls_entry *thread_resources;
if (!th_id) {
#if defined(PTHREADS)
2001-04-05 00:33:06 +08:00
/* Fast path for looking up the resources for the current
* thread. Its used by just about every call to
* ts_resource_ex(). This avoids the need for a mutex lock
* and our hashtable lookup.
*/
thread_resources = pthread_getspecific( tls_key );
#elif defined(TSRM_ST)
thread_resources = st_thread_getspecific(tls_key);
#else
thread_resources = NULL;
#endif
if (thread_resources) {
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for current thread %d", id, (long) thread_resources->thread_id ));
2001-04-05 00:33:06 +08:00
return ts_resource_read( thread_resources, id );
}
thread_id = tsrm_thread_id();
} else {
thread_id = *th_id;
2001-04-05 00:33:06 +08:00
}
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for thread %ld", id, (long) thread_id));
1999-04-21 07:58:02 +08:00
tsrm_mutex_lock(tsmm_mutex);
hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
thread_resources = tsrm_tls_table[hash_value];
if (!thread_resources) {
allocate_new_resource(&tsrm_tls_table[hash_value], thread_id);
return ts_resource_ex(id, &thread_id);
1999-04-21 07:58:02 +08:00
} else {
do {
if (thread_resources->thread_id == thread_id) {
break;
}
if (thread_resources->next) {
thread_resources = thread_resources->next;
} else {
allocate_new_resource(&thread_resources->next, thread_id);
return ts_resource_ex(id, &thread_id);
1999-10-10 21:30:03 +08:00
/*
* thread_resources = thread_resources->next;
* break;
*/
1999-04-21 07:58:02 +08:00
}
} while (thread_resources);
}
tsrm_mutex_unlock(tsmm_mutex);
2001-04-05 00:33:06 +08:00
return ts_resource_read( thread_resources, id );
}
1999-04-21 07:58:02 +08:00
/* Read a specific resource from the thread's resources.
* This is called outside of a mutex, so have to be aware about external
* changes to the structure as we read it.
*/
void *ts_resource_read( tsrm_tls_entry *thread_resources, ts_rsrc_id id )
{
void *resource;
1999-04-21 07:58:02 +08:00
resource = TSRM_SAFE_ARRAY_OFFSET(thread_resources->storage, TSRM_UNSHUFFLE_RSRC_ID(id), thread_resources->count);
if (resource) {
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Successfully fetched resource id %d for thread id %ld - %x", id, (long) thread_resources->thread_id, (long) resource));
} else {
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Resource id %d is out of range (%d..%d)", id, TSRM_SHUFFLE_RSRC_ID(0), TSRM_SHUFFLE_RSRC_ID(thread_resources->count-1)));
2001-04-05 00:33:06 +08:00
abort();
}
1999-04-21 07:58:02 +08:00
return resource;
}
/* frees all resources allocated for the current thread */
void ts_free_thread(void)
1999-04-21 07:58:02 +08:00
{
tsrm_tls_entry *thread_resources;
2001-04-05 00:33:06 +08:00
int i;
1999-04-21 07:58:02 +08:00
THREAD_T thread_id = tsrm_thread_id();
int hash_value;
tsrm_tls_entry *last=NULL;
tsrm_mutex_lock(tsmm_mutex);
hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
thread_resources = tsrm_tls_table[hash_value];
while (thread_resources) {
if (thread_resources->thread_id == thread_id) {
for (i=0; i<thread_resources->count; i++) {
1999-04-23 19:00:02 +08:00
if (resource_types_table[i].dtor) {
resource_types_table[i].dtor(thread_resources->storage[i]);
2001-04-05 00:33:06 +08:00
}
}
for (i=0; i<thread_resources->count; i++) {
1999-04-21 07:58:02 +08:00
free(thread_resources->storage[i]);
}
free(thread_resources->storage);
if (last) {
last->next = thread_resources->next;
} else {
tsrm_tls_table[hash_value] = thread_resources->next;
}
#if defined(PTHREADS)
2001-04-05 22:02:08 +08:00
pthread_setspecific(tls_key, 0);
2001-04-05 00:33:06 +08:00
#endif
1999-04-21 07:58:02 +08:00
free(thread_resources);
break;
1999-04-21 07:58:02 +08:00
}
if (thread_resources->next) {
last = thread_resources;
}
1999-05-12 05:27:44 +08:00
thread_resources = thread_resources->next;
1999-04-21 07:58:02 +08:00
}
tsrm_mutex_unlock(tsmm_mutex);
}
/* deallocates all occurrences of a given id */
void ts_free_id(ts_rsrc_id id)
{
}
/*
* Utility Functions
*/
/* Obtain the current thread id */
1999-08-14 17:35:52 +08:00
TSRM_API THREAD_T tsrm_thread_id(void)
1999-04-21 07:58:02 +08:00
{
2000-09-02 23:03:19 +08:00
#ifdef TSRM_WIN32
1999-04-21 07:58:02 +08:00
return GetCurrentThreadId();
#elif defined(GNUPTH)
return pth_self();
1999-04-21 07:58:02 +08:00
#elif defined(PTHREADS)
return pthread_self();
#elif defined(NSAPI)
return systhread_current();
#elif defined(PI3WEB)
return PIThread_getCurrent();
#elif defined(TSRM_ST)
return st_thread_self();
1999-04-21 07:58:02 +08:00
#endif
}
/* Allocate a mutex */
TSRM_API MUTEX_T tsrm_mutex_alloc(void)
1999-04-21 07:58:02 +08:00
{
MUTEX_T mutexp;
2000-09-02 23:03:19 +08:00
#ifdef TSRM_WIN32
mutexp = malloc(sizeof(CRITICAL_SECTION));
InitializeCriticalSection(mutexp);
#elif defined(GNUPTH)
mutexp = (MUTEX_T) malloc(sizeof(*mutexp));
pth_mutex_init(mutexp);
1999-04-21 07:58:02 +08:00
#elif defined(PTHREADS)
mutexp = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
pthread_mutex_init(mutexp,NULL);
#elif defined(NSAPI)
mutexp = crit_init();
#elif defined(PI3WEB)
mutexp = PIPlatform_allocLocalMutex();
#elif defined(TSRM_ST)
mutexp = st_mutex_new();
1999-04-21 07:58:02 +08:00
#endif
#ifdef THR_DEBUG
printf("Mutex created thread: %d\n",mythreadid());
#endif
return( mutexp );
}
/* Free a mutex */
TSRM_API void tsrm_mutex_free(MUTEX_T mutexp)
1999-04-21 07:58:02 +08:00
{
if (mutexp) {
2000-09-02 23:03:19 +08:00
#ifdef TSRM_WIN32
DeleteCriticalSection(mutexp);
#elif defined(GNUPTH)
free(mutexp);
1999-04-21 07:58:02 +08:00
#elif defined(PTHREADS)
1999-11-26 23:33:42 +08:00
pthread_mutex_destroy(mutexp);
free(mutexp);
1999-04-21 07:58:02 +08:00
#elif defined(NSAPI)
crit_terminate(mutexp);
#elif defined(PI3WEB)
PISync_delete(mutexp);
#elif defined(TSRM_ST)
st_mutex_destroy(mutexp);
1999-04-21 07:58:02 +08:00
#endif
}
#ifdef THR_DEBUG
printf("Mutex freed thread: %d\n",mythreadid());
#endif
}
/* Lock a mutex */
TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp)
1999-04-21 07:58:02 +08:00
{
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex locked thread: %ld", tsrm_thread_id()));
2000-09-02 23:03:19 +08:00
#ifdef TSRM_WIN32
EnterCriticalSection(mutexp);
return 1;
#elif defined(GNUPTH)
return pth_mutex_acquire(mutexp, 0, NULL);
1999-04-21 07:58:02 +08:00
#elif defined(PTHREADS)
return pthread_mutex_lock(mutexp);
#elif defined(NSAPI)
return crit_enter(mutexp);
#elif defined(PI3WEB)
return PISync_lock(mutexp);
#elif defined(TSRM_ST)
return st_mutex_lock(mutexp);
1999-04-21 07:58:02 +08:00
#endif
}
/* Unlock a mutex */
TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp)
1999-04-21 07:58:02 +08:00
{
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex unlocked thread: %ld", tsrm_thread_id()));
2000-09-02 23:03:19 +08:00
#ifdef TSRM_WIN32
LeaveCriticalSection(mutexp);
return 1;
#elif defined(GNUPTH)
return pth_mutex_release(mutexp);
1999-04-21 07:58:02 +08:00
#elif defined(PTHREADS)
return pthread_mutex_unlock(mutexp);
#elif defined(NSAPI)
return crit_exit(mutexp);
#elif defined(PI3WEB)
return PISync_unlock(mutexp);
#elif defined(TSRM_ST)
return st_mutex_unlock(mutexp);
1999-04-21 07:58:02 +08:00
#endif
}
1999-08-14 17:35:52 +08:00
TSRM_API void *tsrm_set_new_thread_begin_handler(void (*new_thread_begin_handler)(THREAD_T thread_id))
{
void *retval = (void *) tsrm_new_thread_begin_handler;
tsrm_new_thread_begin_handler = new_thread_begin_handler;
return retval;
}
TSRM_API void *tsrm_set_new_thread_end_handler(void (*new_thread_end_handler)(THREAD_T thread_id))
{
void *retval = (void *) tsrm_new_thread_end_handler;
tsrm_new_thread_end_handler = new_thread_end_handler;
return retval;
}
1999-04-21 07:58:02 +08:00
/*
* Debug support
*/
2000-11-18 10:41:14 +08:00
#if TSRM_DEBUG
int tsrm_error(int level, const char *format, ...)
1999-04-21 07:58:02 +08:00
{
if (level<=tsrm_error_level) {
1999-04-21 07:58:02 +08:00
va_list args;
int size;
2000-11-18 10:41:14 +08:00
fprintf(tsrm_error_file, "TSRM: ");
1999-04-21 07:58:02 +08:00
va_start(args, format);
2000-11-18 10:41:14 +08:00
size = vfprintf(tsrm_error_file, format, args);
1999-04-21 07:58:02 +08:00
va_end(args);
2000-11-18 10:41:14 +08:00
fprintf(tsrm_error_file, "\n");
fflush(tsrm_error_file);
1999-04-21 07:58:02 +08:00
return size;
} else {
return 0;
}
}
2000-11-18 10:41:14 +08:00
#endif
1999-04-21 07:58:02 +08:00
2000-11-18 10:41:14 +08:00
void tsrm_error_set(int level, char *debug_filename)
1999-04-21 07:58:02 +08:00
{
tsrm_error_level = level;
2000-11-18 10:41:14 +08:00
#if TSRM_DEBUG
if (tsrm_error_file!=stderr) { /* close files opened earlier */
fclose(tsrm_error_file);
}
if (debug_filename) {
tsrm_error_file = fopen(debug_filename, "w");
if (!tsrm_error_file) {
tsrm_error_file = stderr;
}
} else {
tsrm_error_file = stderr;
}
#endif
1999-04-24 17:01:30 +08:00
}
#endif /* ZTS */