| /* $Id$ | |
| * | |
| * 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. | |
| */ | |
| /* | |
| * etchmutex.c | |
| * currently wraps apr mutex. our wrapper carries with it the benefit of | |
| * tracking memory for our mutex, and thus for the underlying apr mutex. | |
| */ | |
| #include "apr_network_io.h" | |
| #include "apr_errno.h" | |
| #include "apr_general.h" | |
| #include "apr_thread_mutex.h" | |
| #include "apr_thread_cond.h" | |
| #include "apr_portable.h" /* apr_os_thread_current() */ | |
| #include "etchmutex.h" | |
| #include "etch_global.h" | |
| #include "etchlog.h" | |
| /** | |
| * etchmutex_acquire() | |
| */ | |
| int etchmutex_acquire(etchmutex* mx) | |
| { | |
| int result = -1; | |
| apr_thread_mutex_t* amx =(apr_thread_mutex_t*) mx->impl; | |
| /* void* thisthread = apr_os_thread_current(); */ | |
| /* if (mx->lockcount == 0 || apr_os_thread_equal(mx->owning_thread, thisthread)) */ | |
| result = apr_thread_mutex_lock(amx); | |
| if (0 == result) | |
| { mx->lockcount++; | |
| /* mx->owning_thread = thisthread; */ | |
| } | |
| else result = -1; | |
| return result; | |
| } | |
| /** | |
| * etchmutex_tryacquire() | |
| */ | |
| int etchmutex_tryacquire(etchmutex* mx) | |
| { | |
| int result = -1; | |
| apr_thread_mutex_t* amx =(apr_thread_mutex_t*) mx->impl; | |
| /* void* thisthread = apr_os_thread_current(); */ | |
| /* if (mx->lockcount == 0 || apr_os_thread_equal(mx->owning_thread, thisthread)) */ | |
| result = apr_thread_mutex_trylock(amx); | |
| if (0 == result) | |
| { mx->lockcount++; | |
| /* mx->owning_thread = thisthread; */ | |
| } | |
| else result = -1; | |
| return result; | |
| } | |
| /** | |
| * etchmutex_release() | |
| */ | |
| int etchmutex_release(etchmutex* mx) | |
| { | |
| apr_thread_mutex_t* amx =(apr_thread_mutex_t*) mx->impl; | |
| int result = apr_thread_mutex_unlock(amx); | |
| if (0 == result) | |
| { if (mx->lockcount > 0) | |
| mx->lockcount--; | |
| } | |
| else result = -1; | |
| return result; | |
| } | |
| /** | |
| * destroy_etchmutex() | |
| */ | |
| int destroy_etchmutex(etchmutex* mx) | |
| { | |
| apr_thread_mutex_t* amx = NULL; | |
| int result = 0; | |
| if (NULL == mx) return -1; | |
| if (mx->refcount > 0 && --mx->refcount > 0) return -1; | |
| amx = (apr_thread_mutex_t*) mx->impl; | |
| if (!is_etchobj_static_content(mx)) | |
| result = apr_thread_mutex_destroy(amx); | |
| if (!is_etchobj_static_shell(mx)) | |
| etch_free(mx); | |
| return result == 0? 0: -1; | |
| } | |
| /** | |
| * new_mutex() | |
| * etchmutex constructor. | |
| * currently accepts apr memory pool as parameter. | |
| */ | |
| etchmutex* new_mutex(void* params, const int is_nestable) | |
| { | |
| apr_thread_mutex_t* amx = 0; | |
| apr_status_t xresult = 0; | |
| apr_pool_t* aprpool = 0; | |
| etchmutex* mx = (etchmutex*) new_object | |
| (sizeof(etchmutex), ETCHTYPEB_MUTEX, CLASSID_MUTEX); | |
| mx->destroy = destroy_etchmutex; | |
| mx->clone = clone_null; | |
| mx->acquire = etchmutex_acquire; | |
| mx->release = etchmutex_release; | |
| mx->tryacquire = etchmutex_tryacquire; | |
| aprpool = (apr_pool_t*) params; | |
| xresult = apr_thread_mutex_create(&amx, | |
| is_nestable? APR_THREAD_MUTEX_NESTED: APR_THREAD_MUTEX_UNNESTED, aprpool); | |
| mx->impl = amx; | |
| return amx? mx: NULL; | |
| } | |
| /** | |
| * etchmutex_hookproc() | |
| * callback for mutex control, used to avoid defining concrete mutex objects. | |
| * for example a synchronized list calls a hook to set and release its lock, | |
| * however the lock appears to the list as a void*. | |
| */ | |
| int etchmutex_hookproc(int action, void* mx) | |
| { | |
| int result = -1; | |
| etchmutex* mutex = (etchmutex*) mx; | |
| if (NULL == mx); | |
| else switch(action) | |
| { | |
| case ETCH_SYNC_SET: | |
| result = mutex->acquire(mutex); | |
| break; | |
| case ETCH_SYNC_TRY: | |
| result = mutex->tryacquire(mutex); | |
| break; | |
| case ETCH_SYNC_REL: | |
| result = mutex->release(mutex); | |
| break; | |
| case ETCH_SYNC_DEL: | |
| result = mutex->destroy(mutex); | |
| break; | |
| } | |
| return result; | |
| } | |
| /* - - - - - - | |
| * etchwait | |
| * - - - - - - | |
| */ | |
| /** | |
| * destroy_etchwait() | |
| * etchwait destructor | |
| */ | |
| int destroy_etchwait(etchwait* waiter) | |
| { | |
| apr_thread_cond_t* tc = 0; | |
| int xresult = 0; | |
| if (!waiter || (waiter->refcount > 0 && --waiter->refcount > 0)) return -1; | |
| tc =(apr_thread_cond_t*) waiter->impl; | |
| if (!is_etchobj_static_content(waiter)) | |
| { xresult = apr_thread_cond_destroy(tc); | |
| waiter->mutex->destroy(waiter->mutex); /* free mutex + wrapper */ | |
| } | |
| if (!is_etchobj_static_shell(waiter)) | |
| etch_free(waiter); | |
| return xresult == 0? 0: -1; | |
| } | |
| /** | |
| * etchwait_waitex() | |
| * private method blocks until signaled or specified condition set or timeout. | |
| * if a specified wait condition preexists, no blocking occurs. | |
| * @return 0 signaled or preexisted, ETCH_TIMEOUT timeout, | |
| * or -1 if unblocked not due to timeout and condition was not met, or error. | |
| * @return 0 OK (equality signaled or condition preexisted), ETCH_TIMEOUT timeout, -1 error. | |
| */ | |
| int etchwait_waitex (etchwait* waiter, int64* cond_var, int64 cond_waitfor, int timeoutms) | |
| { | |
| apr_thread_cond_t* tc =(apr_thread_cond_t*) waiter->impl; | |
| apr_thread_mutex_t* tm =(apr_thread_mutex_t*) waiter->mutex->impl; | |
| int result = 0; | |
| waiter->mutex->acquire(waiter->mutex); | |
| do | |
| { const int64* waitvar = waiter->cond_var; | |
| const int64 waitvalue = waiter->cond_waitfor; | |
| const unsigned char is_same_monitortype | |
| = (waiter->cond_var && cond_var) || (!waiter->cond_var && !cond_var); | |
| const unsigned char is_requesting_same_waitcond = waiter->cond_var | |
| && (waiter->cond_var == cond_var && waiter->cond_waitfor == cond_waitfor); | |
| const unsigned char is_unblock_required = waiter->threadcount > 0 | |
| && (!is_same_monitortype || !is_requesting_same_waitcond); | |
| /* when new condition differs, unblock threads waiting on prior condition. | |
| * such threads recognize that the unblocking condition is not the monitored , | |
| * condition, and return -1 from this method accordingly, see below. */ | |
| if (is_unblock_required) | |
| apr_thread_cond_broadcast(tc); | |
| waiter->cond_var = cond_var; | |
| waiter->cond_waitfor = cond_waitfor; | |
| waiter->threadcount++; | |
| if (waiter->cond_var && *(waiter->cond_var) == waiter->cond_waitfor) | |
| result = 0; /* wait condition preexists */ | |
| else | |
| if (timeoutms > 0) /* BLOCK here with timeout */ | |
| result = apr_thread_cond_timedwait(tc, tm, timeoutms * 1000); | |
| else /* BLOCK here until signaled */ | |
| result = 0 == apr_thread_cond_wait(tc, tm)? 0: -1; | |
| /* thread is now unblocked. result will be APR_TIMEUP if timed out. */ | |
| /* check if we were previously waiting for something other condition*/ | |
| if((result == 0) && waitvar && waitvalue && (*waitvar != waitvalue)) | |
| result = -2; /* current state is not the monitored condition */ | |
| switch(result) | |
| { case 0: break; /* 0 = APR_SUCCESS */ | |
| case APR_TIMEUP: result = ETCH_TIMEOUT; break; | |
| default: result = -1; /* condition not met or error */ | |
| } | |
| waiter->cond_var = NULL; /* reset */ | |
| waiter->threadcount--; | |
| } while(0); | |
| waiter->mutex->release(waiter->mutex); | |
| return result; | |
| } | |
| /** | |
| * etchwait_wait_until_signaled() | |
| * block until signaled or specified condition set. | |
| * if a specified wait condition preexists, no blocking occurs. | |
| * @return 0 signaled or preexisted, -1 error. | |
| */ | |
| int etchwait_wait_until_signaled (etchwait* waiter) | |
| { | |
| return etchwait_waitex (waiter, NULL, 0, 0); | |
| } | |
| /** | |
| * etchwait_timed_wait_until_signaled() | |
| * block until signaled or timout. | |
| * if a specified wait condition preexists, no blocking occurs. | |
| * @return 0 OK (signaled), ETCH_TIMEOUT timeout, -1 error. | |
| */ | |
| int etchwait_timed_wait_until_signaled (etchwait* waiter, int timeoutms) | |
| { | |
| return etchwait_waitex (waiter, NULL, 0, timeoutms); | |
| } | |
| /** | |
| * etchwait_wait_until_equal() | |
| * block until signaled or specified condition set. | |
| * if a specified wait condition preexists, no blocking occurs. | |
| * @param cond_var a pointer to the monitored long integer. if null, no monitoring | |
| * occurs, and threads must be unblocked with signal(). | |
| * @param cond_waitfor the value for which a set() will be compared for equality. | |
| * @return 0 signaled or preexisted, -1 error. | |
| */ | |
| int etchwait_wait_until_equal (etchwait* waiter, int64* cond_var, int64 cond_waitfor) | |
| { | |
| return etchwait_waitex (waiter, cond_var, cond_waitfor, 0); | |
| } | |
| /** | |
| * etchwait_timed_wait_until_equal() | |
| * block until signaled or specified condition set. | |
| * if a specified wait condition preexists, no blocking occurs. | |
| * @param cond_var a pointer to the monitored long integer. if null, no monitoring | |
| * occurs, and threads must be unblocked with signal(). | |
| * @param cond_waitfor the value for which a set() will be compared for equality. | |
| * @param timeoutms length of time to wait for the condition. if the equality condition | |
| * already exists, no blocking is performed and zero is returned. | |
| * @return 0 OK (equality signaled or condition preexisted), ETCH_TIMEOUT timeout, -1 error. | |
| */ | |
| int etchwait_timed_wait_until_equal (etchwait* waiter, int64* cond_var, int64 cond_waitfor, int timeoutms) | |
| { | |
| return etchwait_waitex (waiter, cond_var, cond_waitfor, timeoutms); | |
| } | |
| /** | |
| * etchwait_signal() | |
| * unblock first waiting thread | |
| */ | |
| int etchwait_signal (etchwait* waiter) | |
| { | |
| apr_thread_cond_t* tc =(apr_thread_cond_t*) waiter->impl; | |
| waiter->mutex->acquire(waiter->mutex); | |
| apr_thread_cond_signal(tc); | |
| waiter->mutex->release(waiter->mutex); | |
| return 0; | |
| } | |
| /** | |
| * etchwait_broadcast() | |
| * unblock all waiting threads | |
| */ | |
| int etchwait_broadcast (etchwait* waiter) | |
| { | |
| apr_thread_cond_t* tc =(apr_thread_cond_t*) waiter->impl; | |
| waiter->mutex->acquire(waiter->mutex); | |
| apr_thread_cond_broadcast(tc); | |
| waiter->mutex->release(waiter->mutex); | |
| return 0; | |
| } | |
| /** | |
| * etchwait_setvalue() | |
| * set condition variable and signal if wait condition met | |
| */ | |
| int etchwait_setvalue(etchwait* waiter, int64 newval) | |
| { | |
| int result = -1; | |
| if (waiter->cond_var) /* if waiter is monitoring a condition ... */ | |
| { | |
| result = 0; | |
| *(waiter->cond_var) = newval; | |
| if (newval == waiter->cond_waitfor) /* ... if condition met ... */ | |
| etchwait_broadcast(waiter); /* ... unblock waiting threads */ | |
| } | |
| return result; | |
| } | |
| /** | |
| * new_wait() | |
| * etchwait constructor. | |
| * etchwait is a thread wait condition that blocks until signaled, or a condition is met, or timeout. | |
| * currently wraps an APR condition variable and mutex pair. | |
| * @param params the apr memory pool | |
| */ | |
| etchwait* new_wait(void* params) | |
| { | |
| apr_thread_cond_t* tcx = 0; | |
| apr_status_t xresult = 0; | |
| apr_pool_t* aprpool = 0; | |
| etchwait* newwait = 0; | |
| aprpool = (apr_pool_t*) params; | |
| xresult = apr_thread_cond_create(&tcx, aprpool); | |
| if (xresult != 0) return NULL; | |
| newwait = (etchwait*) new_object(sizeof(etchwait), ETCHTYPEB_WAIT, CLASSID_WAIT); | |
| if (NULL == newwait) return NULL; | |
| newwait->mutex = new_mutex(aprpool, ETCHMUTEX_UNNESTED); | |
| newwait->impl = tcx; | |
| newwait->destroy = destroy_etchwait; | |
| newwait->clone = clone_null; | |
| newwait->wait = etchwait_wait_until_signaled; | |
| newwait->timed_wait = etchwait_timed_wait_until_signaled; | |
| newwait->waitequal = etchwait_wait_until_equal; | |
| newwait->timed_waitequal = etchwait_timed_wait_until_equal; | |
| newwait->signal = etchwait_signal; | |
| newwait->broadcast = etchwait_broadcast; | |
| newwait->set = etchwait_setvalue; | |
| return newwait; | |
| } | |