blob: 7b18f92e354a66a6505ccf7f2b07b45dece6976f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file thread_init.c
* @brief hythread init/shutdown functions
*/
#undef LOG_DOMAIN
#define LOG_DOMAIN "tm.init"
#include <open/hythread_ext.h>
#include "port_mutex.h"
#include "thread_private.h"
#ifdef HY_NO_THR
#include "thread_classlib_defs.h"
#endif /* HY_NO_THR */
//global constants:
// Global pointer to the threading library
hythread_library_t TM_LIBRARY = NULL;
//Thread manager memory pool
apr_pool_t *TM_POOL = NULL;
//TLS key
apr_threadkey_t *TM_THREAD_KEY;
//Thread manager global lock
osmutex_t TM_START_LOCK;
static int hythread_library_state = TM_LIBRARY_STATUS_NOT_INITIALIZED;
#define GLOBAL_MONITOR_NAME "global_monitor"
hythread_monitor_t p_global_monitor;
//group for the threads created/attached to the NULL group
hythread_group_t TM_DEFAULT_GROUP;
hythread_group_t group_list;
IDATA groups_count;
static IDATA init_group_list();
static IDATA destroy_group_list();
#ifdef WIN32
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpres) {
if (dwReason == DLL_PROCESS_ATTACH) {
hythread_lib_create(&TM_LIBRARY);
}
return TRUE;
}
#else
void hythread_library_init(void) {
hythread_lib_create(&TM_LIBRARY);
}
#endif
#ifdef HY_NO_THR
/**
* Determine the size of the thread library.
*
* Given a thread library version, return the size of the structure in bytes
* required to be allocated.
*
* @param[in] version The HyThreadLibraryVersion structure.
* @return size of thread library on success, zero on failure
*/
UDATA VMCALL
hythread_getSize (struct HyThreadLibraryVersion * version)
{
/* Can't initialize a structure that is not understood by this version of the thread library */
if (HYTHREAD_MAJOR_VERSION_NUMBER != version->majorVersionNumber)
{
return 0;
}
return sizeof (HyThreadLibrary);
}
/*
* Stub startup function
*/
HY_CFUNC THREXPORT I_32 VMCALL
hythread_startup_library (struct HyThreadLibraryInternal *threadLibrary)
{
/* Do nothing here - thread library initialised by hythread_allocate_library() */
return 0;
}
/*
* Shutdown the thread library - forwards call to hythread_shutdown()
*/
HY_CFUNC THREXPORT I_32 VMCALL
hythread_shutdown_library (struct HyThreadLibraryInternal *threadLibrary)
{
hythread_shutdown();
return 0;
}
/*
* Allocate the thread library function table and return it's pointer
*/
HY_CFUNC THREXPORT I_32 VMCALL
hythread_allocate_library (struct HyThreadLibraryVersion *expectedVersion,
struct HyThreadLibrary **threadLibraryFuncs)
{
UDATA size = hythread_getSize (expectedVersion);
HyThreadLibrary *threadLib;
if (0 == size)
{
return -1;
}
hythread_lib_create(&TM_LIBRARY);
/* Allocate memory for the function table */
*threadLibraryFuncs = NULL;
threadLib = (HyThreadLibrary*) apr_palloc(TM_POOL, sizeof(HyThreadLibrary));
if (NULL == threadLib)
{
return -1;
}
/* Null and initialize the table passed in */
memset(threadLib, 0, size);
memcpy(threadLib, &MasterThreadLibraryTable, size);
/* Set version numbers */
threadLib->threadVersion.majorVersionNumber = expectedVersion->majorVersionNumber;
threadLib->threadVersion.minorVersionNumber = expectedVersion->minorVersionNumber;
threadLib->threadVersion.capabilities = HYTHREAD_CAPABILITY_MASK;
threadLib->self_handle = threadLib;
*threadLibraryFuncs = threadLib;
return 0;
}
#endif /* HY_NO_THR */
/**
* Creates and initializes a threading library.
*
* @param[out] lib pointer to the created thread library
* @return The thread library's initStatus will be set to 0 on success or
* a negative value on failure.
*/
IDATA VMCALL hythread_lib_create(hythread_library_t * lib) {
apr_status_t apr_status;
// Current implementation doesn't support more than one library instance.
if (TM_LIBRARY) {
*lib = TM_LIBRARY;
return TM_ERROR_NONE;
}
apr_status = apr_initialize();
assert(apr_status == APR_SUCCESS);
apr_status = apr_pool_create(&TM_POOL, NULL);
if (apr_status != APR_SUCCESS) return CONVERT_ERROR(apr_status);
*lib = (hythread_library_t) apr_palloc(TM_POOL, sizeof(HyThreadLibraryInternal));
if (*lib == NULL) return TM_ERROR_OUT_OF_MEMORY;
hythread_init(*lib);
return TM_ERROR_NONE;
}
/**
* Shut down the threading library.
*
* @param lib the library
* @return none
*
* @see hythread_lib_create
*/
void VMCALL hythread_lib_destroy(hythread_library_t lib) {
apr_pool_destroy(TM_POOL);
}
/**
* Initialize a threading library.
*
* @note This must only be called once.
*
* If any OS threads were created before calling this function, they must be attached using
* hythread_attach before accessing any thread library functions.
*
* @param[in] lib pointer to the thread library to be initialized (non-NULL)
* @return The thread library's initStatus will be set to 0 on success or
* a negative value on failure.
*/
void VMCALL hythread_init(hythread_library_t lib) {
apr_status_t apr_status;
IDATA status;
hythread_monitor_t *mon;
// Current implementation doesn't support more than one library instance.
if (TM_LIBRARY == NULL) {
TM_LIBRARY = lib;
}
assert(TM_LIBRARY == lib);
if (hythread_library_state != TM_LIBRARY_STATUS_NOT_INITIALIZED)
return;
hythread_library_state = TM_LIBRARY_STATUS_INITIALIZED;
apr_status = apr_initialize();
assert(apr_status == APR_SUCCESS);
// TM_POOL will be NULL if hythread_lib_create was not used to create the library
if (TM_POOL == NULL) {
apr_status = apr_pool_create(&TM_POOL, NULL);
assert(apr_status == APR_SUCCESS);
}
apr_status = apr_threadkey_private_create(&TM_THREAD_KEY, NULL, TM_POOL);
assert(apr_status == APR_SUCCESS);
status = port_mutex_create(&lib->TM_LOCK, APR_THREAD_MUTEX_NESTED);
assert(status == TM_ERROR_NONE);
status = port_mutex_create(&TM_START_LOCK, APR_THREAD_MUTEX_NESTED);
assert(status == TM_ERROR_NONE);
status = init_group_list();
assert(status == TM_ERROR_NONE);
// Create default group - hosts any thread crated with NULL group
status = hythread_group_create(&TM_DEFAULT_GROUP);
assert(status == TM_ERROR_NONE);
//nondaemon thread barrier
////
lib->nondaemon_thread_count = 0;
status = hycond_create(&lib->nondaemon_thread_cond);
assert(status == TM_ERROR_NONE);
// init global monitor
status=hythread_monitor_init_with_name(&p_global_monitor, 0, "Thread Global Monitor");
assert(status == TM_ERROR_NONE);
mon = (hythread_monitor_t*)hythread_global(GLOBAL_MONITOR_NAME);
*mon = p_global_monitor;
assert(mon);
}
/**
* Prepares to shutdown the hythread library.
*
* @return none
*
* @see hythread_init
*/
void VMCALL hythread_shutdowning() {
hythread_library_state = TM_LIBRARY_STATUS_SHUTDOWN;
}
/**
* Returns hythread library state
*/
int VMCALL hythread_lib_state() {
return hythread_library_state;
}
/**
* Shut down the threading library associated with the current thread.
*
* @return none
*
* @see hythread_init
*/
void VMCALL hythread_shutdown() {
hythread_lib_destroy(hythread_self()->library);
}
/**
* Acquires global lock of the library associated with the current thread.
*
* @param[in] self current thread
*/
void VMCALL hythread_lib_lock(hythread_t self) {
IDATA status;
assert(self == hythread_self());
status = port_mutex_lock(&self->library->TM_LOCK);
assert(status == TM_ERROR_NONE);
}
/**
* Releases global lock of the library associated with the current thread.
*
* @param[in] self current thread
*/
void VMCALL hythread_lib_unlock(hythread_t self) {
IDATA status;
assert(self == hythread_self());
status = port_mutex_unlock(&self->library->TM_LOCK);
assert(status == TM_ERROR_NONE);
}
/**
* Acquires the lock over threading subsystem.
*
* The lock blocks new thread creation and thread exit operations.
*/
IDATA VMCALL hythread_global_lock() {
IDATA status;
hythread_t self = hythread_self();
// we need not care about suspension if the thread
// is not even attached to hythread
if (self == NULL) {
return port_mutex_lock(&TM_LIBRARY->TM_LOCK);
}
// disable_count must be 0 on potentially
// blocking operation to prevent suspension deadlocks,
// meaning that the thread is safe for suspension
assert(hythread_is_suspend_enabled());
status = port_mutex_lock(&TM_LIBRARY->TM_LOCK);
assert(status == TM_ERROR_NONE);
// make sure we do not get a global thread lock
// while being requested to suspend
while (self->suspend_count) {
// give up global thread lock before safepoint,
// because this thread can be suspended at a safepoint
status = port_mutex_unlock(&TM_LIBRARY->TM_LOCK);
assert(status == TM_ERROR_NONE);
hythread_safe_point();
status = port_mutex_lock(&TM_LIBRARY->TM_LOCK);
assert(status == TM_ERROR_NONE);
}
return TM_ERROR_NONE;
}
/**
* Releases the lock over threading subsystem.
*
*/
IDATA VMCALL hythread_global_unlock() {
IDATA status;
assert(!hythread_self() || hythread_is_suspend_enabled());
status = port_mutex_unlock(&TM_LIBRARY->TM_LOCK);
assert(status == TM_ERROR_NONE);
return TM_ERROR_NONE;
}
hythread_group_t VMCALL get_java_thread_group(void) {
return TM_DEFAULT_GROUP;
}
static IDATA init_group_list() {
// Initial group, does not contain any actual group, but serves
//as a head and a tail of this list;
hythread_group_t dummy;
//this group will exist as long as TM lives, so it's ok to have
//the same pool for them
////
dummy = (hythread_group_t)apr_pcalloc(TM_POOL, sizeof(HyThreadGroup));
assert(dummy);
dummy->next = dummy->prev = dummy;
group_list = dummy;
groups_count = 0;
lock_table = (HyFatLockTable *) malloc (sizeof(HyFatLockTable));
memset(lock_table, 0, sizeof(HyFatLockTable));
lock_table->tables[0] = (hythread_monitor_t *)calloc(HY_FAT_TABLE_ENTRIES,
sizeof(hythread_monitor_t));
lock_table->live_objs = (unsigned char *)calloc(HY_FAT_TABLE_ENTRIES,
sizeof(unsigned char));
lock_table->size = HY_FAT_TABLE_ENTRIES;
lock_table->array_cursor = 0;
assert (lock_table);
assert (lock_table->tables[0]);
assert (lock_table->live_objs);
if (port_mutex_create(&lock_table->mutex, APR_THREAD_MUTEX_NESTED)) {
return TM_ERROR_OUT_OF_MEMORY;
}
if (hycond_create(&lock_table->write)) {
return TM_ERROR_OUT_OF_MEMORY;
}
if (hycond_create(&lock_table->read)) {
return TM_ERROR_OUT_OF_MEMORY;
}
lock_table->readers_reading = 0;
lock_table->readers_waiting = 0;
lock_table->writers_waiting = 0;
lock_table->state = HYTHREAD_LOCKTABLE_IDLE;
return TM_ERROR_NONE;
}
static IDATA destroy_group_list() {
hythread_group_t cur;
IDATA status,status2;
int i;
// This method works only if there are no running threads.
// there is no good way to kill running threads
status=hythread_global_lock();
if (status != TM_ERROR_NONE) return status;
cur = group_list->next;
status = TM_ERROR_NONE;
while (cur != group_list) {
if (hythread_group_release(cur) == TM_ERROR_NONE) {
cur = group_list->next;
} else {
status = TM_ERROR_RUNNING_THREADS;
cur = cur->next;
}
}
free(lock_table->live_objs);
for (i = 0; i < HY_MAX_FAT_TABLES && lock_table->tables[i]; i++) {
free(lock_table->tables[i]);
}
port_mutex_destroy(&lock_table->mutex);
hycond_destroy(&lock_table->write);
hycond_destroy(&lock_table->read);
free(lock_table);
status2=hythread_global_unlock();
if (status2 != TM_ERROR_NONE) return status2;
return status;
}
IDATA acquire_start_lock() {
return port_mutex_lock(&TM_START_LOCK);
}
IDATA release_start_lock() {
return port_mutex_unlock(&TM_START_LOCK);
}
/*
// very simple Map implementation
// current scenario use only one global so it works well
// need to be hashtable in the future
*/
#define TABLE_SIZE 256
const char *names[TABLE_SIZE];
UDATA data[TABLE_SIZE];
int size = 0;
/*
* return index in array if found, -1 otherwise
*/
int find_entry (const char* name) {
// quick pass
int i;
for (i = 0; i < size; i++) {
if (names[i] == name) {
return i;
}
}
// strcmp pass.
for (i = 0; i < size; i++) {
if (strcmp(names[i], name) == 0) {
return i;
}
}
return -1;
}
//add entry to the end of the array
// return new entry index, -1 if failed.
int add_entry(const char* name) {
int index = size++;
if (index >= TABLE_SIZE-1) {
return -1;
}
names[index] = name;
data[index] = 0;
return index;
}
/**
* Fetch or create a 'named global'.
*
* Return a pointer to the data associated with a named global with the specified name.<br>
* A new named global is created if a named global with the specified name can't be found.
*
* @param[in] name name of named global to read/create
* @return a pointer to a UDATA associated with name<br>
* 0 on failure.
*
*/
UDATA* VMCALL hythread_global (const char* name) {
//hythread_monitor_enter(*p_global_monitor);
int index = find_entry(name);
if (index == -1) {
index = add_entry(name);
assert(index >=0);
if (index < 0) {
//hythread_monitor_exit(*p_global_monitor);
return NULL;
}
}
//hythread_monitor_exit(*p_global_monitor);
return data+index;
}