/* deallocates all occurrences of a given id */ void ts_free_id(ts_rsrc_id id) {/*{{{*/ int i; int j = TSRM_UNSHUFFLE_RSRC_ID(id); tsrm_mutex_lock(tsmm_mutex); TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Freeing resource id %d", id)); if (tsrm_tls_table) { for (i=0; i<tsrm_tls_table_size; i++) { tsrm_tls_entry *p = tsrm_tls_table[i]; while (p) { if (p->count > j && p->storage[j]) { if (resource_types_table && resource_types_table[j].dtor) { resource_types_table[j].dtor(p->storage[j]); } free(p->storage[j]); p->storage[j] = NULL; } p = p->next; } } } resource_types_table[j].done = 1; tsrm_mutex_unlock(tsmm_mutex); TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully freed resource id %d", id)); }/*}}}*/
/* fetches the requested resource for the current thread */ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) {/*{{{*/ THREAD_T thread_id; int hash_value; tsrm_tls_entry *thread_resources; if (!th_id) { /* 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 = tsrm_tls_get(); if (thread_resources) { TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for current thread %d", id, (long) thread_resources->thread_id)); /* 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. */ TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count); } thread_id = tsrm_thread_id(); } else { thread_id = *th_id; } TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for thread %ld", id, (long) thread_id)); 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); } 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); /* * thread_resources = thread_resources->next; * break; */ } } while (thread_resources); } tsrm_mutex_unlock(tsmm_mutex); /* 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. */ TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count); }/*}}}*/
/* allocates a new thread-safe-resource id */ TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) {/*{{{*/ int i; TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size)); tsrm_mutex_lock(tsmm_mutex); /* obtain a resource id */ *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++); TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id)); /* store the new resource type in the resource sizes table */ if (resource_types_table_size < id_count) { tsrm_resource_type *_tmp; _tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count); if (!_tmp) { tsrm_mutex_unlock(tsmm_mutex); TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource")); *rsrc_id = 0; return 0; } resource_types_table = _tmp; resource_types_table_size = id_count; } resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0; /* 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; p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count); for (j=p->count; j<id_count; j++) { p->storage[j] = (void *) malloc(resource_types_table[j].size); if (resource_types_table[j].ctor) { resource_types_table[j].ctor(p->storage[j]); } } p->count = id_count; } p = p->next; } } tsrm_mutex_unlock(tsmm_mutex); TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id)); return *rsrc_id; }/*}}}*/
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)); (*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; /* Set thread local storage to this new thread resources structure */ tsrm_tls_set(*thread_resources_ptr); if (tsrm_new_thread_begin_handler) { tsrm_new_thread_begin_handler(thread_id, &((*thread_resources_ptr)->storage)); } for (i=0; i<id_count; i++) { if (resource_types_table[i].done) { (*thread_resources_ptr)->storage[i] = NULL; } else { (*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size); if (resource_types_table[i].ctor) { resource_types_table[i].ctor((*thread_resources_ptr)->storage[i], &(*thread_resources_ptr)->storage); } } } if (tsrm_new_thread_end_handler) { tsrm_new_thread_end_handler(thread_id, &((*thread_resources_ptr)->storage)); } tsrm_mutex_unlock(tsmm_mutex); }
/* Unlock a mutex. A return value of 0 indicates success */ TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp) { TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex unlocked thread: %ld", tsrm_thread_id())); #ifdef TSRM_WIN32 LeaveCriticalSection(mutexp); return 0; #elif defined(GNUPTH) if (pth_mutex_release(mutexp)) { return 0; } return -1; #elif defined(PTHREADS) return pthread_mutex_unlock(mutexp); #elif defined(NSAPI) crit_exit(mutexp); return 0; #elif defined(PI3WEB) return PISync_unlock(mutexp); #elif defined(TSRM_ST) return st_mutex_unlock(mutexp); #elif defined(BETHREADS) if (atomic_add(&mutexp->ben, -1) != 1) return release_sem(mutexp->sem); return 0; #endif }
/* Lock a mutex. A return value of 0 indicates success */ TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp) { TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex locked thread: %ld", tsrm_thread_id())); #ifdef TSRM_WIN32 EnterCriticalSection(mutexp); return 0; #elif defined(GNUPTH) if (pth_mutex_acquire(mutexp, 0, NULL)) { return 0; } return -1; #elif defined(PTHREADS) return pthread_mutex_lock(mutexp); #elif defined(NSAPI) crit_enter(mutexp); return 0; #elif defined(PI3WEB) return PISync_lock(mutexp); #elif defined(TSRM_ST) return st_mutex_lock(mutexp); #elif defined(BETHREADS) if (atomic_add(&mutexp->ben, 1) != 0) return acquire_sem(mutexp->sem); return 0; #endif }
/* Shutdown TSRM (call once for the entire process) */ TSRM_API void tsrm_shutdown(void) {/*{{{*/ int i; if (!in_main_thread) { /* ensure singleton */ return; } 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<p->count; j++) { if (p->storage[j]) { if (resource_types_table && !resource_types_table[j].done && resource_types_table[j].dtor) { resource_types_table[j].dtor(p->storage[j]); } free(p->storage[j]); } } free(p->storage); free(p); p = next_p; } } free(tsrm_tls_table); tsrm_tls_table = NULL; } if (resource_types_table) { free(resource_types_table); resource_types_table=NULL; } tsrm_mutex_free(tsmm_mutex); tsmm_mutex = NULL; TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Shutdown TSRM")); if (tsrm_error_file!=stderr) { fclose(tsrm_error_file); } #if defined(GNUPTH) pth_kill(); #elif defined(PTHREADS) pthread_setspecific(tls_key, 0); pthread_key_delete(tls_key); #elif defined(TSRM_WIN32) TlsFree(tls_key); #endif if (tsrm_shutdown_handler) { tsrm_shutdown_handler(); } tsrm_new_thread_begin_handler = NULL; tsrm_new_thread_end_handler = NULL; tsrm_shutdown_handler = NULL; }/*}}}*/
TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) { TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size)); /* obtain a resource id */ HPHP::resource_types_table.resize(HPHP::resource_types_table.size() + 1); *rsrc_id = TSRM_SHUFFLE_RSRC_ID(HPHP::resource_types_table.size() - 1); TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id)); /* store the new resource type in the resource sizes table */ HPHP::resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size; HPHP::resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor; HPHP::resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor; HPHP::resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0; TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id)); return *rsrc_id; }
TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset) {/*{{{*/ TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Changed sigmask in thread: %ld", tsrm_thread_id())); /* TODO: add support for other APIs */ #ifdef PTHREADS return pthread_sigmask(how, set, oldset); #else return sigprocmask(how, set, oldset); #endif }/*}}}*/
/* Startup TSRM (call once for the entire process) */ TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename) { #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); #elif defined(TSRM_WIN32) tls_key = TlsAlloc(); #elif defined(BETHREADS) tls_key = tls_allocate(); #endif tsrm_error_file = stderr; tsrm_error_set(debug_level, debug_filename); tsrm_tls_table_size = expected_threads; 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")); 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")); free(tsrm_tls_table); tsrm_tls_table = NULL; return 0; } tsmm_mutex = tsrm_mutex_alloc(); 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)); return 1; }
/* Unlock a mutex. A return value of 0 indicates success */ TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp) {/*{{{*/ TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex unlocked thread: %ld", tsrm_thread_id())); #ifdef TSRM_WIN32 LeaveCriticalSection(mutexp); return 0; #elif defined(GNUPTH) if (pth_mutex_release(mutexp)) { return 0; } return -1; #elif defined(PTHREADS) return pthread_mutex_unlock(mutexp); #elif defined(TSRM_ST) return st_mutex_unlock(mutexp); #endif }/*}}}*/
/* fetches the requested resource for the current thread */ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) { THREAD_T thread_id; int hash_value; tsrm_tls_entry *thread_resources; #ifdef NETWARE /* The below if loop is added for NetWare to fix an abend while unloading PHP * when an Apache unload command is issued on the system console. * While exiting from PHP, at the end for some reason, this function is called * with tsrm_tls_table = NULL. When this happened, the server abends when * tsrm_tls_table is accessed since it is NULL. */ if(tsrm_tls_table) { #endif if (!th_id) { /* 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 = tsrm_tls_get(); if (thread_resources) { TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for current thread %d", id, (long) thread_resources->thread_id)); /* 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. */ TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count); } thread_id = tsrm_thread_id(); } else { thread_id = *th_id; } TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for thread %ld", id, (long) thread_id)); 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); } 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); /* * thread_resources = thread_resources->next; * break; */ } } while (thread_resources); } tsrm_mutex_unlock(tsmm_mutex); /* 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. */ TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count); #ifdef NETWARE } /* if(tsrm_tls_table) */ #endif }