| /* |
| * 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 |
| * @ingroup Thread |
| * @brief Threading and synchronization support |
| */ |
| |
| #include "threaddef.h" |
| #include <stdlib.h> |
| |
| #define CDEV_CURRENT_FUNCTION _prototypes_private |
| static hythread_t allocate_thread PROTOTYPE ((int globalIsLocked)); |
| static void free_thread |
| PROTOTYPE ((hythread_t thread, int globalAlreadyLocked)); |
| static IDATA interrupt_waiting_thread |
| PROTOTYPE ((hythread_t self, hythread_t threadToInterrupt)); |
| static void remove_from_queue |
| PROTOTYPE ((hythread_t volatile *queue, hythread_t thread)); |
| static IDATA destroy_thread |
| PROTOTYPE ((hythread_t thread, int globalAlreadyLocked)); |
| void VMCALL hythread_shutdown PROTOTYPE ((void)); |
| static void HYTHREAD_PROC tls_null_finalizer PROTOTYPE ((void *entry)); |
| static I_32 HYTHREAD_PROC interruptServer PROTOTYPE ((void *entryArg)); |
| static hythread_monitor_t VMCALL hythread_monitor_acquire |
| PROTOTYPE ((hythread_t self, IDATA policy, IDATA policyData)); |
| void VMCALL hythread_init PROTOTYPE ((hythread_library_t lib)); |
| static IDATA init_global_monitor PROTOTYPE ((hythread_library_t lib)); |
| static void tls_finalize PROTOTYPE ((hythread_t thread)); |
| static void free_monitor_pools PROTOTYPE ((void)); |
| static void *VMCALL thread_malloc PROTOTYPE ((void *unused, U_32 size)); |
| static void NORETURN internal_exit PROTOTYPE ((void)); |
| static IDATA monitor_wait |
| PROTOTYPE ((hythread_monitor_t monitor, I_64 millis, IDATA nanos, |
| IDATA interruptable)); |
| static IDATA monitor_enter |
| PROTOTYPE ((hythread_t self, hythread_monitor_t monitor)); |
| static UDATA init_monitor |
| PROTOTYPE ((hythread_monitor_t monitor, UDATA flags)); |
| |
| static IDATA monitor_enter_three_tier |
| PROTOTYPE ((hythread_t self, hythread_monitor_t monitor)); |
| |
| static hytime_t getCurrentCycles PROTOTYPE ((void)); |
| static hythread_monitor_pool_t allocate_monitor_pool PROTOTYPE ((void)); |
| static IDATA create_thread |
| PROTOTYPE ((hythread_t * handle, UDATA stacksize, UDATA priority, |
| UDATA suspend, hythread_entrypoint_t entrypoint, void *entryarg, |
| int globalIsLocked)); |
| static void interrupt_thread |
| PROTOTYPE ((hythread_t thread, UDATA interruptFlag)); |
| |
| static void unblock_spinlock_threads |
| PROTOTYPE ((hythread_t self, hythread_monitor_t monitor)); |
| |
| static void notify_thread |
| PROTOTYPE ((hythread_t threadToNotify, int setNotifiedFlag)); |
| static IDATA monitor_exit |
| PROTOTYPE ((hythread_t self, hythread_monitor_t monitor)); |
| static WRAPPER_TYPE thread_wrapper PROTOTYPE ((WRAPPER_ARG arg)); |
| static void VMCALL thread_free PROTOTYPE ((void *unused, void *ptr)); |
| static void enqueue_thread |
| PROTOTYPE ((hythread_t * queue, hythread_t thread)); |
| static IDATA monitor_notify_one_or_all |
| PROTOTYPE ((hythread_monitor_t monitor, int notifyall)); |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #if defined(THREAD_ASSERTS) |
| /* |
| * Helper variable for asserts. |
| * We use this to keep track of when the global lock is owned. |
| * We don't want to do a re-entrant enter on the global lock |
| */ |
| hythread_t global_lock_owner = UNOWNED; |
| #endif |
| |
| #define BOUNDED_I64_TO_IDATA(longValue) ((longValue) > 0x7FFFFFFF ? 0x7FFFFFFF : (IDATA)(longValue)) |
| |
| #define HYTHREAD_MAX_TLS_KEYS (sizeof( ((HyThreadLibrary*)NULL)->tls_finalizers ) / sizeof( ((HyThreadLibrary*)NULL)->tls_finalizers[0] )) |
| |
| #define CDEV_CURRENT_FUNCTION hythread_init |
| /** |
| * Initialize a Hy 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 Hy thread library functions. |
| * |
| * @param[in] lib pointer to the Hy thread library to be initialized (non-NULL) |
| * @return The Hy thread library's initStatus will be set to 0 on success or |
| * a negative value on failure. |
| * |
| * @see hythread_attach, hythread_shutdown |
| */ |
| void VMCALL |
| hythread_init (hythread_library_t lib) |
| { |
| ASSERT (lib); |
| |
| lib->spinlock = 0; |
| lib->threadCount = 0; |
| lib->globals = NULL; |
| lib->stack_usage = 0; |
| |
| /* set all TLS finalizers to NULL. This indicates that the key is unused */ |
| memset (lib->tls_finalizers, 0, sizeof (lib->tls_finalizers)); |
| |
| STATIC_ASSERT (CALLER_LAST_INDEX <= MAX_CALLER_INDEX); |
| |
| if (TLS_ALLOC (lib->self_ptr)) |
| goto init_cleanup1; |
| |
| lib->monitor_pool = allocate_monitor_pool (); |
| if (lib->monitor_pool == NULL) |
| goto init_cleanup2; |
| |
| if (!MUTEX_INIT (lib->monitor_mutex)) |
| goto init_cleanup3; |
| if (!MUTEX_INIT (lib->tls_mutex)) |
| goto init_cleanup4; |
| if (!MUTEX_INIT (lib->global_mutex)) |
| goto init_cleanup5; |
| |
| lib->thread_pool = |
| pool_new (sizeof (HyThread), 0, 0, 0, thread_malloc, thread_free, NULL); |
| if (lib->thread_pool == NULL) |
| goto init_cleanup6; |
| |
| lib->global_pool = |
| pool_new (sizeof (HyThreadGlobal), 0, 0, 0, thread_malloc, thread_free, |
| NULL); |
| if (lib->global_pool == NULL) |
| goto init_cleanup7; |
| |
| if (init_global_monitor (lib)) |
| goto init_cleanup8; |
| |
| lib->initStatus = 1; |
| return; |
| |
| init_cleanup8:pool_kill (lib->global_pool); |
| init_cleanup7:pool_kill (lib->thread_pool); |
| init_cleanup6:MUTEX_DESTROY (lib->global_mutex); |
| init_cleanup5:MUTEX_DESTROY (lib->tls_mutex); |
| init_cleanup4:MUTEX_DESTROY (lib->monitor_mutex); |
| init_cleanup3:free_monitor_pools (); |
| init_cleanup2:TLS_DESTROY (lib->self_ptr); |
| init_cleanup1:lib->initStatus = -1; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_self |
| /** |
| * Return the hythread_t for the current thread. |
| * |
| * @note Must be called only by an attached thread |
| * |
| * @return hythread_t for the current thread |
| * |
| * @see hythread_attach |
| * |
| */ |
| hythread_t VMCALL |
| hythread_self (void) |
| { |
| return MACRO_SELF (); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_create |
| /** |
| * Create a new OS thread. |
| * |
| * The created thread is attached to the threading library.<br> |
| * <br> |
| * Unlike POSIX, this doesn't require an attributes structure. |
| * Instead, any interesting attributes (e.g. stacksize) are |
| * passed in with the arguments. |
| * |
| * @param[out] handle 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)<br> |
| * 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] entrypoint pointer to the function which the thread will run |
| * @param[in] entryarg 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 * handle, UDATA stacksize, UDATA priority, |
| UDATA suspend, hythread_entrypoint_t entrypoint, |
| void *entryarg) |
| { |
| return create_thread (handle, stacksize, priority, suspend, entrypoint, |
| entryarg, GLOBAL_NOT_LOCKED); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION create_thread |
| /* |
| * Create a new OS thread and attach it. |
| */ |
| static IDATA |
| create_thread (hythread_t * handle, UDATA stacksize, UDATA priority, |
| UDATA suspend, hythread_entrypoint_t entrypoint, |
| void *entryarg, int globalIsLocked) |
| { |
| hythread_t thread; |
| hythread_library_t lib = GLOBAL_DATA (default_library); |
| |
| ASSERT (lib->initStatus); |
| |
| if (priority > HYTHREAD_PRIORITY_MAX) |
| { |
| goto cleanup0; |
| } |
| |
| if (stacksize == 0) |
| { |
| stacksize = STACK_DEFAULT_SIZE; |
| } |
| |
| thread = allocate_thread (globalIsLocked); |
| if (!thread) |
| { |
| goto cleanup0; |
| } |
| if (handle) |
| { |
| *handle = thread; |
| } |
| thread->library = lib; |
| thread->priority = priority; |
| thread->attachcount = 0; |
| thread->stacksize = stacksize; |
| |
| memset (thread->tls, 0, sizeof (thread->tls)); |
| |
| thread->interrupter = NULL; |
| |
| if (!COND_INIT (thread->condition)) |
| { |
| goto cleanup1; |
| } |
| if (!MUTEX_INIT (thread->mutex)) |
| { |
| goto cleanup2; |
| } |
| |
| thread->flags = suspend ? HYTHREAD_FLAG_SUSPENDED : 0; |
| thread->entrypoint = entrypoint; |
| thread->entryarg = entryarg; |
| |
| if (IS_JLM_ENABLED (thread)) |
| { |
| hythread_jlm_thread_init (thread); |
| } |
| |
| #if defined(LINUX) |
| thread->jumpBuffer = NULL; |
| #endif |
| |
| if (!THREAD_CREATE (thread, stacksize, priority, thread_wrapper, thread)) |
| { |
| goto cleanup3; |
| } |
| |
| return 0; |
| |
| /* Cleanup points */ |
| cleanup3:MUTEX_DESTROY (thread->mutex); |
| cleanup2:COND_DESTROY (thread->condition); |
| cleanup1:free_thread (thread, GLOBAL_NOT_LOCKED); |
| cleanup0:if (handle) |
| *handle = NULL; |
| return -1; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_attach |
| /** |
| * Attach an OS thread to the threading library. |
| * |
| * Create a new hythread_t to represent the existing OS thread. |
| * Attaching a thread is required when a thread was created |
| * outside of the Hy threading library wants to use any of the |
| * Hy threading library functionality. |
| * |
| * If the OS thread is already attached, handle is set to point |
| * to the existing hythread_t. |
| * |
| * @param[out] handle pointer to a hythread_t to be set (will be ignored if null) |
| * @return 0 on success or negative value on failure |
| * |
| * @see hythread_detach |
| */ |
| IDATA VMCALL |
| hythread_attach (hythread_t * handle) |
| { |
| hythread_t thread; |
| |
| if (init_thread_library ()) |
| { |
| goto cleanup0; |
| } |
| |
| if ((thread = MACRO_SELF ()) != NULL) |
| { |
| if (handle) |
| { |
| *handle = thread; |
| } |
| THREAD_LOCK (thread, thread, CALLER_ATTACH); |
| thread->attachcount++; |
| THREAD_UNLOCK (thread, thread); |
| return 0; |
| } |
| |
| thread = allocate_thread (GLOBAL_NOT_LOCKED); |
| if (!thread) |
| { |
| goto cleanup0; |
| } |
| |
| thread->library = GLOBAL_DATA (default_library); |
| thread->attachcount = 1; |
| thread->priority = HYTHREAD_PRIORITY_NORMAL; |
| thread->flags = HYTHREAD_FLAG_ATTACHED; |
| |
| if (!COND_INIT (thread->condition)) |
| { |
| goto cleanup1; |
| } |
| |
| if (!MUTEX_INIT (thread->mutex)) |
| { |
| goto cleanup2; |
| } |
| |
| #if defined(WIN32) |
| { |
| DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), |
| GetCurrentProcess (), &thread->handle, 0, TRUE, |
| DUPLICATE_SAME_ACCESS); |
| } |
| #else |
| thread->handle = THREAD_SELF (); |
| #endif |
| |
| initialize_thread_priority (thread); |
| |
| TLS_SET (thread->library->self_ptr, thread); |
| |
| thread->tid = RAS_THREAD_ID (); |
| |
| if (handle) |
| { |
| *handle = thread; |
| } |
| return 0; |
| |
| /* failure points */ |
| cleanup2:COND_DESTROY (thread->condition); |
| cleanup1:free_thread (thread, GLOBAL_NOT_LOCKED); |
| cleanup0:return -1; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_suspend |
| /** |
| * Suspend the current thread. |
| * |
| * Stop the current thread from executing until it is resumed. |
| * |
| * @return none |
| * |
| * @see hythread_resume |
| */ |
| void VMCALL |
| hythread_suspend (void) |
| { |
| hythread_t self = MACRO_SELF (); |
| ASSERT (self); |
| |
| THREAD_LOCK (self, self, CALLER_SUSPEND); |
| self->flags |= HYTHREAD_FLAG_SUSPENDED; |
| |
| COND_WAIT (self->condition, self->mutex); |
| if ((self->flags & HYTHREAD_FLAG_SUSPENDED) == 0) |
| break; |
| COND_WAIT_LOOP (); |
| |
| THREAD_UNLOCK (self, self); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_resume |
| /** |
| * Resume a thread. |
| * |
| * Take a threads out of the suspended state. |
| * |
| * If the thread is not suspended, no action is taken. |
| * |
| * @param[in] thread a thread to be resumed |
| * @return none |
| * |
| * @see hythread_create, hythread_suspend |
| */ |
| void VMCALL |
| hythread_resume (hythread_t thread) |
| { |
| hythread_t self; |
| |
| ASSERT (thread); |
| |
| if ((thread->flags & HYTHREAD_FLAG_SUSPENDED) == 0) |
| { |
| /* it wasn't suspended! */ |
| return; |
| } |
| |
| self = MACRO_SELF (); |
| ASSERT (self); |
| |
| THREAD_LOCK (self, thread, CALLER_RESUME); |
| |
| /* |
| * The thread _should_ only be OS suspended once, but |
| * handle the case where it's suspended more than once anyway. |
| */ |
| COND_NOTIFY_ALL (thread->condition); |
| thread->flags &= ~HYTHREAD_FLAG_SUSPENDED; |
| |
| THREAD_UNLOCK (self, thread); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_yield |
| /** |
| * Yield the processor. |
| * |
| * @return none |
| */ |
| void VMCALL |
| hythread_yield (void) |
| { |
| THREAD_YIELD (); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_init |
| /* |
| * Acquire and initialize a new monitor from the threading library. |
| * |
| * @param[out] handle pointer to a hythread_monitor_t to be set to point to the new monitor |
| * @param[in] flags initial flag values for the monitor |
| * @return 0 on success, negative value on failure |
| * |
| * @deprecated This has been replaced by hythread_monitor_init_with_name |
| * @see hythread_monitor_init_with_name |
| */ |
| IDATA VMCALL |
| hythread_monitor_init (hythread_monitor_t * handle, UDATA flags) |
| { |
| IDATA rc; |
| |
| /* Initialize monitor with default locking policy */ |
| rc = |
| hythread_monitor_init_policy (handle, flags, HYTHREAD_LOCKING_DEFAULT, |
| HYTHREAD_LOCKING_NO_DATA); |
| return rc; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_init_policy |
| /* |
| * Acquire and initialize a new monitor with given locking policy from the threading library. |
| * |
| * @param[out] handle pointer to a hythread_monitor_t to be set to point to the new monitor |
| * @param[in] flags initial flag values for the monitor |
| * @param[in] locking policy for the monitor |
| * @param[in] data associated with locking policy or HYTHREAD_LOCKING_NO_DATA |
| * @return 0 on success, negative value on failure |
| * |
| */ |
| IDATA VMCALL |
| hythread_monitor_init_policy (hythread_monitor_t * handle, UDATA flags, |
| IDATA policy, IDATA policyData) |
| { |
| hythread_monitor_t monitor; |
| |
| hythread_t self = MACRO_SELF (); |
| ASSERT (self); |
| ASSERT (handle); |
| |
| monitor = hythread_monitor_acquire (self, policy, policyData); |
| if (NULL == monitor) |
| { |
| return -1; |
| } |
| |
| if (init_monitor (monitor, flags) != 0) |
| { |
| return -1; |
| } |
| |
| if (IS_JLM_ENABLED (self)) |
| { |
| hythread_jlm_monitor_init (monitor); |
| } |
| |
| *handle = monitor; |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_destroy |
| /** |
| * Destroy a monitor. |
| * |
| * Destroying a monitor frees the internal resources associated |
| * with it. |
| * |
| * @note A monitor must NOT be destroyed if threads are waiting on |
| * it, or if it is currently owned. |
| * |
| * @param[in] monitor a monitor to be destroyed |
| * @return 0 on success or non-0 on failure (the monitor is in use) |
| * |
| * @see hythread_monitor_init_with_name |
| */ |
| IDATA VMCALL |
| hythread_monitor_destroy (hythread_monitor_t monitor) |
| { |
| hythread_t self = MACRO_SELF (); |
| |
| ASSERT (self); |
| ASSERT (monitor); |
| |
| GLOBAL_LOCK (self, CALLER_MONITOR_DESTROY); |
| |
| if (monitor->owner || monitor->waiting) |
| { |
| /* This monitor is in use! It was probably abandoned when a thread was cancelled. |
| * There's actually a very small timing hole here -- if the thread had just locked the |
| * mutex and not yet set the owner field when it was cancelled, we have no way of |
| * knowing that the mutex may be in an invalid state. The same thing can happen |
| * if the thread has just cleared the field and is about to unlock the mutex. |
| * Hopefully the OS takes care of this for us, but it might not. |
| */ |
| GLOBAL_UNLOCK (self); |
| return HYTHREAD_ILLEGAL_MONITOR_STATE; |
| } |
| |
| monitor->owner = (hythread_t) self->library->monitor_pool->next_free; |
| monitor->count = FREE_TAG; |
| monitor->userData = 0; |
| self->library->monitor_pool->next_free = monitor; |
| |
| GLOBAL_UNLOCK (self); |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_enter |
| /** |
| * Enter a monitor. |
| * |
| * A thread may re-enter a monitor it owns multiple times, but must |
| * exit the monitor the same number of times before any other thread |
| * wanting to enter the monitor is permitted to continue. |
| * |
| * @param[in] monitor a monitor to be entered |
| * @return 0 on success<br> |
| * HYTHREAD_PRIORITY_INTERRUPTED if the thread was priority interrupted while blocked |
| * |
| * @see hythread_monitor_enter_using_threadId, hythread_monitor_exit, hythread_monitor_exit_using_threadId |
| */ |
| IDATA VMCALL |
| hythread_monitor_enter (hythread_monitor_t monitor) |
| { |
| hythread_t self = MACRO_SELF (); |
| |
| ASSERT (self); |
| ASSERT (monitor); |
| ASSERT (FREE_TAG != monitor->count); |
| |
| if (monitor->owner == self) |
| { |
| ASSERT (monitor->count >= 1); |
| monitor->count++; |
| |
| if (IS_JLM_ENABLED (self)) |
| { |
| ASSERT (monitor->tracing); |
| monitor->tracing->recursive_count++; |
| monitor->tracing->enter_count++; |
| } /* if (IS_JLM_ENABLED(self)) */ |
| |
| return 0; |
| } |
| |
| return monitor_enter (self, monitor); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_exit |
| /** |
| * Exit a monitor. |
| * |
| * Exit a monitor, and if the owning count is zero, release it. |
| * |
| * @param[in] monitor a monitor to be exited |
| * @return 0 on success, <br>HYTHREAD_ILLEGAL_MONITOR_STATE if the current thread does not own the monitor |
| * |
| * @see hythread_monitor_exit_using_threadId, hythread_monitor_enter, hythread_monitor_enter_using_threadId |
| */ |
| IDATA VMCALL |
| hythread_monitor_exit (hythread_monitor_t monitor) |
| { |
| hythread_t self = MACRO_SELF (); |
| |
| ASSERT (self); |
| ASSERT (monitor); |
| ASSERT (FREE_TAG != monitor->count); |
| |
| if (monitor->owner != self) |
| { |
| return HYTHREAD_ILLEGAL_MONITOR_STATE; |
| } |
| |
| return monitor_exit (self, monitor); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_wait |
| /** |
| * Wait on a monitor until notified. |
| * |
| * Release the monitor, wait for a signal (notification), then re-acquire the monitor. |
| * |
| * @param[in] monitor a monitor to be waited on |
| * @return 0 if the monitor has been waited on, notified, and reobtained<br> |
| * HYTHREAD_INVALID_ARGUMENT if millis or nanos is out of range (millis or nanos < 0, or nanos >= 1E6)<br> |
| * HYTHREAD_ILLEGAL_MONITOR_STATE if the current thread does not own the monitor |
| * |
| * @see hythread_monitor_wait_interruptable, hythread_monitor_wait_timed, hythread_monitor_enter |
| * |
| */ |
| IDATA VMCALL |
| hythread_monitor_wait (hythread_monitor_t monitor) |
| { |
| return monitor_wait (monitor, 0, 0, WAIT_UNINTERRUPTABLE); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_notify |
| /** |
| * Notify a single thread waiting on a monitor. |
| * |
| * A thread is considered to be waiting on the monitor if |
| * it is currently blocked while executing hythread_monitor_wait on the monitor. |
| * |
| * If no threads are waiting, no action is taken. |
| * |
| * @param[in] monitor a monitor to be signaled |
| * @return 0 once the monitor has been signaled<br>HYTHREAD_ILLEGAL_MONITOR_STATE if the current thread does not own the monitor |
| * |
| * @see hythread_monitor_notify_all, hythread_monitor_enter, hythread_monitor_wait |
| */ |
| IDATA VMCALL |
| hythread_monitor_notify (hythread_monitor_t monitor) |
| { |
| return monitor_notify_one_or_all (monitor, NOTIFY_ONE); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_notify_all |
| /** |
| * Notify all threads waiting on a monitor. |
| * |
| * A thread is considered to be waiting on the monitor if |
| * it is currently blocked while executing hythread_monitor_wait on the monitor. |
| * |
| * If no threads are waiting, no action is taken. |
| * |
| * |
| * @param[in] monitor a monitor to be signaled |
| * @return 0 once the monitor has been signaled<br>HYTHREAD_ILLEGAL_MONITOR_STATE if the current thread does not own the monitor |
| * |
| * @see hythread_monitor_notify, hythread_monitor_enter, hythread_monitor_wait |
| */ |
| IDATA VMCALL |
| hythread_monitor_notify_all (hythread_monitor_t monitor) |
| { |
| return monitor_notify_one_or_all (monitor, NOTIFY_ALL); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_tls_alloc |
| /** |
| * Allocate a thread local storage (TLS) key. |
| * |
| * Create and return a new, unique key for thread local storage. |
| * |
| * @note The hande returned will be >=0, so it is safe to test the handle against 0 to see if it's been |
| * allocated yet. |
| * |
| * @param[out] handle pointer to a key to be initialized with a key value |
| * @return 0 on success or negative value if a key could not be allocated (i.e. all TLS has been allocated) |
| * |
| * @see hythread_tls_free, hythread_tls_set |
| */ |
| IDATA VMCALL |
| hythread_tls_alloc (hythread_tls_key_t * handle) |
| { |
| return hythread_tls_alloc_with_finalizer (handle, tls_null_finalizer); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_tls_free |
| /** |
| * Release a TLS key. |
| * |
| * Release a TLS key previously allocated by hythread_tls_alloc. |
| * |
| * @param[in] key TLS key to be freed |
| * @return 0 on success or negative value on failure |
| * |
| * @see hythread_tls_alloc, hythread_tls_set |
| * |
| */ |
| IDATA VMCALL |
| hythread_tls_free (hythread_tls_key_t key) |
| { |
| HyPoolState state; |
| hythread_t each; |
| hythread_library_t lib = GLOBAL_DATA (default_library); |
| ASSERT (lib); |
| |
| /* clear the TLS in every existing thread */ |
| GLOBAL_LOCK_SIMPLE (lib); |
| each = pool_startDo (lib->thread_pool, &state); |
| while (each) |
| { |
| each->tls[key - 1] = NULL; |
| each = pool_nextDo (&state); |
| } |
| GLOBAL_UNLOCK_SIMPLE (lib); |
| |
| /* now return the key to the free set */ |
| MUTEX_ENTER (lib->tls_mutex); |
| lib->tls_finalizers[key - 1] = NULL; |
| MUTEX_EXIT (lib->tls_mutex); |
| |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_tls_set |
| /** |
| * Set a thread's TLS value. |
| * |
| * @param[in] thread a thread |
| * @param[in] key key to have TLS value set (any value returned by hythread_alloc) |
| * @param[in] value value to be stored in TLS |
| * @return 0 on success or negative value on failure |
| * |
| * @see hythread_tls_alloc, hythread_tls_free, hythread_tls_get |
| */ |
| IDATA VMCALL |
| hythread_tls_set (hythread_t thread, hythread_tls_key_t key, void *value) |
| { |
| thread->tls[key - 1] = value; |
| |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_set_priority |
| /** |
| * Set a thread's execution priority. |
| * |
| * @param[in] thread a thread |
| * @param[in] priority |
| * Use the following symbolic constants for priorities:<br> |
| * HYTHREAD_PRIORITY_MAX<br> |
| * HYTHREAD_PRIORITY_USER_MAX<br> |
| * HYTHREAD_PRIORITY_NORMAL<br> |
| * HYTHREAD_PRIORITY_USER_MIN<br> |
| * HYTHREAD_PRIORITY_MIN<br> |
| * |
| * @returns 0 on success or negative value on failure (priority wasn't changed) |
| * |
| * |
| */ |
| IDATA VMCALL |
| hythread_set_priority (hythread_t thread, UDATA priority) |
| { |
| ASSERT (thread); |
| |
| if (priority > HYTHREAD_PRIORITY_MAX) |
| { |
| return -1; |
| } |
| |
| if (THREAD_SET_PRIORITY (thread->handle, priority_map[priority])) |
| { |
| return -1; |
| } |
| |
| thread->priority = priority; |
| |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_interrupt |
| /** |
| * Interrupt a thread. |
| * |
| * If the thread is currently blocked (i.e. waiting on a monitor_wait or sleeping) |
| * resume the thread and cause it to return from the blocking function with |
| * HYTHREAD_INTERRUPTED. |
| * |
| * @param[in] thread a thead to be interrupted |
| * @return none |
| */ |
| void VMCALL |
| hythread_interrupt (hythread_t thread) |
| { |
| interrupt_thread (thread, HYTHREAD_FLAG_INTERRUPTED); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_interrupted |
| /** |
| * Return the value of a thread's interrupted flag. |
| * |
| * @param[in] thread thread to be queried |
| * @return 0 if not interrupted, non-zero if interrupted |
| */ |
| UDATA VMCALL |
| hythread_interrupted (hythread_t thread) |
| { |
| ASSERT (thread); |
| return (thread->flags & HYTHREAD_FLAG_INTERRUPTED) != 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_clear_interrupted |
| /** |
| * Clear the interrupted flag of the current thread and return its previous value. |
| * |
| * @return previous value of interrupted flag: non-zero if the thread had been interrupted. |
| */ |
| UDATA VMCALL |
| hythread_clear_interrupted (void) |
| { |
| UDATA oldFlags; |
| hythread_t self = MACRO_SELF (); |
| ASSERT (self); |
| |
| THREAD_LOCK (self, self, CALLER_CLEAR_INTERRUPTED); |
| oldFlags = self->flags; |
| self->flags = oldFlags & ~HYTHREAD_FLAG_INTERRUPTED; |
| THREAD_UNLOCK (self, self); |
| |
| return (oldFlags & HYTHREAD_FLAG_INTERRUPTED) != 0; |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION remove_from_queue |
| /* |
| * Remove a thread from a monitor's queue. |
| * |
| * @param[in] queue head of a monitor's queue |
| * @param[in] thread thread to be removed from queue |
| * @return none |
| */ |
| static void |
| remove_from_queue (hythread_t volatile *queue, hythread_t thread) |
| { |
| hythread_t queued, next; |
| |
| ASSERT (thread); |
| |
| if ((queued = *queue) == NULL) |
| return; |
| |
| if (queued == thread) |
| { |
| *queue = thread->next; |
| thread->next = NULL; |
| } |
| else |
| { |
| while ((next = queued->next) != NULL && next != thread) |
| { |
| queued = next; |
| } |
| if (next != NULL) |
| { |
| queued->next = thread->next; |
| thread->next = NULL; |
| } |
| } |
| |
| ASSERT (NULL == thread->next); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION allocate_monitor_pool |
| /* |
| * Create and initialize a pool of monitors. |
| * |
| * @return pointer to a new monitor pool on success, NULL on failure |
| * |
| */ |
| static hythread_monitor_pool_t |
| allocate_monitor_pool (void) |
| { |
| int i; |
| hythread_monitor_t entry; |
| hythread_monitor_pool_t pool = |
| (hythread_monitor_pool_t) malloc (sizeof (HyThreadMonitorPool)); |
| if (pool == NULL) |
| { |
| return NULL; |
| } |
| memset (pool, 0, sizeof (HyThreadMonitorPool)); |
| |
| pool->next_free = entry = &pool->entries[0]; |
| for (i = 0; i < MONITOR_POOL_SIZE - 1; i++, entry++) |
| { |
| entry->count = FREE_TAG; |
| entry->owner = (hythread_t) (entry + 1); |
| /* entry->waiting = entry->blocked = NULL; *//* (unnecessary) */ |
| entry->flags = HYTHREAD_MONITOR_MUTEX_UNINITIALIZED; |
| } |
| /* initialize the last monitor */ |
| entry->count = FREE_TAG; |
| entry->flags = HYTHREAD_MONITOR_MUTEX_UNINITIALIZED; |
| |
| return pool; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION thread_wrapper |
| /* |
| * Function we pass to the OS for new threads. |
| * |
| * @param arg pointer to the hythread_t for the new thread |
| * @return none |
| */ |
| static WRAPPER_TYPE |
| thread_wrapper (WRAPPER_ARG arg) |
| { |
| hythread_t thread = (hythread_t) arg; |
| hythread_library_t lib = thread->library; |
| UDATA flags; |
| |
| ASSERT (thread); |
| ASSERT (lib); |
| |
| thread->tid = RAS_THREAD_ID (); |
| |
| TLS_SET (lib->self_ptr, thread); |
| |
| if (lib->stack_usage) |
| { |
| paint_stack (thread); |
| } |
| |
| if (thread->flags & HYTHREAD_FLAG_CANCELED) |
| { |
| internal_exit (); |
| } |
| |
| /* Handle the create-suspended case */ |
| /* (This code is basically the same as hythread_suspend, but we need to |
| test the condition under mutex or else there's a timing hole) */ |
| THREAD_LOCK (thread, thread, CALLER_THREAD_WRAPPER); |
| if (thread->flags & HYTHREAD_FLAG_SUSPENDED) |
| { |
| COND_WAIT (thread->condition, thread->mutex); |
| if ((thread->flags & HYTHREAD_FLAG_SUSPENDED) == 0) |
| break; |
| COND_WAIT_LOOP (); |
| } |
| thread->flags |= HYTHREAD_FLAG_STARTED; |
| flags = thread->flags; |
| THREAD_UNLOCK (thread, thread); |
| |
| if (thread->flags & HYTHREAD_FLAG_CANCELED) |
| { |
| internal_exit (); |
| } |
| |
| #if defined(LINUX) |
| /* Workaround for NPTL bug on Linux. See hythread_exit() */ |
| { |
| jmp_buf jumpBuffer; |
| if (0 == setjmp (jumpBuffer)) |
| { |
| thread->jumpBuffer = &jumpBuffer; |
| thread->entrypoint (thread->entryarg); |
| } |
| thread->jumpBuffer = NULL; |
| } |
| #else |
| thread->entrypoint (thread->entryarg); |
| #endif |
| |
| internal_exit (); |
| /* UNREACHABLE */ |
| WRAPPER_RETURN (); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION monitor_notify_one_or_all |
| /* |
| * Signal one or all threads waiting on the monitor. |
| * |
| * If no threads are waiting, this does nothing. |
| * |
| * @param[in] monitor monitor to be notified on |
| * @param[in] notifyall 0 to notify one, non-zero to notify all |
| * @return 0 once the monitor has been signalled<br> |
| * HYTHREAD_ILLEGAL_MONITOR_STATE if the current thread does not |
| * own the monitor |
| * |
| */ |
| static IDATA |
| monitor_notify_one_or_all (hythread_monitor_t monitor, int notifyall) |
| { |
| |
| hythread_t self = MACRO_SELF (); |
| hythread_t queue, next; |
| int someoneNotified = 0; |
| |
| ASSERT (self); |
| ASSERT (monitor); |
| |
| if (monitor->owner != self) |
| { |
| ASSERT_DEBUG (0); |
| return HYTHREAD_ILLEGAL_MONITOR_STATE; |
| } |
| |
| MONITOR_LOCK (self, monitor, CALLER_NOTIFY_ONE_OR_ALL); |
| |
| next = monitor->waiting; |
| |
| while (next) |
| { |
| queue = next; |
| next = queue->next; |
| THREAD_LOCK (self, queue, CALLER_NOTIFY_ONE_OR_ALL); |
| if (queue->flags & HYTHREAD_FLAG_WAITING) |
| { |
| notify_thread (queue, SET_NOTIFIED_FLAG); |
| someoneNotified = 1; |
| } |
| THREAD_UNLOCK (self, queue); |
| |
| if ((someoneNotified) && (!notifyall)) |
| { |
| break; |
| } |
| } |
| |
| MONITOR_UNLOCK (self, monitor); |
| |
| return 0; |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION notify_thread |
| /* |
| * Notify a thread. |
| * |
| * Helper routine because we notify a thread in |
| * a couple of places. |
| * @note: assumes the caller has THREAD_LOCK'd the |
| * thread being notified (and owns the monitor being notified on) |
| * @param[in] threadToNotify thread to notify |
| * @param[in] setNotifiedFlag indicates whether to set the notified thread's notified flag. |
| * @return none |
| */ |
| static void |
| notify_thread (hythread_t threadToNotify, int setNotifiedFlag) |
| { |
| ASSERT (threadToNotify); |
| ASSERT (threadToNotify->flags & HYTHREAD_FLAG_WAITING); |
| |
| threadToNotify->flags &= ~HYTHREAD_FLAG_WAITING; |
| threadToNotify->flags |= HYTHREAD_FLAG_BLOCKED; |
| if (setNotifiedFlag) |
| { |
| threadToNotify->flags |= HYTHREAD_FLAG_NOTIFIED; |
| } |
| COND_NOTIFY_ALL (threadToNotify->condition); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_sleep_interruptable |
| /** |
| * Suspend the current thread from executing |
| * for at least the specified time. |
| * |
| * @param[in] millis |
| * @param[in] nanos |
| * @return 0 on success<br> |
| * HYTHREAD_INVALID_ARGUMENT if the arguments are invalid<br> |
| * HYTHREAD_INTERRUPTED if the sleep was interrupted |
| * |
| * @see hythread_sleep |
| */ |
| IDATA VMCALL |
| hythread_sleep_interruptable (I_64 millis, IDATA nanos) |
| { |
| hythread_t self = MACRO_SELF (); |
| IDATA boundedMillis = BOUNDED_I64_TO_IDATA (millis); |
| |
| ASSERT (self); |
| |
| if ((millis < 0) || (nanos < 0) || (nanos >= 1000000)) |
| { |
| return HYTHREAD_INVALID_ARGUMENT; |
| } |
| |
| THREAD_LOCK (self, self, CALLER_SLEEP_INTERRUPTABLE); |
| |
| if (self->flags & HYTHREAD_FLAG_INTERRUPTED) |
| { |
| self->flags &= ~HYTHREAD_FLAG_INTERRUPTED; |
| THREAD_UNLOCK (self, self); |
| return HYTHREAD_INTERRUPTED; |
| } |
| |
| if (self->flags & HYTHREAD_FLAG_PRIORITY_INTERRUPTED) |
| { |
| self->flags &= ~HYTHREAD_FLAG_PRIORITY_INTERRUPTED; |
| THREAD_UNLOCK (self, self); |
| return HYTHREAD_PRIORITY_INTERRUPTED; |
| } |
| |
| self->flags |= |
| HYTHREAD_FLAG_SLEEPING | HYTHREAD_FLAG_INTERRUPTABLE | |
| HYTHREAD_FLAG_TIMER_SET; |
| |
| COND_WAIT_IF_TIMEDOUT (self->condition, self->mutex, boundedMillis, nanos) |
| { |
| break; |
| } |
| else |
| { |
| if (self->flags & HYTHREAD_FLAG_INTERRUPTED) |
| { |
| self->flags &= |
| ~(HYTHREAD_FLAG_INTERRUPTED | HYTHREAD_FLAG_SLEEPING | |
| HYTHREAD_FLAG_INTERRUPTABLE | HYTHREAD_FLAG_TIMER_SET); |
| THREAD_UNLOCK (self, self); |
| return HYTHREAD_INTERRUPTED; |
| } |
| if (self->flags & HYTHREAD_FLAG_PRIORITY_INTERRUPTED) |
| { |
| self->flags &= |
| ~(HYTHREAD_FLAG_PRIORITY_INTERRUPTED | HYTHREAD_FLAG_SLEEPING | |
| HYTHREAD_FLAG_INTERRUPTABLE | HYTHREAD_FLAG_TIMER_SET); |
| THREAD_UNLOCK (self, self); |
| return HYTHREAD_PRIORITY_INTERRUPTED; |
| } |
| } |
| COND_WAIT_TIMED_LOOP (); |
| |
| self->flags &= |
| ~(HYTHREAD_FLAG_SLEEPING | HYTHREAD_FLAG_INTERRUPTABLE | |
| HYTHREAD_FLAG_TIMER_SET); |
| THREAD_UNLOCK (self, self); |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_sleep |
| /** |
| * Suspend the current thread from executing |
| * for at least the specified time. |
| * |
| * @param[in] millis minimum number of milliseconds to sleep |
| * @return 0 on success<br> HYTHREAD_INVALID_ARGUMENT if millis < 0 |
| * |
| * @see hythread_sleep_interruptable |
| */ |
| IDATA VMCALL |
| hythread_sleep (I_64 millis) |
| { |
| hythread_t self = MACRO_SELF (); |
| |
| IDATA boundedMillis = (millis > 0x7FFFFFFF ? 0x7FFFFFFF : (IDATA) millis); |
| |
| ASSERT (self); |
| |
| if (millis < 0) |
| { |
| return HYTHREAD_INVALID_ARGUMENT; |
| } |
| |
| THREAD_LOCK (self, self, CALLER_SLEEP); |
| |
| self->flags |= HYTHREAD_FLAG_SLEEPING | HYTHREAD_FLAG_TIMER_SET; |
| |
| COND_WAIT_IF_TIMEDOUT (self->condition, self->mutex, boundedMillis, 0) |
| { |
| break; |
| } |
| COND_WAIT_TIMED_LOOP (); |
| |
| self->flags &= ~(HYTHREAD_FLAG_SLEEPING | HYTHREAD_FLAG_TIMER_SET); |
| THREAD_UNLOCK (self, self); |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_lock |
| /* |
| * Acquire the threading library's global lock. |
| * |
| * @param[in] self hythread_t for the current thread |
| * @param[in] monitor must be NULL |
| * @return none |
| * |
| * @deprecated This has been replaced by hythread_lib_lock. |
| * @see hythread_lib_lock, hythread_lib_unlock |
| */ |
| void VMCALL |
| hythread_monitor_lock (hythread_t self, hythread_monitor_t monitor) |
| { |
| ASSERT (self); |
| |
| if (monitor == NULL) |
| { |
| GLOBAL_LOCK (self, CALLER_GLOBAL_LOCK); |
| } |
| else |
| { |
| ASSERT (0); |
| } |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_unlock |
| /* |
| * Release the threading library's global lock. |
| * |
| * @param[in] self hythread_t for the current thread |
| * @param[in] monitor |
| * @return none |
| * |
| * @deprecated This has been replaced by hythread_lib_unlock. |
| * @see hythread_lib_lock, hythread_lib_unlock |
| */ |
| void VMCALL |
| hythread_monitor_unlock (hythread_t self, hythread_monitor_t monitor) |
| { |
| ASSERT (self); |
| |
| if (monitor == NULL) |
| { |
| GLOBAL_UNLOCK (self); |
| } |
| else |
| { |
| ASSERT (0); |
| } |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_acquire |
| /* |
| * Acquire a monitor from the threading library. (Private) |
| * |
| * @param[in] self current thread |
| * @param[in] locking policy |
| * @param[in] locking policy data or HYTHREAD_LOCKING_NO_DATA |
| * @return NULL on failure, non-NULL on success |
| * |
| * @see hythread_monitor_init_with_name, hythread_monitor_destroy |
| * |
| * |
| */ |
| static hythread_monitor_t VMCALL |
| hythread_monitor_acquire (hythread_t self, IDATA policy, IDATA policyData) |
| { |
| hythread_monitor_t entry; |
| hythread_monitor_pool_t pool = self->library->monitor_pool; |
| IDATA rc; |
| |
| ASSERT (self); |
| ASSERT (pool); |
| |
| GLOBAL_LOCK (self, CALLER_MONITOR_ACQUIRE); |
| |
| entry = pool->next_free; |
| if (entry == NULL) |
| { |
| hythread_monitor_pool_t last_pool = pool; |
| while (last_pool->next != NULL) |
| last_pool = last_pool->next; |
| last_pool->next = allocate_monitor_pool (); |
| if (last_pool->next == NULL) |
| { |
| /* failed to grow monitor pool */ |
| GLOBAL_UNLOCK (self); |
| return NULL; |
| } |
| entry = last_pool->next->next_free; |
| } |
| |
| /* the first time that a mutex is acquired from the pool, we need to |
| * initialize its mutex |
| */ |
| if (entry->flags == HYTHREAD_MONITOR_MUTEX_UNINITIALIZED) |
| { |
| |
| rc = MUTEX_INIT (entry->mutex); |
| |
| if (!rc) |
| { |
| /* failed to initialize mutex */ |
| ASSERT_DEBUG (0); |
| GLOBAL_UNLOCK (self); |
| return NULL; |
| } |
| |
| entry->flags = 0; |
| |
| } |
| |
| pool->next_free = (hythread_monitor_t) entry->owner; |
| entry->count = 0; |
| |
| GLOBAL_UNLOCK (self); |
| |
| return entry; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_cancel |
| /** |
| * Terminate 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) |
| { |
| hythread_monitor_t monitor = NULL; |
| hythread_t self = MACRO_SELF (); |
| |
| ASSERT (thread); |
| THREAD_LOCK (self, thread, CALLER_CANCEL); |
| |
| /* Special case -- we can cancel cleanly if the thread hasn't started yet */ |
| if ((thread->flags & HYTHREAD_FLAG_STARTED) == 0) |
| { |
| thread->flags |= HYTHREAD_FLAG_CANCELED; |
| THREAD_UNLOCK (self, thread); |
| hythread_resume (thread); |
| return; |
| } |
| |
| THREAD_CANCEL (thread->handle); |
| |
| thread->flags |= HYTHREAD_FLAG_CANCELED; |
| |
| THREAD_UNLOCK (self, thread); |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_detach |
| /** |
| * Detaches the current thread from the threading library. |
| * |
| * Detach must only be called by an attached thread. The actual parameter |
| * must be the the current thread's hythread_t, or NULL (in which case this |
| * function retrieves and uses the current thread's hythread_t). This |
| * function cannot be used to detach an arbitrary thread. |
| * |
| * When detached, internal resources associated with the thread are freed |
| * and the hythread_t structure becomes invalid. |
| * |
| * @param[in] thread |
| * the hythread_t of the current thread to be detached, or NULL |
| * meaning the current thread struct is looked-up and detached. |
| * @return none |
| * |
| * @see hythread_attach |
| */ |
| void VMCALL |
| hythread_detach (hythread_t thread) |
| { |
| UDATA destroy = 0; |
| UDATA attached = 0; |
| hythread_t self = MACRO_SELF (); |
| |
| if (thread == NULL) |
| { |
| thread = self; |
| } |
| ASSERT (thread); |
| |
| THREAD_LOCK (self, thread, CALLER_DETACH); |
| if (thread->attachcount < 1) |
| { |
| /* error! */ |
| } |
| else |
| { |
| if (--thread->attachcount == 0) |
| { |
| if (thread->flags & HYTHREAD_FLAG_ATTACHED) |
| { |
| /* this is an attached thread, and it is now fully |
| detached. Mark it dead so that it can be destroyed */ |
| thread->flags |= HYTHREAD_FLAG_DEAD; |
| attached = destroy = 1; |
| } |
| else |
| { |
| destroy = thread->flags & HYTHREAD_FLAG_DEAD; |
| } |
| } |
| } |
| THREAD_UNLOCK (self, thread); |
| |
| if (destroy) |
| { |
| hythread_library_t library = thread->library; |
| |
| tls_finalize (thread); |
| |
| destroy_thread (thread, GLOBAL_NOT_LOCKED); |
| if (attached) |
| { |
| TLS_SET (library->self_ptr, NULL); |
| } |
| } |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_exit |
| /** |
| * Exit the current thread. |
| * |
| * If the thread has been detached, it is destroyed. |
| * |
| * If monitor is not NULL, the monitor will be exited before the thread terminates. |
| * This is useful if the thread wishes to signal its termination to a watcher, since |
| * it exits the monitor and terminates the thread without ever returning control |
| * to the thread's routine, which might be running in a DLL which is about to |
| * be closed. |
| * |
| * @param[in] monitor monitor to be exited before exiting (ignored if NULL) |
| * @return none |
| */ |
| void VMCALL NORETURN |
| hythread_exit (hythread_monitor_t monitor) |
| { |
| hythread_t self = MACRO_SELF (); |
| |
| if (monitor) |
| { |
| hythread_monitor_exit (monitor); |
| } |
| |
| /* Walk all monitors: if this thread owns a monitor, exit it */ |
| monitor = NULL; |
| while ((monitor = hythread_monitor_walk (monitor)) != NULL) |
| { |
| if (monitor->owner == self) |
| { |
| monitor->count = 1; /* exit n-1 times */ |
| hythread_monitor_exit (monitor); |
| } |
| } |
| |
| #if defined(LINUX) |
| /* NPTL calls __pthread_unwind() from pthread_exit(). That walks the stack. |
| * We can't allow that to happen, since our caller might have already been |
| * unloaded. Walking the calling frame could cause a crash. Instead, we |
| * longjmp back out to the initial frame. |
| */ |
| if (self->jumpBuffer) |
| { |
| longjmp (*(jmp_buf *) self->jumpBuffer, 1); |
| } |
| #endif |
| |
| internal_exit (); |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_priority_interrupt |
| /** |
| * Priority interrupt a thread. |
| * |
| * If the thread is currently blocked (i.e. waiting on a monitor_wait or sleeping) |
| * resume the thread and return from the blocking function with |
| * HYTHREAD_PRIORITY_INTERRUPTED |
| * |
| * @param[in] thread a thead to be priority interrupted |
| * @return none |
| */ |
| void VMCALL |
| hythread_priority_interrupt (hythread_t thread) |
| { |
| interrupt_thread (thread, HYTHREAD_FLAG_PRIORITY_INTERRUPTED); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_priority_interrupted |
| /** |
| * Return the value of a thread's priority interrupted flag. |
| * |
| * @param[in] thread thread to be queried |
| * @return 0 if not priority interrupted, non-zero if priority interrupted flag set |
| */ |
| UDATA VMCALL |
| hythread_priority_interrupted (hythread_t thread) |
| { |
| return (thread->flags & HYTHREAD_FLAG_PRIORITY_INTERRUPTED) != 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_clear_priority_interrupted |
| /** |
| * Clear the priority interrupted flag of the current thread and return its previous value. |
| * |
| * @return previous value of priority interrupted flag: nonzero if the thread had been priority interrupted. |
| */ |
| UDATA VMCALL |
| hythread_clear_priority_interrupted (void) |
| { |
| UDATA oldFlags; |
| hythread_t self = MACRO_SELF (); |
| ASSERT (self); |
| |
| THREAD_LOCK (self, self, CALLER_CLEAR_PRIORITY_INTERRUPTED); |
| oldFlags = self->flags; |
| self->flags = oldFlags & ~HYTHREAD_FLAG_PRIORITY_INTERRUPTED; |
| THREAD_UNLOCK (self, self); |
| |
| return (oldFlags & HYTHREAD_FLAG_PRIORITY_INTERRUPTED) != 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_try_enter |
| /** |
| * Attempt to enter a monitor without blocking. |
| * |
| * If the thread must block before it enters the monitor this function |
| * returns immediately with a negative value to indicate failure. |
| * |
| * @param[in] monitor a monitor |
| * @return 0 on success or negative value on failure |
| * |
| * @see hythread_monitor_try_enter_using_threadId |
| * |
| */ |
| IDATA VMCALL |
| hythread_monitor_try_enter (hythread_monitor_t monitor) |
| { |
| return hythread_monitor_try_enter_using_threadId (monitor, MACRO_SELF ()); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_shutdown |
| /** |
| * Shut down the Hy threading library associated with the current thread. |
| * |
| * @return none |
| * |
| * @see hythread_init |
| */ |
| void VMCALL |
| hythread_shutdown (void) |
| { |
| hythread_library_t lib = GLOBAL_DATA (default_library); |
| ASSERT (lib); |
| MUTEX_DESTROY (lib->tls_mutex); |
| MUTEX_DESTROY (lib->monitor_mutex); |
| MUTEX_DESTROY (lib->global_mutex); |
| pool_kill (lib->global_pool); |
| free_monitor_pools (); |
| #if !defined(LINUX) |
| TLS_DESTROY (lib->self_ptr); |
| pool_kill (lib->thread_pool); |
| #endif /* LINUX */ |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION allocate_thread |
| /* |
| * Allocate a hythread_t from the hythread_t pool. |
| * |
| * @note assumes the threading library's thread pool is already initialized |
| * @param[in] globalIsLocked indicates whether the threading library global mutex is already locked. |
| * @return a new hythread_t on success, NULL on failure. |
| * |
| */ |
| static hythread_t |
| allocate_thread (int globalIsLocked) |
| { |
| hythread_t result; |
| hythread_library_t lib = GLOBAL_DATA (default_library); |
| ASSERT (lib); |
| |
| if (!globalIsLocked) |
| { |
| GLOBAL_LOCK_SIMPLE (lib); |
| } |
| lib->threadCount++; |
| result = pool_newElement (lib->thread_pool); |
| if (!globalIsLocked) |
| { |
| GLOBAL_UNLOCK_SIMPLE (lib); |
| } |
| |
| if (result) |
| { |
| memset (result, 0, sizeof (HyThread)); |
| } |
| |
| return result; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION free_thread |
| /* |
| * Return a hythread_t to the threading library's monitor pool. |
| * |
| * @param[in] thread thread to be returned to the pool |
| * @param[in] globalAlreadyLocked indicated whether the threading library global |
| * mutex is already locked |
| * @return none |
| */ |
| static void |
| free_thread (hythread_t thread, int globalAlreadyLocked) |
| { |
| hythread_library_t lib = thread->library; |
| |
| ASSERT (thread); |
| |
| if (!globalAlreadyLocked) |
| { |
| GLOBAL_LOCK_SIMPLE (lib); |
| } |
| pool_removeElement (lib->thread_pool, thread); |
| lib->threadCount--; |
| |
| if (!globalAlreadyLocked) |
| { |
| GLOBAL_UNLOCK_SIMPLE (lib); |
| } |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION thread_malloc |
| /* |
| * Malloc a thread (hythread_t struct). |
| * |
| * Helper function used by the library's thread pool |
| * |
| * @param unused ignored |
| * @param size size of struct to be alloc'd |
| * @return pointer to the malloc'd memory<br> |
| * 0 on failure |
| * |
| */ |
| static void *VMCALL |
| thread_malloc (void *unused, U_32 size) |
| { |
| return malloc (size); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION thread_free |
| /* |
| * Free a thread (hythread_t struct) |
| * Function used by the library's thread pool |
| * |
| * @param unused ignored |
| * @param prt pointer to hythread_t to be freed |
| * @return none |
| * |
| */ |
| static void VMCALL |
| thread_free (void *unused, void *ptr) |
| { |
| free (ptr); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION free_monitor_pools |
| /* |
| * Free the Hy threading library's monitor pool. |
| * |
| * This requires destroying each and every one of the |
| * monitors in the pool. |
| * |
| * @return none |
| */ |
| static void |
| free_monitor_pools (void) |
| { |
| hythread_library_t lib = GLOBAL_DATA (default_library); |
| hythread_monitor_pool_t pool = lib->monitor_pool; |
| |
| ASSERT (lib); |
| ASSERT (pool); |
| |
| while (pool) |
| { |
| int i; |
| hythread_monitor_pool_t next = pool->next; |
| hythread_monitor_t entry = &pool->entries[0]; |
| for (i = 0; i < MONITOR_POOL_SIZE - 1; i++, entry++) |
| { |
| if (entry->flags != HYTHREAD_MONITOR_MUTEX_UNINITIALIZED) |
| { |
| MUTEX_DESTROY (entry->mutex); |
| } |
| } |
| free (pool); |
| pool = next; |
| } |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION init_global_monitor |
| /* |
| * Initialize the mutex used to synchronize access |
| * to thread library global data. |
| * |
| * @param[in] lib pointer to the thread library |
| * @return 0 on success or negative value on failure |
| * |
| */ |
| static IDATA |
| init_global_monitor (hythread_library_t lib) |
| { |
| hythread_monitor_pool_t pool = lib->monitor_pool; |
| hythread_monitor_t monitor = pool->next_free; |
| ASSERT (monitor); |
| pool->next_free = (hythread_monitor_t) monitor->owner; |
| |
| if (init_monitor (monitor, 0) != 0) |
| { |
| return -1; |
| } |
| if (!MUTEX_INIT (monitor->mutex)) |
| { |
| return -1; |
| } |
| |
| monitor->name = "Thread global"; |
| |
| *hythread_global ("global_monitor") = (UDATA) monitor; |
| |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION internal_exit |
| /* |
| * Exit from the current thread. |
| * |
| * If the thread has been detached it is destroyed. |
| */ |
| static void NORETURN |
| internal_exit (void) |
| { |
| |
| hythread_t self = MACRO_SELF (); |
| hythread_library_t lib = self->library; |
| int detached; |
| |
| ASSERT (self); |
| ASSERT (lib); |
| |
| tls_finalize (self); |
| |
| GLOBAL_LOCK (self, CALLER_INTERNAL_EXIT1); |
| THREAD_LOCK (self, self, CALLER_INTERNAL_EXIT1); |
| self->flags |= HYTHREAD_FLAG_DEAD; |
| detached = self->attachcount == 0; |
| |
| /* |
| * Is there an interruptServer thread out there |
| * trying to interrupt us? Its services are |
| * no longer required. |
| */ |
| if (self->interrupter) |
| { |
| THREAD_LOCK (self, self->interrupter, CALLER_INTERNAL_EXIT1); |
| self->interrupter->flags |= HYTHREAD_FLAG_CANCELED; |
| THREAD_UNLOCK (self, self->interrupter); |
| self->interrupter = NULL; |
| } |
| |
| THREAD_UNLOCK (self, self); |
| |
| /* On z/OS we create the thread in the detached state, so the */ |
| /* call to pthread_detach is not required. @dfa1 */ |
| THREAD_DETACH (self->handle); |
| |
| if (detached) |
| { |
| destroy_thread (self, GLOBAL_IS_LOCKED); |
| } |
| |
| GLOBAL_UNLOCK_SIMPLE (lib); |
| THREAD_EXIT (); |
| ASSERT (0); |
| /* UNREACHABLE */ |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION enqueue_thread |
| /* |
| * Add a thread to a monitor's wait queue. |
| * |
| * @note The calling thread must be the current owner of the monitor |
| * @param[in] queue head of the monitor's wait queue |
| * @param[in] thread thread to be added |
| * @return none |
| * |
| */ |
| static void |
| enqueue_thread (hythread_t * queue, hythread_t thread) |
| { |
| hythread_t qthread = *queue; |
| |
| ASSERT (thread); |
| /* can't be on two queues at the same time */ |
| ASSERT (NULL == thread->next); |
| |
| if (qthread != NULL) |
| { |
| while (qthread->next) |
| { |
| qthread = qthread->next; |
| } |
| qthread->next = thread; |
| } |
| else |
| { |
| *queue = thread; |
| } |
| |
| ASSERT (*queue != NULL); |
| ASSERT (NULL == thread->next); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_wait_timed |
| /** |
| * Wait on a monitor until notified or timed out. |
| * |
| * A timeout of 0 (0ms, 0ns) indicates wait indefinitely. |
| * |
| * @param[in] monitor a monitor to be waited on |
| * @param[in] millis >=0 |
| * @param[in] nanos >=0 |
| * |
| * @return 0 the monitor has been waited on, notified, and reobtained<br> |
| * HYTHREAD_INVALID_ARGUMENT millis or nanos is out of range (millis or nanos < 0, or nanos >= 1E6)<br> |
| * HYTHREAD_ILLEGAL_MONITOR_STATE the current thread does not own the monitor<br> |
| * HYTHREAD_TIMED_OUT the timeout expired |
| * |
| * @see hythread_monitor_wait, hythread_monitor_wait_interruptable, hythread_monitor_enter |
| * |
| */ |
| IDATA VMCALL |
| hythread_monitor_wait_timed (hythread_monitor_t monitor, I_64 millis, |
| IDATA nanos) |
| { |
| return monitor_wait (monitor, millis, nanos, WAIT_UNINTERRUPTABLE); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_dump_trace |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_dump_all |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_dump_trace |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_global |
| /** |
| * 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) |
| { |
| HyThreadGlobal *global; |
| hythread_library_t lib = GLOBAL_DATA (default_library); |
| |
| MUTEX_ENTER (lib->global_mutex); |
| |
| global = lib->globals; |
| |
| while (global) |
| { |
| if (strcmp (global->name, name) == 0) |
| { |
| MUTEX_EXIT (lib->global_mutex); |
| return &global->data; |
| } |
| global = global->next; |
| } |
| |
| /* |
| * If we got here, we couldn't find it, therefore |
| * we will create a new one |
| */ |
| |
| global = pool_newElement (lib->global_pool); |
| if (global == NULL) |
| { |
| MUTEX_EXIT (lib->global_mutex); |
| return NULL; |
| } |
| |
| global->next = lib->globals; |
| global->name = name; |
| global->data = 0; |
| lib->globals = global; |
| |
| MUTEX_EXIT (lib->global_mutex); |
| |
| return &global->data; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hysem_init |
| /* |
| * Initialize a semaphore. |
| * |
| * Acquire a semaphore from the threading library. |
| * |
| * @param[out] sp pointer to semaphore to be initialized |
| * @param[in] initValue initial count value (>=0) for the semaphore |
| * @return 0 on success or negative value on failure |
| * |
| * @deprecated Semaphores are no longer supported. |
| * |
| * @see hysem_destroy, hysem_init, hysem_post |
| */ |
| IDATA VMCALL |
| hysem_init (hysem_t * sp, I_32 initValue) |
| { |
| hysem_t s; |
| IDATA rc = -1; |
| |
| (*sp) = s = SEM_CREATE (initValue); |
| if (s) |
| { |
| rc = SEM_INIT (s, 0, initValue); |
| } |
| return rc; |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hysem_destroy |
| /* |
| * Destroy a semaphore. |
| * |
| * Returns the resources associated with a semaphore back to the Hy threading library. |
| * |
| * @param[in] s semaphore to be destroyed |
| * @return 0 on success or negative value on failure |
| * |
| * @deprecated Semaphores are no longer supported. |
| * |
| * @see hysem_init, hysem_wait, hysem_post |
| */ |
| IDATA VMCALL |
| hysem_destroy (hysem_t s) |
| { |
| int rval = 0; |
| if (s) |
| { |
| rval = SEM_DESTROY (s); |
| SEM_FREE (s); |
| } |
| return rval; |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hysem_post |
| /* |
| * Release a semaphore by 1. |
| * |
| * @param[in] s semaphore to be released by 1 |
| * @return 0 on success or negative value on failure |
| * |
| * @deprecated Semaphores are no longer supported. |
| * |
| * @see hysem_init, hysem_destroy, hysem_wait |
| */ |
| IDATA VMCALL |
| hysem_post (hysem_t s) |
| { |
| if (s) |
| { |
| return SEM_POST (s); |
| } |
| return -1; |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hysem_wait |
| /* |
| * Wait on a semaphore. |
| * |
| * @param[in] s semaphore to be waited on |
| * @return 0 on success or negative value on failure |
| * |
| * @deprecated Semaphores are no longer supported. |
| * |
| * @see hysem_init, hysem_destroy, hysem_wait |
| * |
| */ |
| IDATA VMCALL |
| hysem_wait (hysem_t s) |
| { |
| if (s) |
| { |
| while (SEM_WAIT (s) != 0) |
| { |
| /* loop until success */ |
| } |
| return 0; |
| } |
| else |
| { |
| return -1; |
| } |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION error |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION monitor_enter |
| /* |
| * Enter a monitor. |
| * |
| * A thread may enter a monitor it owns multiple times, but must |
| * exit the monitor the same number of times before other threads |
| * waiting on the monitor are permitted to continue |
| * |
| * @param[in] self current thread |
| * @param[in] monitor monitor to enter |
| * @return 0 on success<br> |
| * HYTHREAD_PRIORITY_INTERRUPTED if the thread was priority |
| * interrupted while blocked |
| */ |
| static IDATA |
| monitor_enter (hythread_t self, hythread_monitor_t monitor) |
| { |
| |
| ASSERT (self); |
| ASSERT (0 == self->monitor); |
| ASSERT (monitor); |
| ASSERT (monitor->owner != self); |
| ASSERT (FREE_TAG != monitor->count); |
| |
| return monitor_enter_three_tier (self, monitor); |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION monitor_enter_three_tier |
| |
| /* |
| * Enter a three-tier monitor. |
| * |
| * Spin on a spinlock. Block when that fails, and repeat. |
| * |
| * @param[in] self current thread |
| * @param[in] monitor monitor to enter |
| * @return 0 on success |
| */ |
| |
| static IDATA |
| monitor_enter_three_tier (hythread_t self, hythread_monitor_t monitor) |
| { |
| int firstTimeBlocking = 1; |
| |
| while (1) |
| { |
| |
| if (hythread_spinlock_acquire (self, monitor) == 0) |
| { |
| monitor->owner = self; |
| monitor->count = 1; |
| ASSERT (monitor->spinlockState != |
| HYTHREAD_MONITOR_SPINLOCK_UNOWNED); |
| break; |
| } |
| |
| MONITOR_LOCK (self, monitor, CALLER_MONITOR_ENTER_THREE_TIER1); |
| |
| if (HYTHREAD_MONITOR_SPINLOCK_UNOWNED == |
| hythread_spinlock_swapState (monitor, |
| HYTHREAD_MONITOR_SPINLOCK_EXCEEDED)) |
| { |
| MONITOR_UNLOCK (self, monitor); |
| monitor->owner = self; |
| monitor->count = 1; |
| ASSERT (monitor->spinlockState != |
| HYTHREAD_MONITOR_SPINLOCK_UNOWNED); |
| break; |
| } |
| |
| THREAD_LOCK (self, self, CALLER_MONITOR_ENTER_THREE_TIER2); |
| self->flags |= (HYTHREAD_FLAG_BLOCKED); |
| self->monitor = monitor; |
| THREAD_UNLOCK (self, self); |
| |
| /* |
| * First time we've had to block? |
| * If so, record the info for JLM. |
| */ |
| if (IS_JLM_ENABLED (self)) |
| { |
| if (firstTimeBlocking) |
| { |
| firstTimeBlocking = 0; |
| |
| } |
| } |
| |
| enqueue_thread (&monitor->blocking, self); |
| COND_WAIT (self->condition, monitor->mutex); |
| break; |
| COND_WAIT_LOOP (); |
| remove_from_queue (&monitor->blocking, self); |
| |
| MONITOR_UNLOCK (self, monitor); |
| |
| } |
| |
| /* We now own the monitor */ |
| |
| /* |
| * If the monitor field is set, we must have blocked on it |
| * at some point. We're no longer blocked, so clear this. |
| */ |
| if (self->monitor != 0) |
| { |
| THREAD_LOCK (self, self, CALLER_MONITOR_ENTER_THREE_TIER3); |
| self->flags &= ~(HYTHREAD_FLAG_BLOCKED); |
| self->monitor = 0; |
| THREAD_UNLOCK (self, self); |
| } |
| |
| /* Did we block? If so, finish up the JLM calcs */ |
| /* TODO: this is pretty much the same as in monitor_enter.... */ |
| if (IS_JLM_ENABLED (self)) |
| { |
| monitor->tracing->enter_count++; |
| if (0 == firstTimeBlocking) |
| { |
| monitor->tracing->slow_count++; |
| |
| } |
| } |
| |
| ASSERT (!(self->flags & HYTHREAD_FLAG_BLOCKED)); |
| ASSERT (0 == self->monitor); |
| |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION monitor_exit |
| /* |
| * Exit a monitor. |
| * |
| * If the current thread is not the owner of the monitor, the |
| * mutex is unaffected, and an error is returned. This should be |
| * tested to determine if IllegalMonitorState should be |
| * thrown. |
| * |
| * @param[in] self current thread |
| * @param[in] monitor monitor to be exited |
| * @return 0 on success<br> |
| * HYTHREAD_ILLEGAL_MONITOR_STATE if the current thread does not |
| * own the monitor |
| */ |
| static IDATA |
| monitor_exit (hythread_t self, hythread_monitor_t monitor) |
| { |
| |
| ASSERT (monitor); |
| ASSERT (self); |
| ASSERT (0 == self->monitor); |
| |
| if (monitor->owner != self) |
| { |
| ASSERT_DEBUG (0); |
| return HYTHREAD_ILLEGAL_MONITOR_STATE; |
| } |
| |
| monitor->count--; |
| ASSERT (monitor->count >= 0); |
| |
| if (monitor->count == 0) |
| { |
| monitor->owner = NULL; |
| |
| if (HYTHREAD_MONITOR_SPINLOCK_EXCEEDED == |
| hythread_spinlock_swapState (monitor, |
| HYTHREAD_MONITOR_SPINLOCK_UNOWNED)) |
| { |
| MONITOR_LOCK (self, monitor, CALLER_MONITOR_EXIT1); |
| unblock_spinlock_threads (self, monitor); |
| MONITOR_UNLOCK (self, monitor); |
| } |
| |
| } |
| |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION unblock_spinlock_threads |
| |
| /* |
| * Notify all threads blocked on the monitor's mutex, waiting |
| * to be told that it's ok to try again to get the spinlock. |
| * |
| * Assumes that the caller already owns the monitor's mutex. |
| * |
| */ |
| static void |
| unblock_spinlock_threads (hythread_t self, hythread_monitor_t monitor) |
| { |
| hythread_t queue, next; |
| |
| ASSERT (self); |
| ASSERT (monitor); |
| |
| next = monitor->blocking; |
| while (next) |
| { |
| queue = next; |
| next = queue->next; |
| COND_NOTIFY_ALL (queue->condition); |
| } |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_reset_tracing |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_wait_interruptable |
| /** |
| * Wait on a monitor until notified, interrupted (priority or normal), or timed out. |
| * |
| * A timeout of 0 (0ms, 0ns) indicates wait indefinitely. |
| * |
| * If 'interruptable' is non-zero, the wait may be interrupted by one of the |
| * interrupt functions. (i.e. hythread_interrupt, hythread_priority_interrupt); |
| * |
| * @param[in] monitor a monitor to be waited on |
| * @param[in] millis >=0 |
| * @param[in] nanos >=0 |
| * @param[in] interruptable non-zero if the wait is to be interruptable |
| * |
| * @return 0 the monitor has been waited on, notified, and reobtained<br> |
| * HYTHREAD_INVALID_ARGUMENT if millis or nanos is out of range (millis or nanos < 0, or nanos >= 1E6)<br> |
| * HYTHREAD_ILLEGAL_MONITOR_STATE if the current thread does not own the monitor<br> |
| * HYTHREAD_INTERRUPTED if the thread was interrupted while waiting<br> |
| * HYTHREAD_PRIORITY_INTERRUPTED if the thread was priority interrupted while waiting, or while re-obtaining the monitor<br> |
| * HYTHREAD_TIMED_OUT if the timeout expired<br> |
| * |
| * @see hythread_monitor_wait, hythread_monitor_wait_timed, hythread_monitor_enter |
| * @see hythread_interrupt, hythread_priority_interrupt * |
| */ |
| IDATA VMCALL |
| hythread_monitor_wait_interruptable (hythread_monitor_t monitor, I_64 millis, |
| IDATA nanos) |
| { |
| return monitor_wait (monitor, millis, nanos, WAIT_INTERRUPTABLE); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_num_waiting |
| /** |
| * Returns how many threads are currently waiting on a monitor. |
| * |
| * @note This can only be called by the owner of this monitor. |
| * |
| * @param[in] monitor a monitor |
| * @return number of threads waiting on the monitor (>=0) |
| */ |
| UDATA VMCALL |
| hythread_monitor_num_waiting (hythread_monitor_t monitor) |
| { |
| UDATA numWaiting = 0; |
| hythread_t curr; |
| hythread_t self = MACRO_SELF (); |
| |
| ASSERT (monitor); |
| |
| MONITOR_LOCK (self, monitor, CALLER_MONITOR_NUM_WAITING); |
| |
| curr = monitor->waiting; |
| while (curr != NULL) |
| { |
| numWaiting++; |
| curr = curr->next; |
| } |
| |
| MONITOR_UNLOCK (self, monitor); |
| |
| return numWaiting; |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION monitor_wait |
| /* |
| * |
| * Wait on a monitor. |
| * |
| * Release the monitor, wait for a signal (notification), then re-acquire the monitor. |
| * |
| * In this function, we 'unwind' any recursive hold (monitor->count) the thread has |
| * on the monitor and release the OS monitor. When the monitor is re-acquired, |
| * the recursive count is restored to its original value. |
| * |
| * A timeout of 0 (0ms, 0ns) indicates wait indefinitely. |
| * |
| * If 'interruptable' is non-zero, the wait may be interrupted by one of the |
| * interrupt functions. (i.e. hythread_interrupt, hythread_priority_interrupt); |
| * |
| * @param[in] monitor monitor to be waited on |
| * @param[in] millis >=0 |
| * @param[in] nanos >=0 |
| * @param[in] interruptable non-zero if the wait is to be interruptable |
| * |
| * @return HYTHREAD_INVALID_ARGUMENT - if millis or nanos is out of range (millis or nanos < 0, or nanos >= 1E6) |
| * HYTHREAD_ILLEGAL_MONITOR_STATE - the current thread does not own the monitor |
| * 0 - the monitor has been waited on, notified, and reobtained |
| * HYTHREAD_INTERRUPTED - the thread was interrupted while waiting |
| * HYTHREAD_PRIORITY_INTERRUPTED - if the thread was priority interrupted while waiting, or while re-obtaining the monitor |
| * HYTHREAD_TIMED_OUT - the timeout expired |
| * |
| * @see hythread_monitor_wait, hythread_monitor_wait_interruptable, hythread_monitor_enter |
| * @see hythread_interrupt, hythread_priority_interrupt |
| */ |
| static IDATA |
| monitor_wait (hythread_monitor_t monitor, I_64 millis, IDATA nanos, |
| IDATA interruptable) |
| { |
| hythread_t self = MACRO_SELF (); |
| IDATA count = -1; |
| UDATA flags; |
| UDATA interrupted = 0, notified = 0, priorityinterrupted = 0; |
| UDATA timedOut = 0; |
| |
| ASSERT (monitor); |
| ASSERT (FREE_TAG != monitor->count); |
| |
| if (monitor->owner != self) |
| { |
| ASSERT_DEBUG (0); |
| return HYTHREAD_ILLEGAL_MONITOR_STATE; |
| } |
| |
| if ((millis < 0) || (nanos < 0) || (nanos >= 1000000)) |
| { |
| ASSERT_DEBUG (0); |
| return HYTHREAD_INVALID_ARGUMENT; |
| } |
| |
| count = monitor->count; |
| flags = monitor->flags; |
| |
| THREAD_LOCK (self, self, CALLER_MONITOR_WAIT1); |
| ASSERT (0 == self->monitor); |
| |
| /* |
| * Before we wait, check if we've already been either interrupted or pri interrupted |
| */ |
| if (interruptable && (self->flags & HYTHREAD_FLAG_INTERRUPTED)) |
| { |
| self->flags &= ~HYTHREAD_FLAG_INTERRUPTED; |
| THREAD_UNLOCK (self, self); |
| return HYTHREAD_INTERRUPTED; |
| } |
| |
| if (interruptable && (self->flags & HYTHREAD_FLAG_PRIORITY_INTERRUPTED)) |
| { |
| self->flags &= ~HYTHREAD_FLAG_PRIORITY_INTERRUPTED; |
| THREAD_UNLOCK (self, self); |
| return HYTHREAD_PRIORITY_INTERRUPTED; |
| } |
| |
| self->flags |= |
| (HYTHREAD_FLAG_WAITING | |
| (interruptable ? HYTHREAD_FLAG_INTERRUPTABLE : 0)); |
| if (millis || nanos) |
| { |
| self->flags |= HYTHREAD_FLAG_TIMER_SET; |
| } |
| self->monitor = monitor; |
| THREAD_UNLOCK (self, self); |
| |
| ASSERT (self->flags & HYTHREAD_FLAG_WAITING); |
| monitor->owner = NULL; |
| monitor->count = 0; |
| |
| MONITOR_LOCK (self, monitor, CALLER_MONITOR_WAIT); |
| if (HYTHREAD_MONITOR_SPINLOCK_EXCEEDED == |
| hythread_spinlock_swapState (monitor, |
| HYTHREAD_MONITOR_SPINLOCK_UNOWNED)) |
| { |
| unblock_spinlock_threads (self, monitor); |
| } |
| |
| enqueue_thread (&monitor->waiting, self); |
| |
| if (millis || nanos) |
| { |
| IDATA boundedMillis = BOUNDED_I64_TO_IDATA (millis); |
| |
| COND_WAIT_IF_TIMEDOUT (self->condition, monitor->mutex, boundedMillis, |
| nanos) |
| { |
| |
| THREAD_LOCK (self, self, CALLER_MONITOR_WAIT2); |
| interrupted = interruptable |
| && ((self->flags & HYTHREAD_FLAG_INTERRUPTED) != 0); |
| priorityinterrupted = interruptable |
| && ((self->flags & HYTHREAD_FLAG_PRIORITY_INTERRUPTED) != 0); |
| notified = self->flags & HYTHREAD_FLAG_NOTIFIED; |
| if (!(interrupted || priorityinterrupted || notified)) |
| { |
| timedOut = 1; |
| } |
| break; |
| } |
| else |
| { |
| |
| THREAD_LOCK (self, self, CALLER_MONITOR_WAIT2); |
| interrupted = interruptable |
| && ((self->flags & HYTHREAD_FLAG_INTERRUPTED) != 0); |
| priorityinterrupted = interruptable |
| && ((self->flags & HYTHREAD_FLAG_PRIORITY_INTERRUPTED) != 0); |
| notified = self->flags & HYTHREAD_FLAG_NOTIFIED; |
| if (interrupted || priorityinterrupted || notified) |
| { |
| break; |
| } |
| /* must have been spurious */ |
| ASSERT_DEBUG (0); |
| THREAD_UNLOCK (self, self); |
| } |
| COND_WAIT_TIMED_LOOP (); |
| } |
| else |
| { |
| /* |
| * WAIT UNTIL NOTIFIED |
| */ |
| |
| COND_WAIT (self->condition, monitor->mutex); |
| |
| THREAD_LOCK (self, self, CALLER_MONITOR_WAIT2); |
| interrupted = interruptable |
| && ((self->flags & HYTHREAD_FLAG_INTERRUPTED) != 0); |
| priorityinterrupted = interruptable |
| && ((self->flags & HYTHREAD_FLAG_PRIORITY_INTERRUPTED) != 0); |
| notified = self->flags & HYTHREAD_FLAG_NOTIFIED; |
| if (interrupted || priorityinterrupted || notified) |
| { |
| break; |
| } |
| /* must have been spurious */ |
| ASSERT_DEBUG (0); |
| THREAD_UNLOCK (self, self); |
| COND_WAIT_LOOP (); |
| } |
| |
| /* DONE WAITING AT THIS POINT */ |
| |
| /* we have to remove self from the wait queue */ |
| remove_from_queue (&monitor->waiting, self); |
| |
| MONITOR_UNLOCK (self, monitor); |
| |
| /* at this point, this thread should already be locked */ |
| ASSERT (notified || interrupted || priorityinterrupted || timedOut); |
| ASSERT (!interrupted || interruptable); /* if we were interrupted, then we'd better have been interruptable */ |
| |
| self->flags &= |
| ~(HYTHREAD_FLAG_WAITING | HYTHREAD_FLAG_NOTIFIED | |
| HYTHREAD_FLAG_PRIORITY_INTERRUPTED | HYTHREAD_FLAG_INTERRUPTABLE | |
| HYTHREAD_FLAG_TIMER_SET); |
| |
| if (interrupted && !(notified || priorityinterrupted)) |
| self->flags &= ~HYTHREAD_FLAG_INTERRUPTED; |
| |
| /* |
| * Is there an interruptServer thread out there |
| * trying to interrupt us? Its services are |
| * no longer required. |
| */ |
| if (self->interrupter) |
| { |
| ASSERT (interrupted || priorityinterrupted); |
| THREAD_LOCK (self, self->interrupter, CALLER_MONITOR_WAIT2); |
| self->interrupter->flags |= HYTHREAD_FLAG_CANCELED; |
| THREAD_UNLOCK (self, self->interrupter); |
| self->interrupter = NULL; |
| } |
| |
| THREAD_UNLOCK (self, self); |
| |
| monitor_enter_three_tier (self, monitor); |
| |
| monitor->count = count; |
| |
| ASSERT (monitor->owner == self); |
| ASSERT (monitor->count == count); |
| ASSERT (monitor->count >= 1); |
| ASSERT (0 == self->monitor); |
| ASSERT (!(monitor->flags & HYTHREAD_FLAG_WAITING)); |
| ASSERT (!(monitor->flags & HYTHREAD_FLAG_TIMER_SET)); |
| ASSERT (!(monitor->flags & HYTHREAD_FLAG_BLOCKED)); |
| ASSERT (!(monitor->flags & HYTHREAD_FLAG_NOTIFIED)); |
| ASSERT (!(monitor->flags & HYTHREAD_FLAG_INTERRUPTABLE)); |
| ASSERT (NULL == self->next); |
| |
| if (priorityinterrupted) |
| return HYTHREAD_PRIORITY_INTERRUPTED; |
| if (notified) |
| return 0; |
| if (interrupted) |
| return HYTHREAD_INTERRUPTED; |
| if (timedOut) |
| return HYTHREAD_TIMED_OUT; |
| ASSERT (0); |
| return 0; |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION interrupt_thread |
| /* |
| * Interrupt a thread. |
| * |
| * If the thread is currently blocked (e.g. waiting on monitor_wait) |
| * resume the thread and return from the blocking function with |
| * HYTHREAD_INTERRUPTED or HYTHREAD_PRIORITY_INTERRUPTED |
| * |
| * If it can't be resumed (it's not in an interruptable state) |
| * then just set the appropriate interrupt flag. |
| * |
| * @param[in] thread thread to be interrupted |
| * @param[in] interruptFlag indicated whether to priority interrupt or just normally interrupt. |
| * @return none |
| */ |
| static void |
| interrupt_thread (hythread_t thread, UDATA interruptFlag) |
| { |
| UDATA currFlags, newFlags; |
| hythread_t self = MACRO_SELF (); |
| |
| ASSERT (self); |
| ASSERT (thread); |
| |
| GLOBAL_LOCK (self, CALLER_INTERRUPT_THREAD); |
| THREAD_LOCK (self, thread, CALLER_INTERRUPT_THREAD); |
| if (thread->flags & interruptFlag) |
| { |
| THREAD_UNLOCK (self, thread); |
| GLOBAL_UNLOCK (self); |
| return; |
| } |
| |
| currFlags = thread->flags; |
| newFlags = currFlags | interruptFlag; |
| if (currFlags & HYTHREAD_FLAG_INTERRUPTABLE) |
| { |
| if (currFlags & (HYTHREAD_FLAG_SLEEPING | HYTHREAD_FLAG_PARKED)) |
| { |
| COND_NOTIFY_ALL (thread->condition); |
| } |
| else if (currFlags & HYTHREAD_FLAG_WAITING) |
| { |
| if (interrupt_waiting_thread (self, thread)) |
| { |
| newFlags |= HYTHREAD_FLAG_BLOCKED; |
| } |
| } |
| } |
| |
| thread->flags = newFlags; |
| THREAD_UNLOCK (self, thread); |
| GLOBAL_UNLOCK (self); |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION interrupt_waiting_thread |
| /* |
| * Interrupt a waiting thread. |
| * |
| * @param[in] self current thread |
| * @param[in] threadToInterrupt |
| * @return 1 if the thread was immediately interrupted<br> |
| * 0 if the thread will be interrupted asap by a special thread. |
| * @note: Assumes caller has locked the global mutex |
| */ |
| static IDATA |
| interrupt_waiting_thread (hythread_t self, hythread_t threadToInterrupt) |
| { |
| |
| IDATA retVal = 0; |
| hythread_monitor_t monitor; |
| |
| ASSERT (self); |
| ASSERT (threadToInterrupt); |
| ASSERT (self != threadToInterrupt); |
| ASSERT (threadToInterrupt->flags & HYTHREAD_FLAG_INTERRUPTABLE); |
| ASSERT (threadToInterrupt->monitor); |
| ASSERT (NULL == threadToInterrupt->interrupter); |
| |
| #if !defined(ALWAYS_SPAWN_THREAD_TO_INTERRUPT) |
| /* |
| * If we can enter the monitor without blocking, we don't need the |
| * interruptServer thread |
| */ |
| monitor = threadToInterrupt->monitor; |
| if (hythread_monitor_try_enter_using_threadId (monitor, self) == 0) |
| { |
| ASSERT (monitor->owner == self); |
| COND_NOTIFY_ALL (threadToInterrupt->condition); |
| hythread_monitor_exit_using_threadId (monitor, self); |
| retVal = 1; |
| } |
| else |
| #endif |
| |
| { |
| /* |
| * spawn a thread to do it for us, because it's possible that |
| * having this thread lock the waiting thread's monitor may |
| * cause deadlock |
| */ |
| create_thread (&threadToInterrupt->interrupter, 0, |
| HYTHREAD_PRIORITY_NORMAL, 0, interruptServer, |
| (void *) threadToInterrupt, GLOBAL_IS_LOCKED); |
| } |
| return retVal; |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION interruptServer |
| /* |
| * Interrupt a thread waiting on a monitor. |
| * |
| * This function serves as the entry point for a |
| * thread whose sole purpose is to interrupt another |
| * thread. |
| * |
| * @param[in] entryArg pointer to the thread to interrupt (non-NULL) |
| * @return 0 |
| */ |
| static I_32 HYTHREAD_PROC |
| interruptServer (void *entryArg) |
| { |
| hythread_t self = MACRO_SELF (); |
| hythread_t threadToInterrupt = (hythread_t) entryArg; |
| hythread_monitor_t monitor; |
| |
| ASSERT (threadToInterrupt); |
| ASSERT (self); |
| |
| GLOBAL_LOCK (self, CALLER_INTERRUPT_SERVER); |
| |
| /* |
| * Did the thread to interrupt die or come out of wait already? |
| * If it did, it cancelled this thread (set our CANCELED bit) |
| */ |
| if (self->flags & HYTHREAD_FLAG_CANCELED) |
| { |
| GLOBAL_UNLOCK (self); |
| hythread_exit (NULL); /* this should not return */ |
| } |
| |
| THREAD_LOCK (self, threadToInterrupt, CALLER_INTERRUPT_SERVER); |
| |
| if (threadToInterrupt->interrupter != self) |
| { |
| THREAD_UNLOCK (self, threadToInterrupt); |
| GLOBAL_UNLOCK (self); |
| hythread_exit (NULL); /* this should not return */ |
| } |
| |
| monitor = threadToInterrupt->monitor; |
| ASSERT (monitor); |
| ASSERT (threadToInterrupt->flags & HYTHREAD_FLAG_WAITING); |
| |
| hythread_monitor_pin (monitor, self); |
| THREAD_UNLOCK (self, threadToInterrupt); |
| GLOBAL_UNLOCK (self); |
| |
| /* try to take the monitor so that we can notify the thread to interrupt */ |
| hythread_monitor_enter (monitor); |
| |
| GLOBAL_LOCK (self, CALLER_INTERRUPT_SERVER); |
| hythread_monitor_unpin (monitor, self); |
| |
| /* Did the thread to interrupt die or come out of wait already? */ |
| if (self->flags & HYTHREAD_FLAG_CANCELED) |
| { |
| GLOBAL_UNLOCK (self); |
| hythread_exit (monitor); /* this should not return */ |
| ASSERT (0); |
| } |
| |
| THREAD_LOCK (self, threadToInterrupt, CALLER_INTERRUPT_SERVER); |
| if ((threadToInterrupt->interrupter == self) |
| && (threadToInterrupt->flags & HYTHREAD_FLAG_WAITING)) |
| { |
| notify_thread (threadToInterrupt, DONT_SET_NOTIFIED_FLAG); |
| } |
| threadToInterrupt->interrupter = NULL; |
| ASSERT (threadToInterrupt->flags & HYTHREAD_FLAG_INTERRUPTED); |
| THREAD_UNLOCK (self, threadToInterrupt); |
| |
| GLOBAL_UNLOCK (self); |
| hythread_exit (monitor); |
| |
| ASSERT (0); |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_exit_using_threadId |
| /** |
| * Exit a monitor. |
| * |
| * This is a slightly faster version of hythread_monitor_exit because |
| * the hythread_t for the current thread doesn't have to be looked up |
| * |
| * @param[in] monitor a monitor to be exited |
| * @param[in] threadId hythread_t for the current thread |
| * @return 0 on success<br> |
| * HYTHREAD_ILLEGAL_MONITOR_STATE if the current thread does not own the monitor |
| * |
| * @see hythread_monitor_exit, hythread_monitor_enter, hythread_monitor_enter_using_threadId |
| */ |
| IDATA VMCALL |
| hythread_monitor_exit_using_threadId (hythread_monitor_t monitor, |
| hythread_t threadId) |
| { |
| ASSERT (threadId == MACRO_SELF ()); |
| ASSERT (monitor); |
| ASSERT (FREE_TAG != monitor->count); |
| |
| if (monitor->owner != threadId) |
| { |
| ASSERT_DEBUG (0); |
| return HYTHREAD_ILLEGAL_MONITOR_STATE; |
| } |
| |
| return monitor_exit (threadId, monitor); |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_enter_using_threadId |
| /** |
| * Enter a monitor. |
| * |
| * This is a slightly faster version of hythread_monitor_enter because |
| * the hythread_t for the current thread doesn't have to be looked up |
| * |
| * @param[in] monitor a monitor to be entered |
| * @param[in] threadId hythread_t for the current thread |
| * @return 0 on success<br> |
| * HYTHREAD_PRIORITY_INTERRUPTED if the thread was priority interrupted while blocked |
| * |
| * @see hythread_monitor_enter, hythread_monitor_exit, hythread_monitor_exit_using_threadId |
| * |
| */ |
| IDATA VMCALL |
| hythread_monitor_enter_using_threadId (hythread_monitor_t monitor, |
| hythread_t threadId) |
| { |
| ASSERT (threadId != 0); |
| ASSERT (threadId == MACRO_SELF ()); |
| ASSERT (monitor); |
| ASSERT (FREE_TAG != monitor->count); |
| |
| if (monitor->owner == threadId) |
| { |
| ASSERT (monitor->count >= 1); |
| monitor->count++; |
| |
| if (IS_JLM_ENABLED (threadId)) |
| { |
| ASSERT (monitor->tracing); |
| monitor->tracing->recursive_count++; |
| monitor->tracing->enter_count++; |
| } /* if (IS_JLM_ENABLED(threadId)) */ |
| |
| return 0; |
| } |
| return monitor_enter (threadId, monitor); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_try_enter_using_threadId |
| /** |
| * Attempt to enter a monitor without blocking. |
| * |
| * If the thread must block before it enters the monitor this function |
| * returns immediately with a negative value to indicate failure.<br> |
| * |
| * This is a slightly faster version of hythread_monitor_try_enter because |
| * the current thread's hythread_t doesn't have to be looked up. |
| * |
| * @param[in] monitor a monitor |
| * @param[in] threadId the current thread |
| * @return 0 on success or negative value on failure |
| * |
| * @see hythread_monitor_try_enter |
| * |
| */ |
| IDATA VMCALL |
| hythread_monitor_try_enter_using_threadId (hythread_monitor_t monitor, |
| hythread_t threadId) |
| { |
| |
| ASSERT (threadId != 0); |
| ASSERT (threadId == MACRO_SELF ()); |
| ASSERT (FREE_TAG != monitor->count); |
| |
| /* Are we already the owner? */ |
| if (monitor->owner == threadId) |
| { |
| ASSERT (monitor->count >= 1); |
| monitor->count++; |
| |
| if (IS_JLM_ENABLED (threadId)) |
| { |
| monitor->tracing->recursive_count++; |
| monitor->tracing->enter_count++; |
| } /* if (IS_JLM_ENABLED(threadId)) */ |
| |
| return 0; |
| } |
| |
| if (hythread_spinlock_acquire (threadId, monitor) == 0) |
| |
| { |
| ASSERT (NULL == monitor->owner); |
| ASSERT (0 == monitor->count); |
| |
| monitor->owner = threadId; |
| monitor->count = 1; |
| |
| if (IS_JLM_ENABLED (threadId)) |
| { |
| monitor->tracing->enter_count++; |
| |
| } /* if (IS_JLM_ENABLED(threadId)) */ |
| |
| return 0; |
| } |
| |
| return -1; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_probe |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION destroy_thread |
| /* |
| * Destroy the resources associated with the thread. |
| * |
| * If the thread is not already dead, the function fails. |
| * |
| * @param[in] thread thread to be destroyed |
| * @param[in] globalAlreadyLocked indicated whether the thread library global mutex is already locked |
| * @return 0 on success or negative value on failure. |
| */ |
| static IDATA |
| destroy_thread (hythread_t thread, int globalAlreadyLocked) |
| { |
| |
| hythread_t self = MACRO_SELF (); |
| hythread_library_t lib = self->library; |
| |
| ASSERT (thread); |
| ASSERT (lib); |
| |
| THREAD_LOCK (self, thread, CALLER_DESTROY); |
| if ((thread->flags & HYTHREAD_FLAG_DEAD) == 0) |
| { |
| THREAD_UNLOCK (self, thread); |
| return -1; |
| } |
| THREAD_UNLOCK (self, thread); |
| |
| COND_DESTROY (thread->condition); |
| |
| MUTEX_DESTROY (thread->mutex); |
| |
| #if defined(WIN32) |
| if (thread->flags & HYTHREAD_FLAG_ATTACHED) |
| { |
| CloseHandle (thread->handle); |
| } |
| #endif |
| |
| free_thread (thread, globalAlreadyLocked); |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_jlm_thread_init |
| |
| /* |
| * Initialize and clear a thread's JLM tracing information. |
| * |
| * Tracing information for the thread is allocated if not already allocated |
| * The library's thread tracing pool is initialized if not already initialized. |
| * |
| * @param[in] thread thread to be initialized |
| * @return none |
| * |
| */ |
| void VMCALL |
| hythread_jlm_thread_init (hythread_t thread) |
| { |
| hythread_library_t library = thread->library; |
| |
| ASSERT (thread); |
| ASSERT (library); |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_jlm_thread_clear |
| |
| /* |
| * Clear (reset) a thread's JLM tracing structure. |
| * |
| * Assumes the thread's tracing structure has already been allocated. |
| * |
| * @param[in] thread thread to be initialized (non-NULL) |
| * @return none |
| * |
| */ |
| void VMCALL |
| hythread_jlm_thread_clear (hythread_t thread) |
| { |
| ASSERT (thread); |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_jlm_monitor_init |
| |
| /* |
| * Initialize and clear a monitor's JLM tracing information. |
| * |
| * Tracing information for the monitor is allocated if not already allocated |
| * The library's monitor tracing pool is initialized if not already initialized. |
| * |
| * @param[in] monitor monitor to be initialized |
| * @return none |
| * |
| */ |
| void VMCALL |
| hythread_jlm_monitor_init (hythread_monitor_t monitor) |
| { |
| hythread_t self = MACRO_SELF (); |
| hythread_library_t library; |
| ASSERT (self); |
| ASSERT (monitor); |
| library = self->library; |
| ASSERT (library); |
| |
| if (library->monitor_tracing_pool == NULL) |
| { |
| library->monitor_tracing_pool = |
| pool_new (sizeof (HyThreadMonitorTracing), 0, 0, 0, thread_malloc, |
| thread_free, NULL); |
| } |
| |
| if (monitor->tracing == NULL) |
| { |
| /* cannot have been set, so set it now */ |
| monitor->tracing = pool_newElement (library->monitor_tracing_pool); |
| } |
| |
| hythread_jlm_monitor_clear (monitor); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_jlm_monitor_clear |
| |
| /* |
| * Clear (reset) a monitor's JLM tracing structure. |
| * |
| * Assumes the monitor's tracing structure has already been allocated. |
| * |
| * @param[in] monitor a monitor to be initialized |
| * @return none |
| */ |
| void VMCALL |
| hythread_jlm_monitor_clear (hythread_monitor_t monitor) |
| { |
| ASSERT (monitor); |
| ASSERT (monitor->tracing); |
| memset (monitor->tracing, 0, sizeof (*monitor->tracing)); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION init_monitor |
| /* |
| * Re-initialize the 'simple' fields of a monitor |
| * that has been initialized previously, but is now |
| * being re-used. |
| * |
| * @param[in] monitor monitor to be initialized |
| * @return 0 on success or negative value on failure. |
| * @see hythread_monitor_init_with_name |
| * |
| */ |
| static UDATA |
| init_monitor (hythread_monitor_t monitor, UDATA flags) |
| { |
| ASSERT (monitor); |
| monitor->count = 0; |
| monitor->owner = NULL; |
| monitor->waiting = NULL; |
| monitor->flags = flags; |
| monitor->userData = 0; |
| monitor->name = 0; |
| monitor->pinCount = 0; |
| |
| monitor->proDeflationCount = 0; |
| monitor->antiDeflationCount = 0; |
| |
| monitor->blocking = NULL; |
| monitor->spinlockState = HYTHREAD_MONITOR_SPINLOCK_UNOWNED; |
| monitor->lockingWord = 0; |
| /* these numbers are taken from the GC spinlock. They probably need to be tuned more (dynamically?) */ |
| /* note that every spinCount must be > 0! */ |
| |
| monitor->spinCount1 = 256; |
| monitor->spinCount2 = 32; |
| monitor->spinCount3 = 45; |
| |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION getCurrentCycles |
| /* |
| * Return the cycle count on the current processor. |
| * |
| * Units will be platform dependent. |
| * @todo This will be implmented in builder |
| */ |
| static hytime_t |
| getCurrentCycles (void) |
| { |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_lib_get_flags |
| |
| /** |
| * Get threading library global flags. |
| * |
| * Returns the flags for the threading library associated with the current thread. |
| * |
| * @note: assumes caller has global lock |
| * |
| * @see hythread_lib_clear_flags, hythread_lib_set_flags, hythread_lib_lock |
| * @return current flags value |
| */ |
| UDATA VMCALL |
| hythread_lib_get_flags () |
| { |
| hythread_t self; |
| self = MACRO_SELF (); |
| |
| ASSERT (self); |
| ASSERT (self->library); |
| |
| return self->library->flags; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_lib_set_flags |
| |
| /** |
| * Set threading library global flags. |
| * |
| * Sets the flags for the threading library associated with the current thread. |
| * |
| * @param[in] flags flags to be set (bit vector: 1 means set the flag, 0 means ignore) |
| * @return old flags values |
| * @see hythread_lib_clear_flags, hythread_lib_get_flags |
| * |
| */ |
| UDATA VMCALL |
| hythread_lib_set_flags (UDATA flags) |
| { |
| hythread_t self; |
| UDATA oldFlags; |
| self = MACRO_SELF (); |
| |
| ASSERT (self); |
| ASSERT (self->library); |
| |
| GLOBAL_LOCK (self, CALLER_LIB_SET_FLAGS); |
| oldFlags = self->library->flags; |
| self->library->flags |= flags; |
| GLOBAL_UNLOCK (self); |
| |
| return oldFlags; |
| |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_lib_clear_flags |
| |
| /** |
| * Clear specified threading library global flags. |
| * |
| * @see hythread_lib_set_flags, hythread_lib_get_flags |
| * @param[in] flags flags to be cleared (bit vector: 1 means clear the flag, 0 means ignore) |
| * @return old flags values |
| */ |
| UDATA VMCALL |
| hythread_lib_clear_flags (UDATA flags) |
| { |
| hythread_t self; |
| UDATA oldFlags; |
| self = MACRO_SELF (); |
| |
| ASSERT (self); |
| ASSERT (self->library); |
| |
| GLOBAL_LOCK (self, CALLER_LIB_CLEAR_FLAGS); |
| oldFlags = self->library->flags; |
| self->library->flags &= ~flags; |
| GLOBAL_UNLOCK (self); |
| |
| return oldFlags; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_monitor_init_with_name |
| /** |
| * Acquire and initialize a new monitor from the threading library. |
| * |
| * @param[out] handle pointer to a hythread_monitor_t to be set to point to the new monitor |
| * @param[in] flags initial flag values for the monitor |
| * @param[in] name pointer to a C string with a description of how the monitor will be used (may be NULL)<br> |
| * If non-NULL, the C string must be valid for the entire life of the monitor |
| * |
| * @return 0 on success or negative value on failure |
| * |
| * @see hythread_monitor_destroy |
| * |
| */ |
| IDATA VMCALL |
| hythread_monitor_init_with_name (hythread_monitor_t * handle, UDATA flags, |
| const char *name) |
| { |
| ASSERT (handle); |
| |
| if (hythread_monitor_init (handle, flags) == 0) |
| { |
| (*handle)->name = name; |
| return 0; |
| } |
| |
| return -1; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_jlm_gc_lock_init |
| |
| /* |
| * Initialize pools and tracing structures for JLM tracing. |
| * |
| * Can be called multiple times. |
| * |
| * @return none |
| */ |
| void VMCALL |
| hythread_jlm_gc_lock_init () |
| { |
| hythread_t self = MACRO_SELF (); |
| hythread_library_t library; |
| ASSERT (self); |
| library = self->library; |
| ASSERT (library); |
| |
| /* If no monitor_tracing pool yet, create it */ |
| if (library->monitor_tracing_pool == NULL) |
| { |
| library->monitor_tracing_pool = |
| pool_new (sizeof (HyThreadMonitorTracing), 0, 0, 0, thread_malloc, |
| thread_free, NULL); |
| } |
| |
| /* If no GC lock tracing pool yet, create it */ |
| if (library->gc_lock_tracing == NULL) |
| { |
| library->gc_lock_tracing = |
| pool_newElement (library->monitor_tracing_pool); |
| } |
| |
| /* Init the tracing structure */ |
| memset (library->gc_lock_tracing, 0, sizeof (*library->gc_lock_tracing)); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_jlm_get_gc_lock_tracing |
| |
| /* |
| * Return tracing info. |
| * |
| * @return pointer to GC lock tracing structure. 0 of not yet initialized |
| */ |
| HyThreadMonitorTracing *VMCALL |
| hythread_jlm_get_gc_lock_tracing () |
| { |
| hythread_t self = MACRO_SELF (); |
| ASSERT (self); |
| ASSERT (self->library); |
| return self->library->gc_lock_tracing; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_lib_lock |
| /** |
| * Acquire the threading library's global lock. |
| * |
| * @note This must not be called recursively by a thread that already owns the global lock. |
| * @param[in] self hythread_t for the current thread |
| * @return none |
| * |
| * @see hythread_lib_unlock |
| */ |
| void VMCALL |
| hythread_lib_lock (hythread_t self) |
| { |
| ASSERT (self); |
| GLOBAL_LOCK (self, CALLER_GLOBAL_LOCK); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_lib_unlock |
| /** |
| * Release the threading library's global lock. |
| * |
| * @note This must be called only by the thread that currently has the global lock locked. |
| * @param[in] self hythread_t for the current thread |
| * @return none |
| * |
| * @see hythread_lib_lock |
| */ |
| void VMCALL |
| hythread_lib_unlock (hythread_t self) |
| { |
| ASSERT (self); |
| GLOBAL_UNLOCK (self); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_park |
| /** |
| * 'Park' the current thread. |
| * |
| * Stop the current thread from executing until it is unparked, interrupted, or the specified timeout elapses. |
| * |
| * Unlike wait or sleep, the interrupted flag is NOT cleared by this API. |
| * |
| * @param[in] millis |
| * @param[in] nanos |
| * |
| * @return 0 if the thread is unparked |
| * HYTHREAD_INTERRUPTED if the thread was interrupted while parked<br> |
| * HYTHREAD_PRIORITY_INTERRUPTED if the thread was priority interrupted while parked<br> |
| * HYTHREAD_TIMED_OUT if the timeout expired<br> |
| * |
| * @see hythread_unpark |
| */ |
| IDATA VMCALL |
| hythread_park (I_64 millis, IDATA nanos) |
| { |
| IDATA rc = 0; |
| hythread_t self = MACRO_SELF (); |
| ASSERT (self); |
| |
| THREAD_LOCK (self, self, CALLER_PARK); |
| |
| if (self->flags & HYTHREAD_FLAG_UNPARKED) |
| { |
| self->flags &= ~HYTHREAD_FLAG_UNPARKED; |
| } |
| else if (self->flags & HYTHREAD_FLAG_INTERRUPTED) |
| { |
| rc = HYTHREAD_INTERRUPTED; |
| } |
| else if (self->flags & HYTHREAD_FLAG_PRIORITY_INTERRUPTED) |
| { |
| rc = HYTHREAD_PRIORITY_INTERRUPTED; |
| } |
| else |
| { |
| self->flags |= HYTHREAD_FLAG_PARKED | HYTHREAD_FLAG_INTERRUPTABLE; |
| |
| if (millis || nanos) |
| { |
| IDATA boundedMillis = BOUNDED_I64_TO_IDATA (millis); |
| |
| self->flags |= HYTHREAD_FLAG_TIMER_SET; |
| |
| COND_WAIT_IF_TIMEDOUT (self->condition, self->mutex, boundedMillis, |
| nanos) |
| { |
| rc = HYTHREAD_TIMED_OUT; |
| break; |
| } |
| else |
| if (self->flags & HYTHREAD_FLAG_UNPARKED) |
| { |
| self->flags &= ~HYTHREAD_FLAG_UNPARKED; |
| break; |
| } |
| else if (self->flags & HYTHREAD_FLAG_INTERRUPTED) |
| { |
| rc = HYTHREAD_INTERRUPTED; |
| break; |
| } |
| else if (self->flags & HYTHREAD_FLAG_PRIORITY_INTERRUPTED) |
| { |
| rc = HYTHREAD_PRIORITY_INTERRUPTED; |
| break; |
| } |
| COND_WAIT_TIMED_LOOP (); |
| } |
| else |
| { |
| COND_WAIT (self->condition, self->mutex); |
| if (self->flags & HYTHREAD_FLAG_UNPARKED) |
| { |
| self->flags &= ~HYTHREAD_FLAG_UNPARKED; |
| break; |
| } |
| else if (self->flags & HYTHREAD_FLAG_INTERRUPTED) |
| { |
| rc = HYTHREAD_INTERRUPTED; |
| break; |
| } |
| else if (self->flags & HYTHREAD_FLAG_PRIORITY_INTERRUPTED) |
| { |
| rc = HYTHREAD_PRIORITY_INTERRUPTED; |
| break; |
| } |
| COND_WAIT_LOOP (); |
| } |
| } |
| |
| self->flags &= |
| ~(HYTHREAD_FLAG_PARKED | HYTHREAD_FLAG_INTERRUPTABLE | |
| HYTHREAD_FLAG_TIMER_SET); |
| |
| THREAD_UNLOCK (self, self); |
| |
| return rc; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_unpark |
| /** |
| * 'Unpark' the specified thread. |
| * |
| * If the thread is parked, it will return from park. |
| * If the thread is not parked, its 'UNPARKED' flag will be set, and it will return immediately the next time it is parked. |
| * |
| * Note that unparks are not counted. Unparking a thread once is the same as unparking it n times. |
| * |
| * @see hythread_park |
| */ |
| void VMCALL |
| hythread_unpark (hythread_t thread) |
| { |
| hythread_t self = MACRO_SELF (); |
| |
| ASSERT (self); |
| ASSERT (thread); |
| |
| /* TODO: is GLOBAL_LOCK/GLOBAl_UNLOCK required here? */ |
| |
| GLOBAL_LOCK (self, CALLER_UNPARK_THREAD); |
| THREAD_LOCK (self, thread, CALLER_UNPARK_THREAD); |
| |
| thread->flags |= HYTHREAD_FLAG_UNPARKED; |
| |
| if (thread->flags & HYTHREAD_FLAG_PARKED) |
| { |
| COND_NOTIFY_ALL (thread->condition); |
| } |
| |
| THREAD_UNLOCK (self, thread); |
| GLOBAL_UNLOCK (self); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_tls_alloc_with_finalizer |
| /** |
| * Allocate a thread local storage (TLS) key. |
| * |
| * Create and return a new, unique key for thread local storage. |
| * |
| * @note The hande returned will be >=0, so it is safe to test the handle against 0 to see if it's been |
| * allocated yet. |
| * |
| * @param[out] handle pointer to a key to be initialized with a key value |
| * @param[in] a finalizer function which will be invoked when a thread is detached or terminates if the thread's TLS entry for this key is non-NULL |
| * @return 0 on success or negative value if a key could not be allocated (i.e. all TLS has been allocated) |
| * |
| * @see hythread_tls_free, hythread_tls_set |
| */ |
| IDATA VMCALL |
| hythread_tls_alloc_with_finalizer (hythread_tls_key_t * handle, |
| hythread_tls_finalizer_t finalizer) |
| { |
| IDATA index; |
| hythread_library_t lib = GLOBAL_DATA (default_library); |
| ASSERT (lib); |
| |
| *handle = 0; |
| |
| MUTEX_ENTER (lib->tls_mutex); |
| |
| for (index = 0; index < HYTHREAD_MAX_TLS_KEYS; index++) |
| { |
| if (lib->tls_finalizers[index] == NULL) |
| { |
| *handle = index + 1; |
| lib->tls_finalizers[index] = finalizer; |
| break; |
| } |
| } |
| |
| MUTEX_EXIT (lib->tls_mutex); |
| |
| return index < HYTHREAD_MAX_TLS_KEYS ? 0 : -1; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION tls_finalize |
| /* |
| * Run finalizers on any non-NULL TLS values for the current thread |
| * |
| * @param[in] thread current thread |
| * @return none |
| */ |
| static void |
| tls_finalize (hythread_t thread) |
| { |
| IDATA index; |
| hythread_library_t lib = thread->library; |
| |
| for (index = 0; index < HYTHREAD_MAX_TLS_KEYS; index++) |
| { |
| if (thread->tls[index] != NULL) |
| { |
| void *value; |
| hythread_tls_finalizer_t finalizer; |
| |
| /* read the value and finalizer together under mutex to be sure that they belong together */ |
| MUTEX_ENTER (lib->tls_mutex); |
| value = thread->tls[index]; |
| finalizer = lib->tls_finalizers[index]; |
| MUTEX_EXIT (lib->tls_mutex); |
| |
| if (value) |
| { |
| finalizer (value); |
| } |
| } |
| } |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION tls_null_finalizer |
| static void HYTHREAD_PROC |
| tls_null_finalizer (void *entry) |
| { |
| /* do nothing */ |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hythread_current_stack_free |
| /** |
| * Return the remaining useable bytes of the current thread's OS stack. |
| * |
| * @return OS stack free size in bytes, 0 if it cannot be determined. |
| */ |
| UDATA VMCALL |
| hythread_current_stack_free(void) |
| { |
| #if defined(WIN32) |
| MEMORY_BASIC_INFORMATION memInfo; |
| SYSTEM_INFO sysInfo; |
| UDATA stackFree; |
| UDATA guardPageSize; |
| |
| GetSystemInfo(&sysInfo); |
| VirtualQuery(&memInfo, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)); |
| stackFree = ((UDATA) &memInfo - (UDATA) memInfo.AllocationBase) & ~sizeof(UDATA); |
| |
| /* By observation, Win32 reserves 3 pages at the low end of the stack for guard pages, so omit them */ |
| |
| guardPageSize = 3 * (UDATA) sysInfo.dwPageSize; |
| return (stackFree < guardPageSize) ? 0 : stackFree - guardPageSize; |
| #else |
| return 0; |
| #endif |
| } |