From 47025954242f9b83a1ca9dce67f33cda5e610254 Mon Sep 17 00:00:00 2001 From: Salikh Zakirov Date: Mon, 12 Mar 2007 18:02:49 +0300 Subject: [PATCH] Rewrite hymutex and hycond to use OS layer instead of APR --- vm/include/open/hycond_win.h | 54 ++++++++++ vm/include/open/hythread_ext.h | 35 ++++-- vm/thread/src/linux/os_condvar.c | 98 ++++++++++++++++++ vm/thread/src/linux/os_mutex.c | 104 +++++++++++++++++++ vm/thread/src/thread_native_basic.c | 1 - vm/thread/src/thread_native_condvar.c | 100 +++++-------------- vm/thread/src/thread_native_latch.c | 2 +- vm/thread/src/thread_native_mutex.c | 110 -------------------- vm/thread/src/thread_private.h | 9 +-- vm/thread/src/win/os_condvar.c | 182 +++++++++++++++++++++++++++++++++ vm/thread/src/win/os_mutex.c | 103 +++++++++++++++++++ 11 files changed, 592 insertions(+), 206 deletions(-) create mode 100644 vm/include/open/hycond_win.h create mode 100644 vm/thread/src/linux/os_condvar.c create mode 100644 vm/thread/src/linux/os_mutex.c delete mode 100644 vm/thread/src/thread_native_mutex.c create mode 100644 vm/thread/src/win/os_condvar.c create mode 100644 vm/thread/src/win/os_mutex.c diff --git a/vm/include/open/hycond_win.h b/vm/include/open/hycond_win.h new file mode 100644 index 0000000..01e3189 --- /dev/null +++ b/vm/include/open/hycond_win.h @@ -0,0 +1,54 @@ +/* + * 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 + * Condition variable implementation for Windows, based on wait queues. + */ +#ifndef _HYCOND_WIN_H +#define _HYCOND_WIN_H + +#ifndef _WIN32 +#error This code is only supposed to work on Windows +#endif // _WIN32 + + #if _MSC_VER >= 1300 || __INTEL_COMPILER + // workaround for the + // http://www.microsoft.com/msdownload/platformsdk/sdkupdate/2600.2180.7/contents.htm + #include + #endif + +#include + +struct waiting_node { + // notification event + HANDLE event; + // doubly-linked queue + struct waiting_node *prev; + struct waiting_node *next; +}; + +// queue based condition implementation +struct HyCond { + // Synchronization is necessary because signal() caller is not required + // to hold mutex associated with the condition variable. + hymutex_t queue_mutex; + // head-tail marker node + struct waiting_node dummy_node; +}; + +#endif // _HYCOND_WIN_H diff --git a/vm/include/open/hythread_ext.h b/vm/include/open/hythread_ext.h index 9d7e4fc..f599ed6 100644 --- a/vm/include/open/hythread_ext.h +++ b/vm/include/open/hythread_ext.h @@ -129,8 +129,19 @@ extern "C" { */ //@{ -typedef struct HyMutex *hymutex_t; -typedef struct HyCond *hycond_t; +#ifdef __linux__ +#include +#define hymutex_t pthread_mutex_t +#define hycond_t pthread_cond_t +#endif // __linux__ + +#ifdef _WIN32 +#define hymutex_t CRITICAL_SECTION +#define hycond_t struct HyCond +#include "hycond_win.h" +#endif // _WIN32 + + typedef struct HyLatch *hylatch_t; typedef struct HyThreadGroup *hythread_group_t; typedef struct HyThread *hythread_iterator_t; @@ -188,12 +199,12 @@ UDATA VMCALL hythread_tls_get_suspend_request_offset(); //@{ IDATA VMCALL hycond_create (hycond_t *cond); -IDATA VMCALL hycond_wait (hycond_t cond, hymutex_t mutex); -IDATA VMCALL hycond_wait_timed (hycond_t cond, hymutex_t mutex, I_64 millis, IDATA nanos); -IDATA VMCALL hycond_wait_interruptable (hycond_t cond, hymutex_t mutex, I_64 millis, IDATA nanos); -IDATA VMCALL hycond_notify (hycond_t cond); -IDATA VMCALL hycond_notify_all (hycond_t cond); -IDATA VMCALL hycond_destroy (hycond_t cond); +IDATA VMCALL hycond_wait (hycond_t *cond, hymutex_t *mutex); +IDATA VMCALL hycond_wait_timed (hycond_t *cond, hymutex_t *mutex, I_64 millis, IDATA nanos); +IDATA VMCALL hycond_wait_interruptable (hycond_t *cond, hymutex_t *mutex, I_64 millis, IDATA nanos); +IDATA VMCALL hycond_notify (hycond_t *cond); +IDATA VMCALL hycond_notify_all (hycond_t *cond); +IDATA VMCALL hycond_destroy (hycond_t *cond); //@} /** @name Safe suspension support @@ -254,10 +265,10 @@ IDATA hysem_set(hysem_t sem, IDATA count); //@{ IDATA hymutex_create (hymutex_t *mutex, UDATA flags); -IDATA hymutex_lock(hymutex_t mutex); -IDATA hymutex_trylock (hymutex_t mutex); -IDATA hymutex_unlock (hymutex_t mutex); -IDATA hymutex_destroy (hymutex_t mutex); +IDATA hymutex_lock(hymutex_t *mutex); +IDATA hymutex_trylock (hymutex_t *mutex); +IDATA hymutex_unlock (hymutex_t *mutex); +IDATA hymutex_destroy (hymutex_t *mutex); //@} /** @name Thin monitors support diff --git a/vm/thread/src/linux/os_condvar.c b/vm/thread/src/linux/os_condvar.c new file mode 100644 index 0000000..dccd6b9 --- /dev/null +++ b/vm/thread/src/linux/os_condvar.c @@ -0,0 +1,98 @@ +/* + * 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 os_condvar.c + * @brief Binding of hycond to condition variables provided by Pthreads + */ + +#include "thread_private.h" +#include + + +/** @name Conditional variable + */ +//@{ + +/** + * waits on a condition variable, directly using OS interfaces. + * + * This function does not implement interruptability and thread state + * functionality, thus the caller of this function have to handle it. + */ +int os_cond_timedwait(hycond_t *cond, hymutex_t *mutex, I_64 ms, IDATA nano) +{ + int r = 0; + if (!ms && !nano) { + r = pthread_cond_wait(cond, mutex); + } else { + struct timespec abstime; + apr_time_t then = apr_time_now() + ms*1000 + nano/1000; + abstime.tv_sec = apr_time_sec(then); + abstime.tv_nsec = apr_time_usec(then)*1000 + nano%1000; + r = pthread_cond_timedwait(cond, mutex, &abstime); + } + if (r == ETIMEDOUT) + r = TM_ERROR_TIMEOUT; + else if (r == EINTR) + r = TM_ERROR_INTERRUPT; + return r; +} + + +/** + * Creates and initializes condition variable. + * + * @param[in] cond the address of the condition variable. + * @return 0 on success, non-zero otherwise. + */ +IDATA VMCALL hycond_create (hycond_t *cond) { + return pthread_cond_init(cond, NULL); +} + +/** + * Signals a single thread that is blocking on the given condition variable + * to wake up. + * + * @param[in] cond the condition variable on which to produce the signal. + * @sa apr_thread_cond_signal() + */ +IDATA VMCALL hycond_notify (hycond_t *cond) { + return pthread_cond_signal(cond); +} + +/** + * Signals all threads blocking on the given condition variable. + * + * @param[in] cond the condition variable on which to produce the broadcast. + * @sa apr_thread_cond_broadcast() + */ +IDATA VMCALL hycond_notify_all (hycond_t *cond) { + return pthread_cond_broadcast(cond); +} + +/** + * Destroys the condition variable and releases the associated memory. + * + * @param[in] cond the condition variable to destroy + * @sa apr_thread_cond_destroy() + */ +IDATA VMCALL hycond_destroy (hycond_t *cond) { + return pthread_cond_destroy(cond); +} + +//@} diff --git a/vm/thread/src/linux/os_mutex.c b/vm/thread/src/linux/os_mutex.c new file mode 100644 index 0000000..5c05ead --- /dev/null +++ b/vm/thread/src/linux/os_mutex.c @@ -0,0 +1,104 @@ +/* + * 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 os_mutex.c + * @brief hymutex binding to Pthreads mutexes + */ + +#include "thread_private.h" +#include + + +/** @name Mutex + * + */ +//@{ + +/** + * Initializes a mutex. + * + * A memory for mutex must be preallocated. + * + * @param[in] mutex the address of the mutex to be initialized + * @param[in] flags Or'ed value of: + *
+ *           APR_THREAD_MUTEX_DEFAULT   platform-optimal lock behavior.
+ *           APR_THREAD_MUTEX_NESTED    enable nested (recursive) locks.
+ *           APR_THREAD_MUTEX_UNNESTED  disable nested locks (non-recursive).
+ * 
+ */ +IDATA VMCALL hymutex_create (hymutex_t *mutex, UDATA flags) { + int r = 0; + if (flags & APR_THREAD_MUTEX_NESTED) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + r = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); + if (r) return r; + r = pthread_mutex_init(mutex, &attr); + pthread_mutexattr_destroy(&attr); + } else { + r = pthread_mutex_init(mutex, NULL); + } + return r; +} + +/** + * Acquires the lock for the given mutex. If the mutex is already locked, + * the current thread will be put to sleep until the lock becomes available. + * + * @param[in] mutex the mutex on which to acquire the lock. + * @sa apr_thread_mutex_lock() + */ +IDATA VMCALL hymutex_lock(hymutex_t *mutex) { + return pthread_mutex_lock(mutex); +} + +/** + * Attempts to acquire the lock for the given mutex. + * + * @param[in] mutex the mutex on which to attempt the lock acquiring. + * @sa apr_thread_mutex_trylock() + */ +IDATA VMCALL hymutex_trylock (hymutex_t *mutex) { + int r; + r = pthread_mutex_trylock(mutex); + if (r == EBUSY) return TM_ERROR_EBUSY; + return r; +} + +/** + * Releases the lock for the given mutex. + * + * @param[in] mutex the mutex from which to release the lock. + * @sa apr_thread_mutex_unlock() + */ +IDATA VMCALL hymutex_unlock (hymutex_t *mutex) { + return pthread_mutex_unlock(mutex); +} + +/** + * Destroys the mutex. + * + * @param[in] mutex the mutex to destroy. + * @sa apr_thread_mutex_destroy() + */ +IDATA VMCALL hymutex_destroy (hymutex_t *mutex) { + return pthread_mutex_destroy(mutex); +} + +//@} diff --git a/vm/thread/src/thread_native_basic.c b/vm/thread/src/thread_native_basic.c index 7d65eab..4a0cfe0 100644 --- a/vm/thread/src/thread_native_basic.c +++ b/vm/thread/src/thread_native_basic.c @@ -93,7 +93,6 @@ static void thread_set_self(hythread_t thread); * @param[in] data argument to be passed to starting function */ IDATA VMCALL hythread_create_with_group(hythread_t *ret_thread, hythread_group_t group, UDATA stacksize, UDATA priority, UDATA suspend, hythread_entrypoint_t func, void *data) { - apr_threadattr_t *apr_attrs; hythread_t new_thread; thread_start_proc_data * start_proc_data; int r; diff --git a/vm/thread/src/thread_native_condvar.c b/vm/thread/src/thread_native_condvar.c index 81b3d04..83e1921 100644 --- a/vm/thread/src/thread_native_condvar.c +++ b/vm/thread/src/thread_native_condvar.c @@ -29,54 +29,44 @@ //@{ /** - * Creates and initializes condition variable. - * - * @param[out] cond the memory address where the newly created condition variable - * will be stored. - * @sa apr_thread_cond_create() + * Waits on a conditional, handling interruptions and thread state. */ -IDATA VMCALL hycond_create (hycond_t *cond) { - apr_pool_t *pool = get_local_pool(); - apr_status_t apr_status = apr_thread_cond_create((apr_thread_cond_t**)cond, pool); - if (apr_status != APR_SUCCESS) return CONVERT_ERROR(apr_status); - return TM_ERROR_NONE; -} - -IDATA condvar_wait_impl(hycond_t cond, hymutex_t mutex, I_64 ms, IDATA nano, IDATA interruptable) { - apr_status_t apr_status; +static IDATA condvar_wait_impl(hycond_t *cond, hymutex_t *mutex, I_64 ms, IDATA nano, IDATA interruptable) { + int r; int disable_count; - hythread_t this_thread; + hythread_t self; - this_thread = tm_self_tls; + self = tm_self_tls; // Store provided cond into current thread cond - this_thread->current_condition = interruptable ? cond : NULL; + self->current_condition = interruptable ? cond : NULL; // check interrupted flag - if (interruptable && (this_thread->state & TM_THREAD_STATE_INTERRUPTED)) { + if (interruptable && (self->state & TM_THREAD_STATE_INTERRUPTED)) { // clean interrupted flag - this_thread->state &= (~TM_THREAD_STATE_INTERRUPTED); - return TM_ERROR_INTERRUPT; + hymutex_lock(&self->mutex); + self->state &= ~TM_THREAD_STATE_INTERRUPTED; + hymutex_unlock(&self->mutex); + return TM_ERROR_INTERRUPT; } disable_count = reset_suspend_disable(); - // Delegate to OS wait - apr_status = (!ms && !nano)? - apr_thread_cond_wait((apr_thread_cond_t*)cond, (apr_thread_mutex_t*)mutex): - apr_thread_cond_timedwait ((apr_thread_cond_t*)cond, (apr_thread_mutex_t*)mutex, ms*1000 + ((nano < 1000) ? 1 : (nano / 1000))); - - set_suspend_disable(disable_count); - this_thread->current_condition = NULL; + r = os_cond_timedwait(cond, mutex, ms, nano); + + set_suspend_disable(disable_count); + self->current_condition = NULL; // check interrupted flag - if (interruptable && (this_thread->state & TM_THREAD_STATE_INTERRUPTED)) { + if (interruptable && (self->state & TM_THREAD_STATE_INTERRUPTED)) { // clean interrupted flag - this_thread->state &= (~TM_THREAD_STATE_INTERRUPTED); + hymutex_lock(&self->mutex); + self->state &= (~TM_THREAD_STATE_INTERRUPTED); + hymutex_unlock(&self->mutex); return TM_ERROR_INTERRUPT; } - return CONVERT_ERROR(apr_status); + return r; } /** @@ -88,8 +78,8 @@ IDATA condvar_wait_impl(hycond_t cond, hymutex_t mutex, I_64 ms, IDATA nano, IDA * @return * TM_NO_ERROR on success */ -IDATA VMCALL hycond_wait(hycond_t cond, hymutex_t mutex) { - return condvar_wait_impl(cond, mutex, 0, 0, 0); +IDATA VMCALL hycond_wait(hycond_t *cond, hymutex_t *mutex) { + return condvar_wait_impl(cond, mutex, 0, 0, WAIT_NONINTERRUPTABLE); } /** @@ -104,8 +94,8 @@ IDATA VMCALL hycond_wait(hycond_t cond, hymutex_t mutex) { * @return * TM_NO_ERROR on success */ -IDATA VMCALL hycond_wait_timed(hycond_t cond, hymutex_t mutex, I_64 ms, IDATA nano) { - return condvar_wait_impl(cond, mutex, ms, nano, 0); +IDATA VMCALL hycond_wait_timed(hycond_t *cond, hymutex_t *mutex, I_64 ms, IDATA nano) { + return condvar_wait_impl(cond, mutex, ms, nano, WAIT_NONINTERRUPTABLE); } /** @@ -121,48 +111,8 @@ IDATA VMCALL hycond_wait_timed(hycond_t cond, hymutex_t mutex, I_64 ms, IDATA na * TM_NO_ERROR on success * TM_THREAD_INTERRUPTED in case thread was interrupted during wait. */ -IDATA VMCALL hycond_wait_interruptable(hycond_t cond, hymutex_t mutex, I_64 ms, IDATA nano) { +IDATA VMCALL hycond_wait_interruptable(hycond_t *cond, hymutex_t *mutex, I_64 ms, IDATA nano) { return condvar_wait_impl(cond, mutex, ms, nano, WAIT_INTERRUPTABLE); } -/** - * Signals a single thread that is blocking on the given condition variable to wake up. - * - * @param[in] cond the condition variable on which to produce the signal. - * @sa apr_thread_cond_signal() - */ -IDATA VMCALL hycond_notify (hycond_t cond) { - apr_status_t apr_status = apr_thread_cond_signal((apr_thread_cond_t*)cond); - if (apr_status != APR_SUCCESS) return CONVERT_ERROR(apr_status); - return TM_ERROR_NONE; -} - -/** - * Signals all threads blocking on the given condition variable. - * - * @param[in] cond the condition variable on which to produce the broadcast. - * @sa apr_thread_cond_broadcast() - */ -IDATA VMCALL hycond_notify_all (hycond_t cond) { - apr_status_t apr_status = apr_thread_cond_broadcast((apr_thread_cond_t*)cond); - if (apr_status != APR_SUCCESS) return CONVERT_ERROR(apr_status); - return TM_ERROR_NONE; -} - -/** - * Destroys the condition variable and releases the associated memory. - * - * @param[in] cond the condition variable to destroy - * @sa apr_thread_cond_destroy() - */ -IDATA VMCALL hycond_destroy (hycond_t cond) { - apr_status_t apr_status; - apr_pool_t *pool = apr_thread_cond_pool_get ((apr_thread_cond_t*)cond); - if (pool != get_local_pool()) { - return local_pool_cleanup_register(hycond_destroy, cond); - } - apr_status=apr_thread_cond_destroy((apr_thread_cond_t*)cond); - return CONVERT_ERROR(apr_status); -} - //@} diff --git a/vm/thread/src/thread_native_latch.c b/vm/thread/src/thread_native_latch.c index 8cb6020..9b037ba 100644 --- a/vm/thread/src/thread_native_latch.c +++ b/vm/thread/src/thread_native_latch.c @@ -47,7 +47,7 @@ IDATA VMCALL hylatch_create(hylatch_t *latch, IDATA count) { l = malloc(sizeof(HyLatch)); if (l == NULL) { - return TM_ERROR_OUT_OF_MEMORY; + return TM_ERROR_OUT_OF_MEMORY; } apr_status = apr_thread_mutex_create((apr_thread_mutex_t**)&(l->mutex), TM_MUTEX_DEFAULT, pool); if (apr_status != APR_SUCCESS) return CONVERT_ERROR(apr_status); diff --git a/vm/thread/src/thread_native_mutex.c b/vm/thread/src/thread_native_mutex.c deleted file mode 100644 index c271fdd..0000000 --- a/vm/thread/src/thread_native_mutex.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file thread_native_mutex.c - * @brief Hythread mutex related functions - */ - -#undef LOG_DOMAIN -#define LOG_DOMAIN "tm.locks" - -#include "thread_private.h" -#include - - -/** @name Mutex - * - */ -//@{ - -/** - * Creates and initializes a mutex. - * - * Mutex is a fat lock which implies parking of threads in case of contention. - * @param[in] mutex the memory address where the newly created mutex will be - * stored. - * @param[in] flags Or'ed value of: - *
- *           HYTHREAD_MUTEX_DEFAULT   platform-optimal lock behavior.
- *           HYTHREAD_MUTEX_NESTED    enable nested (recursive) locks.
- *           HYTHREAD_MUTEX_UNNESTED  disable nested locks (non-recursive).
- * 
- * @sa apr_thread_mutex_create() - */ -IDATA VMCALL hymutex_create (hymutex_t *mutex, UDATA flags) { - apr_pool_t *pool = get_local_pool(); - apr_status_t apr_status; - - apr_status = apr_thread_mutex_create((apr_thread_mutex_t**)mutex, flags, pool); - return CONVERT_ERROR(apr_status); -} - -/** - * Acquires the lock for the given mutex. If the mutex is already locked, - * the current thread will be put to sleep until the lock becomes available. - * - * @param[in] mutex the mutex on which to acquire the lock. - * @sa apr_thread_mutex_lock() - */ -IDATA VMCALL hymutex_lock(hymutex_t mutex) { - apr_status_t apr_status; - apr_status = apr_thread_mutex_lock((apr_thread_mutex_t*)mutex); - - return CONVERT_ERROR(apr_status); -} - -/** - * Attempts to acquire the lock for the given mutex. - * - * @param[in] mutex the mutex on which to attempt the lock acquiring. - * @sa apr_thread_mutex_trylock() - */ -IDATA VMCALL hymutex_trylock (hymutex_t mutex) { - return CONVERT_ERROR(apr_thread_mutex_trylock((apr_thread_mutex_t*)mutex)); -} - -/** - * Releases the lock for the given mutex. - * - * @param[in] mutex the mutex from which to release the lock. - * @sa apr_thread_mutex_unlock() - */ -IDATA VMCALL hymutex_unlock (hymutex_t mutex) { - apr_status_t apr_status = apr_thread_mutex_unlock((apr_thread_mutex_t*)mutex); - assert(apr_status == APR_SUCCESS); - return CONVERT_ERROR(apr_status); -} - -/** - * Destroys the mutex and releases the memory associated with the lock. - * - * @param[in] mutex the mutex to destroy. - * @sa apr_thread_mutex_destroy() - */ -IDATA VMCALL hymutex_destroy (hymutex_t mutex) { - apr_pool_t *pool = apr_thread_mutex_pool_get ((apr_thread_mutex_t*)mutex); - apr_status_t apr_status; - if (pool != get_local_pool()) { - return local_pool_cleanup_register(hymutex_destroy, mutex); - } - apr_status=apr_thread_mutex_destroy((apr_thread_mutex_t*)mutex); - return CONVERT_ERROR(apr_status); -} - - -//@} diff --git a/vm/thread/src/thread_private.h b/vm/thread/src/thread_private.h index 45222be..0ad8814 100644 --- a/vm/thread/src/thread_private.h +++ b/vm/thread/src/thread_private.h @@ -126,13 +126,6 @@ apr_pool_t* get_local_pool(); IDATA local_pool_cleanup_register(void* func, void* data); - -// Direct mappings to porting layer / APR -#define HyCond apr_thread_cond_t -#define HyMutex apr_thread_mutex_t -//#define tm_rwlock_t apr_rwlock_t -//#define _tm_threadkey_t apr_threadkey_t - #ifdef __linux__ #define osthread_t pthread_t #elif _WIN32 @@ -673,6 +666,8 @@ int os_thread_join(osthread_t); void os_thread_exit(int status); void os_thread_yield_other(osthread_t); +int os_cond_timedwait(hycond_t *cond, hymutex_t *mutex, I_64 ms, IDATA nano); + #ifdef __cplusplus } #endif diff --git a/vm/thread/src/win/os_condvar.c b/vm/thread/src/win/os_condvar.c new file mode 100644 index 0000000..5d58371 --- /dev/null +++ b/vm/thread/src/win/os_condvar.c @@ -0,0 +1,182 @@ +/* + * 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 os_condvar.c + * @brief Custom queue-based condition variable implementation. + * @detailed Custom implementation is needed on Windows, because it lacks + * the suitable condition variable synchronization object. + */ + +#include +#include "thread_private.h" + +static void _enqueue (hycond_t *cond, struct waiting_node *node) +{ + node->next = &cond->dummy_node; + node->prev = cond->dummy_node.prev; + node->prev->next = node; + cond->dummy_node.prev = node; +} + +static int _remove_from_queue (hycond_t *cond, struct waiting_node *node) +{ + if (node->next == NULL || node->prev == NULL) { + // already dequeued (by signal) + return -1; + } + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = NULL; + node->prev = NULL; + return 0; +} + +// returns NULL if queue is empty +static struct waiting_node * _dequeue (hycond_t *cond) +{ + struct waiting_node *node; + if (cond->dummy_node.next == &cond->dummy_node) { + // the queue is empty + return NULL; + } + node = cond->dummy_node.next; + _remove_from_queue(cond,node); + return node; +} + +/** + * waits on a condition variable, directly using OS interfaces. + * + * This function does not implement interruptability and thread state + * functionality, thus the caller of this function have to handle it. + */ +int os_cond_timedwait(hycond_t *cond, hymutex_t *mutex, I_64 ms, IDATA nano) +{ + int r = 0; + struct waiting_node node; + DWORD res; + DWORD timeout; + if (!ms && !nano) { + timeout = INFINITE; + } else { + timeout = (DWORD)ms + (nano ? 1:0); + } + + // NULL attributes, manual reset, initially unsignalled, NULL name + node.event = CreateEvent(NULL, TRUE, FALSE, NULL); + hymutex_lock(&cond->queue_mutex); + _enqueue(cond, &node); + hymutex_unlock(&cond->queue_mutex); + + // release mutex and wait for signal + hymutex_unlock(mutex); + + res = WaitForSingleObject(node.event, timeout); + if (res != WAIT_OBJECT_0) { + if (res == WAIT_TIMEOUT) + r = TM_ERROR_TIMEOUT; + else + r = (int)GetLastError(); + } + + // re-acquire mutex associated with condition variable + hymutex_lock(mutex); + + hymutex_lock(&cond->queue_mutex); + _remove_from_queue(cond, &node); + CloseHandle(node.event); + hymutex_unlock(&cond->queue_mutex); + + return r; +} + +/** @name Conditional variable + */ +//@{ + +/** + * Creates and initializes condition variable. + * + * @param[in] cond the address of the condition variable. + * @return 0 on success, non-zero otherwise. + */ +IDATA VMCALL hycond_create (hycond_t *cond) { + cond->dummy_node.next = cond->dummy_node.prev = &cond->dummy_node; + hymutex_create(&cond->queue_mutex, APR_THREAD_MUTEX_NESTED); + return 0; +} + +/** + * Signals a single thread that is blocking on the given condition variable to + * wake up. + * + * @param[in] cond the condition variable on which to produce the signal. + * @sa apr_thread_cond_signal() + */ +IDATA VMCALL hycond_notify (hycond_t *cond) { + int r = 0; + DWORD res; + struct waiting_node *node; + + hymutex_lock(&cond->queue_mutex); + node = _dequeue(cond); + if (node != NULL) { + res = SetEvent(node->event); + if (res == 0) { + r = (int)GetLastError(); + } + } + hymutex_unlock(&cond->queue_mutex); + return r; +} + +/** + * Signals all threads blocking on the given condition variable. + * + * @param[in] cond the condition variable on which to produce the broadcast. + * @sa apr_thread_cond_broadcast() + */ +IDATA VMCALL hycond_notify_all (hycond_t *cond) { + int r = 0; + DWORD res; + struct waiting_node *node; + + hymutex_lock(&cond->queue_mutex); + for (node = _dequeue(cond); node != NULL; node = _dequeue(cond)) { + res = SetEvent(node->event); + if (res == 0) { + r = GetLastError(); + } + } + hymutex_unlock(&cond->queue_mutex); + return r; +} + +/** + * Destroys the condition variable and releases the associated memory. + * + * @param[in] cond the condition variable to destroy + * @sa apr_thread_cond_destroy() + */ +IDATA VMCALL hycond_destroy (hycond_t *cond) { + assert(cond->dummy_node.next == &cond->dummy_node + && "destroying condition variable with active waiters"); + return hymutex_destroy(&cond->queue_mutex); +} + +//@} diff --git a/vm/thread/src/win/os_mutex.c b/vm/thread/src/win/os_mutex.c new file mode 100644 index 0000000..e2b9f2d --- /dev/null +++ b/vm/thread/src/win/os_mutex.c @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file thread_native_mutex.c + * @brief hymutex binding to Win32 critical sections + */ + +#include "thread_private.h" +#include + + +/** @name Mutex + * + */ +//@{ + +/** + * Initializes a mutex. + * + * A memory for mutex must be preallocated. + * + * @param[in] mutex the address of the mutex to be initialized + * @param[in] flags Or'ed value of: + *
+ *           APR_THREAD_MUTEX_DEFAULT   platform-optimal lock behavior.
+ *           APR_THREAD_MUTEX_NESTED    enable nested (recursive) locks.
+ *           APR_THREAD_MUTEX_UNNESTED  disable nested locks (non-recursive).
+ * 
+ */ +IDATA VMCALL hymutex_create (hymutex_t *mutex, UDATA flags) { + int r = 0; + if (flags & APR_THREAD_MUTEX_UNNESTED) { + assert(!"not implemented"); + return -1; + } + InitializeCriticalSection(mutex); + return r; +} + +/** + * Acquires the lock for the given mutex. If the mutex is already locked, + * the current thread will be put to sleep until the lock becomes available. + * + * @param[in] mutex the mutex on which to acquire the lock. + * @sa apr_thread_mutex_lock() + */ +IDATA VMCALL hymutex_lock(hymutex_t *mutex) { + EnterCriticalSection(mutex); + return 0; +} + +/** + * Attempts to acquire the lock for the given mutex. + * + * @param[in] mutex the mutex on which to attempt the lock acquiring. + * @sa apr_thread_mutex_trylock() + */ +IDATA VMCALL hymutex_trylock (hymutex_t *mutex) { + int r; + r = TryEnterCriticalSection(mutex); + // Return code is non-zero on success + if (r == 0) return TM_ERROR_EBUSY; + return 0; +} + +/** + * Releases the lock for the given mutex. + * + * @param[in] mutex the mutex from which to release the lock. + * @sa apr_thread_mutex_unlock() + */ +IDATA VMCALL hymutex_unlock (hymutex_t *mutex) { + LeaveCriticalSection(mutex); + return 0; +} + +/** + * Destroys the mutex. + * + * @param[in] mutex the mutex to destroy. + * @sa apr_thread_mutex_destroy() + */ +IDATA VMCALL hymutex_destroy (hymutex_t *mutex) { + DeleteCriticalSection(mutex); + return 0; +} + +//@} -- 1.5.0.33.g1b20