--- gcv4\src\gc_thread.h 2006-10-16 13:07:10.000000000 +-0600 +++ gcv4\src\gc_thread.h 2006-12-19 11:40:23.000000000 +-0600 @@ -116,12 +116,16 @@ void reset(bool); void wait_for_work(); void signal_work_is_done(); + void send_shutdown_request(); + + bool is_shutdown_requested() { return _gc_shutdown_request; } + inline gc_thread_action get_task_to_do() { return _task_to_do; } inline void set_task_to_do(gc_thread_action task) { _task_to_do = task; @@ -192,12 +196,14 @@ return stash; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// private: + // true if current thread was requested to stop, false otherwise + volatile bool _gc_shutdown_request; int _sweep_start_index; int _num_chunks_to_sweep; #ifdef _DEBUG chunk_sweep_stats _sweep_stats[GC_MAX_CHUNKS]; --- gcv4\src\gc_threads.cpp 2006-10-16 13:07:09.000000000 +-0600 +++ gcv4\src\gc_threads.cpp 2006-12-19 12:30:43.000000000 +-0600 @@ -60,12 +60,13 @@ extern bool mark_scan_load_balanced; extern bool cross_block_compaction; extern bool sweeps_during_gc; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void gc_thread_shutdown_callback(void * data); int __cdecl gc_thread_func (void *arg); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// GC_Mark_Activity::GC_Mark_Activity(Garbage_Collector *p_gc) { assert(p_gc); _p_gc = p_gc; @@ -99,12 +100,13 @@ } GC_Thread::GC_Thread(Garbage_Collector *p_gc, int gc_thread_id) : GC_Mark_Activity(p_gc) { + _gc_shutdown_request = false; _id = gc_thread_id; stash = NULL; if (sweeps_during_gc) { @@ -120,13 +122,14 @@ assert(stat == TM_ERROR_NONE); ////////////////// stat = hysem_create(&_gc_thread_work_done_event, 0, 1); assert(stat == TM_ERROR_NONE); _thread_handle=NULL; - stat = hythread_create_with_group(&_thread_handle, get_thread_group(), 0, 0, 0, gc_thread_func, this); + stat = hythread_create_with_group(&_thread_handle, get_thread_group(), 0, 0, 0, + gc_thread_func, this, gc_thread_shutdown_callback, this); if (stat != TM_ERROR_NONE) { DIE("GC_Thread::GC_Thread(..): CreateThread() failed...exiting..."); } // Reset every GC _num_bytes_recovered_by_sweep = 0; @@ -186,26 +189,42 @@ { // I am done with my job. SIGNAL MASTER THREAD IDATA UNUSED sstat = hysem_post(_gc_thread_work_done_event); assert(sstat == TM_ERROR_NONE); } +void +GC_Thread::send_shutdown_request() { + _gc_shutdown_request = true; + IDATA UNUSED wstat = hysem_post(_gc_thread_start_work_event); + assert(wstat == TM_ERROR_NONE); +} volatile POINTER_SIZE_INT dummy_for_good_cache_performance = 0; +void gc_thread_shutdown_callback(void * data) { + GC_Thread *p_gc_thread = (GC_Thread *) data; + p_gc_thread->send_shutdown_request(); +} int __cdecl gc_thread_func (void *arg) { GC_Thread *p_gc_thread = (GC_Thread *) arg; assert(p_gc_thread); apr_time_t task_start_time, task_end_time; while(true) { //I will go to sleep forever or until the next time I am woken up with work p_gc_thread->wait_for_work(); + + if (p_gc_thread->is_shutdown_requested()) { + return 0; + } + + // When thread is woken up, it has work to do. assert(p_gc_thread->get_task_to_do() != GC_BOGUS_TASK); if (p_gc_thread->get_task_to_do() == GC_MARK_SCAN_TASK) { gc_time_start_hook(&task_start_time); --- include\open\hythread_ext.h 2006-12-08 18:48:39.000000000 +-0600 +++ include\open\hythread_ext.h 2006-12-19 11:19:12.000000000 +-0600 @@ -134,44 +134,53 @@ typedef struct HyThread *hythread_iterator_t; typedef struct HyThreadLibrary *hythread_library_t; typedef I_32 hythread_thin_monitor_t; typedef void (*hythread_event_callback_proc)(void); +typedef void (* hythread_shutdown_callback_t)(void *); + //@} /** @name Thread Manager initialization / shutdown */ //@{ //temporary, should be static for the file containing dinamic library //initialization //// +#define TM_LIBRARY_STATE_GET 0 +#define TM_LIBRARY_STATE_INITIALIZED 1 +#define TM_LIBRARY_STATE_BLOCKED 2 + IDATA VMCALL hythread_global_lock(); IDATA VMCALL hythread_global_unlock(); void VMCALL hythread_init(hythread_library_t lib); void VMCALL hythread_shutdown(); IDATA VMCALL hythread_lib_create(hythread_library_t * lib); void VMCALL hythread_lib_destroy(hythread_library_t lib); +IDATA VMCALL hythread_lib_set_state(hythread_library_t lib, IDATA state); //@} /** @name Basic manipulation */ //@{ -IDATA VMCALL hythread_attach_ex(hythread_t *handle, hythread_library_t lib); -IDATA VMCALL hythread_attach_to_group(hythread_t *handle, hythread_library_t lib, hythread_group_t group); -IDATA hythread_create_with_group(hythread_t *ret_thread, hythread_group_t group, UDATA stacksize, UDATA priority, UDATA suspend, hythread_entrypoint_t func, void *data); +IDATA VMCALL hythread_attach_ex(hythread_t *handle, hythread_library_t lib, hythread_shutdown_callback_t callback, void * callback_data); +IDATA VMCALL hythread_attach_to_group(hythread_t *handle, hythread_library_t lib, hythread_group_t group, hythread_shutdown_callback_t callback, void * callback_data); +IDATA hythread_create_ex(hythread_t *ret_thread, UDATA stacksize, UDATA priority, UDATA suspend, hythread_entrypoint_t func, void *data, hythread_shutdown_callback_t callback, void * callback_data); +IDATA hythread_create_with_group(hythread_t *ret_thread, hythread_group_t group, UDATA stacksize, UDATA priority, UDATA suspend, hythread_entrypoint_t func, void *data, hythread_shutdown_callback_t callback, void * callback_data); UDATA VMCALL hythread_clear_interrupted_other(hythread_t thread); IDATA VMCALL hythread_join(hythread_t t); IDATA VMCALL hythread_join_timed(hythread_t t, I_64 millis, IDATA nanos); IDATA VMCALL hythread_join_interruptable(hythread_t t, I_64 millis, IDATA nanos); IDATA VMCALL hythread_get_self_id(); IDATA VMCALL hythread_get_id(hythread_t t); hythread_t VMCALL hythread_get_thread(IDATA id); IDATA VMCALL hythread_struct_init(hythread_t *ret_thread); +IDATA VMCALL hythread_cancel_safe(hythread_t thread); IDATA VMCALL hythread_cancel_all(hythread_group_t group); IDATA hythread_group_create(hythread_group_t *group); IDATA VMCALL hythread_group_release(hythread_group_t group); IDATA VMCALL hythread_group_get_list(hythread_group_t **list, int* size); void* VMCALL hythread_get_private_data(hythread_t t); IDATA VMCALL hythread_set_private_data(hythread_t t, void* data); --- tests\unit\thread\test_native_basic.c 2006-11-24 19:34:16.000000000 +-0600 +++ tests\unit\thread\test_native_basic.c 2006-12-18 19:39:05.000000000 +-0600 @@ -49,13 +49,13 @@ hythread_group_create((hythread_group_t *)&args[0]); args[1] = apr_palloc(pool, sizeof(jthread_threadattr_t)); ((jthread_threadattr_t *)args[1])->stacksize = 1024; ((jthread_threadattr_t *)args[1])->priority = 1; - hythread_create_with_group(&thread, args[0], 1024, 1, 0, start_proc, args); + hythread_create_with_group(&thread, args[0], 1024, 1, 0, start_proc, args, NULL, NULL); hythread_join(thread); // TODO: should check that created thread finished without errors. return TEST_PASSED; } @@ -72,13 +72,13 @@ hythread_group_create(&group); hylatch_create(&start, n); hylatch_create(&end, 1); for (i = 0; i < n; i++) { thread = NULL; - hythread_create_with_group(&thread, group, 0, 0, 0, start_proc_empty, NULL); + hythread_create_with_group(&thread, group, 0, 0, 0, start_proc_empty, NULL, NULL, NULL); } // Wait util all threads have started. hylatch_wait(start); iterator = hythread_iterator_create(group); // Notify all threads @@ -160,13 +160,13 @@ args[1] = apr_palloc(pool, sizeof(jthread_threadattr_t)); ((jthread_threadattr_t *)args[1])->stacksize = 1024; ((jthread_threadattr_t *)args[1])->priority = 1; thread = NULL; - hythread_create_with_group(&thread, group, 1024, 1, 0, start_proc, args); + hythread_create_with_group(&thread, group, 1024, 1, 0, start_proc, args, NULL, NULL); buf = (char *)apr_pcalloc(pool, sizeof(char)*12); /* sprintf(buf, "Thread %d\0", i); hythread_set_name(thread, buf); */ --- thread\src\hythr.def 2006-12-13 12:41:19.000000000 +-0600 +++ thread\src\hythr.def 2006-12-18 19:06:43.000000000 +-0600 @@ -20,12 +20,13 @@ hythread_tls_free hythread_yield hythread_suspend hythread_interrupt hythread_tls_set hythread_create +hythread_create_ex hythread_monitor_wait hythread_monitor_wait_interruptable hythread_monitor_exit hythread_set_priority hythread_unpark hythread_sleep @@ -47,12 +48,13 @@ hythread_join_timed hythread_join_interruptable hythread_get_self_id hythread_get_id hythread_get_thread hythread_struct_init +hythread_cancel_safe hythread_cancel_all hythread_group_create hythread_group_release hythread_group_get_list hythread_get_private_data hythread_set_private_data @@ -118,12 +120,13 @@ hythread_init hythread_shutdown hythread_lib_create hythread_lib_destroy hythread_lib_lock hythread_lib_unlock +hythread_lib_set_state release_start_lock acquire_start_lock inflate_lock is_fat_lock owns_thin_lock --- thread\src\hythr.exp 2006-12-13 12:41:19.000000000 +-0600 +++ thread\src\hythr.exp 2006-12-18 19:06:56.000000000 +-0600 @@ -19,12 +19,13 @@ hythread_tls_free; hythread_yield; hythread_suspend; hythread_interrupt; hythread_tls_set; hythread_create; +hythread_create_ex; hythread_monitor_wait; hythread_monitor_wait_interruptable; hythread_monitor_exit; hythread_set_priority; hythread_unpark; hythread_sleep; @@ -46,12 +47,13 @@ hythread_join_timed; hythread_join_interruptable; hythread_get_self_id; hythread_get_id; hythread_get_thread; hythread_struct_init; +hythread_cancel_safe; hythread_cancel_all; hythread_group_create; hythread_group_release; hythread_group_get_list; hythread_get_private_data; hythread_set_private_data; @@ -129,12 +131,13 @@ hythread_shutdown; hythread_lib_create; hythread_lib_destroy; hythread_lib_lock; hythread_lib_unlock; set_safepoint_callback; +hythread_lib_set_state; release_start_lock; acquire_start_lock; inflate_lock; is_fat_lock; owns_thin_lock; --- thread\src\thread_init.c 2006-12-08 18:48:39.000000000 +-0600 +++ thread\src\thread_init.c 2006-12-08 16:26:57.000000000 +-0600 @@ -185,12 +185,14 @@ // 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); + + hythread_lib_set_state(lib, TM_LIBRARY_STATE_INITIALIZED); } /** * Shut down the threading library associated with the current thread. * * @return none @@ -239,12 +241,39 @@ /** * Releases the lock over threading subsystem. * */ IDATA VMCALL hythread_global_unlock() { return hymutex_unlock(TM_LIBRARY->TM_LOCK);; +} + +/** + * Sets the global library state. + * + * @param[in] lib threading library + * @param[in] state new state + * + * @return old state + */ +IDATA VMCALL hythread_lib_set_state(hythread_library_t lib, IDATA state) { + IDATA status; + IDATA old_state; + + status = hythread_global_lock(NULL); + assert(status == TM_ERROR_NONE); + + old_state = lib->state; + + if (state != TM_LIBRARY_STATE_GET) { + lib->state = state; + } + + status = hythread_global_unlock(NULL); + assert(status == TM_ERROR_NONE); + + return old_state; } hythread_group_t get_java_thread_group(void){ return TM_DEFAULT_GROUP; } --- thread\src\thread_native_basic.c 2006-12-20 17:37:31.000000000 +-0600 +++ thread\src\thread_native_basic.c 2006-12-22 17:42:46.000000000 +-0600 @@ -89,15 +89,22 @@ * * @param[out] new_thread The newly created thread. * @param[in] group thread group, or NULL; in case of NULL this thread will go to the default group. * @param[in] attr threadattr to use to determine how to create the thread, or NULL for default attributes * @param[in] func function to run in the new thread * @param[in] data argument to be passed to starting function + * @param[in] callback shutdown callback (@see hythread_create_ex) + * @param[in] callback_data data to be passed to shutdown callback + * * @sa apr_thread_create() */ -IDATA VMCALL hythread_create_with_group(hythread_t *ret_thread, hythread_group_t group, UDATA stacksize, UDATA priority, UDATA suspend, hythread_entrypoint_t func, void *data) { +IDATA VMCALL hythread_create_with_group(hythread_t *ret_thread, hythread_group_t group, + UDATA stacksize, UDATA priority, UDATA suspend, + hythread_entrypoint_t func, void *data, + hythread_shutdown_callback_t callback, + void * callback_data) { apr_threadattr_t *apr_attrs; hythread_t new_thread; thread_start_proc_data * start_proc_data; apr_status_t apr_status; if (ret_thread) { @@ -109,12 +116,14 @@ if (new_thread == NULL) { return TM_ERROR_OUT_OF_MEMORY; } new_thread->library = hythread_self()->library; + new_thread->shutdown_callback = callback; + new_thread->shutdown_data = callback_data; if (stacksize) { apr_threadattr_create(&apr_attrs, new_thread->pool); apr_threadattr_stacksize_set(apr_attrs, stacksize); new_thread->apr_attrs = apr_attrs; } else { new_thread->apr_attrs = NULL; @@ -162,24 +171,66 @@ * @param[in] data a value to pass to the entrypoint function * * @return 0 on success or negative value on failure * * @see hythread_exit, hythread_resume */ -IDATA VMCALL hythread_create(hythread_t *ret_thread, UDATA stacksize, UDATA priority, UDATA suspend, hythread_entrypoint_t func, void *data) { - return hythread_create_with_group(ret_thread, NULL, stacksize, priority, suspend, func, data); +IDATA VMCALL hythread_create(hythread_t *ret_thread, UDATA stacksize, UDATA priority, + UDATA suspend, hythread_entrypoint_t func, void *data) { + return hythread_create_with_group(ret_thread, NULL, stacksize, priority, suspend, func, data, NULL, NULL); } + /** + * Create a new OS thread. + * + * The created thread is attached to the threading library.
+ *
+ * Unlike POSIX, this doesn't require an attributes structure. + * Instead, any interesting attributes (e.g. stacksize) are + * passed in with the arguments. + * + * @param[out] ret_thread a pointer to a hythread_t which will point to the thread (if successfully created) + * @param[in] stacksize the size of the new thread's stack (bytes)
+ * 0 indicates use default size + * @param[in] priority priorities range from HYTHREAD_PRIORITY_MIN to HYTHREAD_PRIORITY_MAX (inclusive) + * @param[in] suspend set to non-zero to create the thread in a suspended state. + * @param[in] func pointer to the function which the thread will run + * @param[in] data a value to pass to the entrypoint function + * @param[in] callback when non null callback function is specified it will be + * executed by hythread_cancel(hythread_t thread) to initiate thread shutdown + * sequence. If no callback is given then this thread doesn't have a chance to + * cleanup its internals and will be forcibly terminated. + * Note that by the time of callback execution the VM is already in shutdown + * stage thus not all services provided by VM are available. For example, + * any attempt to create a new java thread or attach the current one will fail. + * @param[in] callback_data data to be passed to shutdown callback + * + * @return 0 on success or negative value on failure + * + * @see hythread_exit, hythread_resume + */ +IDATA VMCALL hythread_create_ex(hythread_t *ret_thread, UDATA stacksize, UDATA priority, + UDATA suspend, hythread_entrypoint_t func, void *data, + hythread_shutdown_callback_t callback, void * callback_data) { + return hythread_create_with_group(ret_thread, NULL, stacksize, priority, + suspend, func, data, callback, callback_data); +} + + +/** * Registers the current OS thread with the threading subsystem. * * @param[in] handle thread to register * @param[in] lib thread library to attach to * @param[in] group thread group, or NULL; in case of NULL this thread will go to the default group + * @param[in] callback shutdown callback (@see hythread_create_ex) + * @param[in] callback_data data to be passed to shutdown callback */ -IDATA hythread_attach_to_group(hythread_t * handle, hythread_library_t lib, hythread_group_t group) { +IDATA hythread_attach_to_group(hythread_t * handle, hythread_library_t lib, hythread_group_t group, + hythread_shutdown_callback_t callback, void * callback_data) { hythread_t thread; apr_thread_t *os_handle = NULL; apr_os_thread_t *os_thread; apr_status_t apr_status; if (lib == NULL) { @@ -201,12 +252,14 @@ thread = allocate_thread(); } if (thread == NULL) { return TM_ERROR_OUT_OF_MEMORY; } thread->library = lib; + thread->shutdown_callback = callback; + thread->shutdown_data = callback_data; os_thread = apr_palloc(thread->pool, sizeof(apr_os_thread_t)); if (os_thread == NULL) { return TM_ERROR_OUT_OF_MEMORY; } *os_thread = apr_os_thread_current(); apr_status = apr_os_thread_put(&os_handle, os_thread, thread->pool); @@ -234,27 +287,31 @@ * @return 0 on success or negative value on failure * * @note (*handle) should be NULL or point to hythread_t structure * @see hythread_detach */ IDATA VMCALL hythread_attach(hythread_t *handle) { - return hythread_attach_to_group(handle, TM_LIBRARY, NULL); + return hythread_attach_to_group(handle, TM_LIBRARY, NULL, NULL, NULL); } /** * Attach an OS thread to the threading library. * * @param[out] handle pointer to a hythread_t to be set (will be ignored if null) - * @param[in] lib thread library to attach thread to + * @param[in] lib thread library to attach the thread to + * @param[in] callback shutdown callback (@see hythread_create_ex) + * @param[in] callback_data data to be passed to shutdown callback + * * @return 0 on success or negative value on failure * * @note (*handle) should be NULL or point to hythread_t structure * @see hythread_detach */ -IDATA VMCALL hythread_attach_ex(hythread_t *handle, hythread_library_t lib) { - return hythread_attach_to_group(handle, lib, NULL); +IDATA VMCALL hythread_attach_ex(hythread_t *handle, hythread_library_t lib, + hythread_shutdown_callback_t callback, void * callback_data) { + return hythread_attach_to_group(handle, lib, NULL, callback, callback_data); } /** * Detaches a thread from the threading library. * * @note Assumes that the thread being detached is already attached.
@@ -269,36 +326,45 @@ * @return none * * @see hythread_attach */ void VMCALL hythread_detach(hythread_t thread) { IDATA status; - - if (thread == NULL) { - thread = hythread_self(); - } - + hythread_t self; + // Acquire global TM lock to prevent concurrent acccess to thread list status = hythread_global_lock(NULL); assert (status == TM_ERROR_NONE); + self = hythread_self(); + if (thread == NULL) { + thread = self; + } + // No actions required in case the specified thread is detached already. if (thread->group != NULL) { - assert(thread == tm_self_tls); - - thread_set_self(NULL); + if (thread == self) { + thread_set_self(NULL); + } + + thread->state = TM_THREAD_STATE_TERMINATED | (TM_THREAD_STATE_INTERRUPTED & thread->state); + thread->exit_value = 0; + fast_thread_array[thread->thread_id] = NULL; thread->prev->next = thread->next; thread->next->prev = thread->prev; thread->group->threads_count--; thread->group = NULL; } - hythread_global_unlock(NULL); + status = hythread_global_unlock(NULL); assert(status == TM_ERROR_NONE); + + // Send join event to those threads who called join on this thread. + hylatch_count_down(thread->join_event); } /** * Waits until the selected thread finishes execution. * * @param[in] t thread to join @@ -528,24 +594,57 @@ */ IDATA VMCALL hythread_get_group(hythread_group_t *group, hythread_t thread) { (*group) = thread->group; return TM_ERROR_NONE; } +/** + * Executes thread's shutdown callback in case it was registered with + * thread creation/attaching procedures. There is a gurantee to execute + * a shutdown callback on a live thread only in a thread-safe manner. + * Though it is possible that the callback is executed several times + * before the target thread notices shutdown request. + * + * @param[in] thread a thread to be terminated by executing its shutdown callback + * @return 0 if shutdown callback was successfully excuted + */ +IDATA VMCALL hythread_cancel_safe(hythread_t thread) { + IDATA status; + IDATA res; + + res = -1; + if (thread->shutdown_callback) { + status = hythread_global_lock(); + assert(status == TM_ERROR_NONE); + + if (!hythread_is_terminated(thread)) { + thread->shutdown_callback(thread->shutdown_data); + res = 0; + } + + status = hythread_global_unlock(); + assert(status == TM_ERROR_NONE); + } + + return res; +} + /** * Terminates a running thread. * * @note This should only be used as a last resort. The system may be in * an unpredictable state once a thread is cancelled. In addition, the thread * may not even stop running if it refuses to cancel. * * @param[in] thread a thread to be terminated * @return none */ void VMCALL hythread_cancel(hythread_t thread) { - apr_thread_cancel(thread->os_handle); + if (apr_thread_cancel(thread->os_handle) == APR_SUCCESS) { + hythread_detach(thread); + } } /** * Terminates all running threads in the given group. * * @param[in] group thread group @@ -595,13 +694,19 @@ assert(thread); assert(group); // Acquire global TM lock to prevent concurrent acccess to thread list status = hythread_global_lock(NULL); - if (status != TM_ERROR_NONE) return status; + assert(status == TM_ERROR_NONE); + + if (hythread_lib_set_state(thread->library, TM_LIBRARY_STATE_GET) == TM_LIBRARY_STATE_BLOCKED) { + status = hythread_global_unlock(NULL); + assert(status == TM_ERROR_NONE); + return TM_ERROR_START; + } thread_set_self(thread); assert(thread == tm_self_tls); thread->state |= TM_THREAD_STATE_ALIVE | TM_THREAD_STATE_RUNNABLE; @@ -620,13 +725,17 @@ group->threads_count++; cur = group->thread_list->next; prev = cur->prev; thread->next = cur; thread->prev = prev; prev->next = cur->prev = thread; - return hythread_global_unlock(NULL); + + status = hythread_global_unlock(NULL); + assert(status == TM_ERROR_NONE); + + return TM_ERROR_NONE; } /* * Allocates and initializes a new thread_t structure * * @return created and initialized thread_t structure @@ -660,12 +769,15 @@ assert (status == TM_ERROR_NONE); status = hymutex_create(&ptr->mutex, TM_MUTEX_NESTED); assert (status == TM_ERROR_NONE); status = hycond_create(&ptr->condition); assert (status == TM_ERROR_NONE); + ptr->shutdown_callback = NULL; + ptr->shutdown_data = NULL; + ptr->state = TM_THREAD_STATE_ALLOCATED; return ptr; } static void reset_thread(hythread_t thread) { apr_status_t apr_status; @@ -687,13 +799,16 @@ status = hylatch_set(thread->join_event, 1); assert (status == TM_ERROR_NONE); status = hylatch_set(thread->safe_region_event, 1); assert (status == TM_ERROR_NONE); status = hysem_set(thread->resume_event, 0); assert (status == TM_ERROR_NONE); - + + thread->shutdown_callback = NULL; + thread->shutdown_data = NULL; + thread->state = TM_THREAD_STATE_ALLOCATED; } // Wrapper around user thread start proc. Used to perform some duty jobs // right after thread is started. ////// @@ -718,26 +833,16 @@ status = hythread_set_priority(thread, thread->priority); //assert (status == TM_ERROR_NONE);//now we down - fixme thread->state |= TM_THREAD_STATE_RUNNABLE; // Do actual call of the thread body supplied by the user. start_proc_data->start_proc(start_proc_data->start_proc_args); - - // Shutdown sequence. - status = hythread_global_lock(NULL); - assert (status == TM_ERROR_NONE); - assert(hythread_is_suspend_enabled()); - thread->state = TM_THREAD_STATE_TERMINATED | (TM_THREAD_STATE_INTERRUPTED & thread->state); - thread->exit_value = 0; + + assert(hythread_is_suspend_enabled()); hythread_detach(thread); - // Send join event to those threads who called join on this thread. - hylatch_count_down(thread->join_event); - - status = hythread_global_unlock(NULL); - assert (status == TM_ERROR_NONE); // TODO: It seems it is there is no need to call apr_thread_exit. // Current thread should automatically exit upon returning from this function. return (void *)(IDATA)apr_thread_exit(thd, APR_SUCCESS); } --- thread\src\thread_private.h 2006-12-13 12:40:47.000000000 +-0600 +++ thread\src\thread_private.h 2006-12-15 18:20:02.000000000 +-0600 @@ -147,18 +147,18 @@ /** * current capacity of the thread local storage */ extern int16 tm_tls_size; - typedef struct HyThreadLibrary { IDATA a; hymutex_t TM_LOCK; IDATA nondaemon_thread_count; hycond_t nondaemon_thread_cond; + IDATA state; } HyThreadLibrary; /** * Native thread control structure. @@ -235,15 +235,20 @@ * Placeholder for any data to be associated with this thread. * Java layer is using it to keep java-specific context. */ void *private_data; /** - * Flag indicating there was request to exit + * Callback to be executed upon thread cancelation. + */ + hythread_shutdown_callback_t shutdown_callback; + + /** + * Pointer to the data for shutdown callback. */ - Boolean exit_request; + void * shutdown_data; /** * Exit value of this thread */ IDATA exit_value; --- vmcore\src\init\vm_init.cpp 2006-12-20 16:20:31.000000000 +-0600 +++ vmcore\src\init\vm_init.cpp 2006-12-19 17:35:04.000000000 +-0600 @@ -570,13 +570,13 @@ hythread_t native_thread; jint status; native_thread = hythread_self(); if (!native_thread) { status = hythread_attach_to_group(&native_thread, - ((JavaVM_Internal *)java_vm)->vm_env->hythread_lib, NULL); + ((JavaVM_Internal *)java_vm)->vm_env->hythread_lib, NULL, NULL, NULL); if (status != TM_ERROR_NONE) return JNI_ERR; } assert(native_thread); status = vm_attach(java_vm, &jni_env); if (status != JNI_OK) return status; @@ -601,13 +601,13 @@ JNIEnv * jni_env; TRACE("Initializing VM"); vm_env = java_vm->vm_env; - if (hythread_attach_ex(NULL, vm_env->hythread_lib) != TM_ERROR_NONE) { + if (hythread_attach_ex(NULL, vm_env->hythread_lib, NULL, NULL) != TM_ERROR_NONE) { return JNI_ERR; } assert(hythread_is_suspend_enabled()); status = check_platform(); --- vmcore\src\init\vm_shutdown.cpp 2006-12-22 17:06:24.000000000 +-0600 +++ vmcore\src\init\vm_shutdown.cpp 2006-12-22 17:51:06.000000000 +-0600 @@ -184,12 +184,71 @@ } } } } /** + * Stops running native threads by executing shutdown callback. + * + * @param[in] vm_env a global VM environment + */ +static void vm_shutdown_stop_native_threads(Global_Env * vm_env) { + hythread_t self; + hythread_t * running_threads; + hythread_t native_thread; + hythread_iterator_t it; + hythread_group_t * group_list; + int group_count; + int size; + jint status; + + self = hythread_self(); + + status = hythread_global_lock(); + assert(status == TM_ERROR_NONE); + + status = hythread_group_get_list(&group_list, &group_count); + + size = 0; + for (int i = 0; i < group_count; i++) { + it = hythread_iterator_create(group_list[i]); + size += hythread_iterator_size(it); + hythread_iterator_release(&it); + } + + running_threads = (hythread_t *)apr_palloc(vm_env->mem_pool, size * sizeof(hythread_t)); + + size = 0; + for (int i = 0; i < group_count; i++) { + it = hythread_iterator_create(group_list[i]); + native_thread = hythread_iterator_next(&it); + while(native_thread != NULL && native_thread != self) { + running_threads[size] = native_thread; + ++size; + native_thread = hythread_iterator_next(&it); + } + hythread_iterator_release(&it); + } + + status = hythread_global_unlock(); + assert(status == TM_ERROR_NONE); + + for (int i = 0; i < size; i++) { + if (hythread_cancel_safe(running_threads[i]) != TM_ERROR_NONE) { + running_threads[i] = NULL; + } + } + + for (int i = 0; i < size; i++) { + if (running_threads[i]) { + hythread_join(running_threads[i]); + } + } +} + +/** * Waits until all non-daemon threads finish their execution, * initiates VM shutdown sequence and stops running threads if any. * * @param[in] java_vm JVM that should be destroyed * @param[in] java_thread current java thread */ @@ -239,29 +298,29 @@ status = jthread_detach(java_thread); if (status != TM_ERROR_NONE) return JNI_ERR; // Call Agent_OnUnload() for agents and unload agents. java_vm->vm_env->TI->Shutdown(java_vm); - // Block thread creation. - // TODO: investigate how to achive that with ThreadManager - // Starting this moment any exception occured in java thread will cause // entire java stack unwinding to the most recent native frame. // JNI is not available as well. assert(java_vm->vm_env->vm_state == Global_Env::VM_RUNNING); java_vm->vm_env->vm_state = Global_Env::VM_SHUTDOWNING; + // Block thread creation. + hythread_lib_set_state(java_vm->vm_env->hythread_lib, TM_LIBRARY_STATE_BLOCKED); + // Stop all (except current) java and native threads // before destroying VM-wide data. // Stop java threads vm_shutdown_stop_java_threads(java_vm->vm_env); - // TODO: ups we don't stop native threads as well :-(( - // We are lucky! Currently, there are no such threads. + // Stop native threads. + vm_shutdown_stop_native_threads(java_vm->vm_env); return JNI_OK; } static int vm_interrupt_process(void * data) { hythread_t * threadBuf;