| /* 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. |
| */ |
| |
| #include "apr.h" |
| #include "apr_private.h" |
| #include "apr_general.h" |
| #include "apr_strings.h" |
| #include "win32/apr_arch_thread_rwlock.h" |
| #include "apr_portable.h" |
| |
| static apr_status_t thread_rwlock_cleanup(void *data) |
| { |
| apr_thread_rwlock_t *rwlock = data; |
| |
| if (! CloseHandle(rwlock->read_event)) |
| return apr_get_os_error(); |
| |
| if (! CloseHandle(rwlock->write_mutex)) |
| return apr_get_os_error(); |
| |
| return APR_SUCCESS; |
| } |
| |
| APR_DECLARE(apr_status_t)apr_thread_rwlock_create(apr_thread_rwlock_t **rwlock, |
| apr_pool_t *pool) |
| { |
| *rwlock = apr_palloc(pool, sizeof(**rwlock)); |
| |
| (*rwlock)->pool = pool; |
| (*rwlock)->readers = 0; |
| |
| if (! ((*rwlock)->read_event = CreateEvent(NULL, TRUE, FALSE, NULL))) { |
| *rwlock = NULL; |
| return apr_get_os_error(); |
| } |
| |
| if (! ((*rwlock)->write_mutex = CreateMutex(NULL, FALSE, NULL))) { |
| CloseHandle((*rwlock)->read_event); |
| *rwlock = NULL; |
| return apr_get_os_error(); |
| } |
| |
| apr_pool_cleanup_register(pool, *rwlock, thread_rwlock_cleanup, |
| apr_pool_cleanup_null); |
| |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t apr_thread_rwlock_rdlock_core(apr_thread_rwlock_t *rwlock, |
| DWORD milliseconds) |
| { |
| DWORD code = WaitForSingleObject(rwlock->write_mutex, milliseconds); |
| |
| if (code == WAIT_FAILED || code == WAIT_TIMEOUT) |
| return APR_FROM_OS_ERROR(code); |
| |
| /* We've successfully acquired the writer mutex, we can't be locked |
| * for write, so it's OK to add the reader lock. The writer mutex |
| * doubles as race condition protection for the readers counter. |
| */ |
| InterlockedIncrement(&rwlock->readers); |
| |
| if (! ResetEvent(rwlock->read_event)) |
| return apr_get_os_error(); |
| |
| if (! ReleaseMutex(rwlock->write_mutex)) |
| return apr_get_os_error(); |
| |
| return APR_SUCCESS; |
| } |
| |
| APR_DECLARE(apr_status_t) apr_thread_rwlock_rdlock(apr_thread_rwlock_t *rwlock) |
| { |
| return apr_thread_rwlock_rdlock_core(rwlock, INFINITE); |
| } |
| |
| APR_DECLARE(apr_status_t) |
| apr_thread_rwlock_tryrdlock(apr_thread_rwlock_t *rwlock) |
| { |
| return apr_thread_rwlock_rdlock_core(rwlock, 0); |
| } |
| |
| static apr_status_t |
| apr_thread_rwlock_wrlock_core(apr_thread_rwlock_t *rwlock, DWORD milliseconds) |
| { |
| DWORD code = WaitForSingleObject(rwlock->write_mutex, milliseconds); |
| |
| if (code == WAIT_FAILED || code == WAIT_TIMEOUT) |
| return APR_FROM_OS_ERROR(code); |
| |
| /* We've got the writer lock but we have to wait for all readers to |
| * unlock before it's ok to use it. |
| */ |
| if (rwlock->readers) { |
| /* Must wait for readers to finish before returning, unless this |
| * is an trywrlock (milliseconds == 0): |
| */ |
| code = milliseconds |
| ? WaitForSingleObject(rwlock->read_event, milliseconds) |
| : WAIT_TIMEOUT; |
| |
| if (code == WAIT_FAILED || code == WAIT_TIMEOUT) { |
| /* Unable to wait for readers to finish, release write lock: */ |
| if (! ReleaseMutex(rwlock->write_mutex)) |
| return apr_get_os_error(); |
| |
| return APR_FROM_OS_ERROR(code); |
| } |
| } |
| |
| return APR_SUCCESS; |
| } |
| |
| APR_DECLARE(apr_status_t) apr_thread_rwlock_wrlock(apr_thread_rwlock_t *rwlock) |
| { |
| return apr_thread_rwlock_wrlock_core(rwlock, INFINITE); |
| } |
| |
| APR_DECLARE(apr_status_t)apr_thread_rwlock_trywrlock(apr_thread_rwlock_t *rwlock) |
| { |
| return apr_thread_rwlock_wrlock_core(rwlock, 0); |
| } |
| |
| APR_DECLARE(apr_status_t) apr_thread_rwlock_unlock(apr_thread_rwlock_t *rwlock) |
| { |
| apr_status_t rv = 0; |
| |
| /* First, guess that we're unlocking a writer */ |
| if (! ReleaseMutex(rwlock->write_mutex)) |
| rv = apr_get_os_error(); |
| |
| if (rv == APR_FROM_OS_ERROR(ERROR_NOT_OWNER)) { |
| /* Nope, we must have a read lock */ |
| if (rwlock->readers && |
| ! InterlockedDecrement(&rwlock->readers) && |
| ! SetEvent(rwlock->read_event)) { |
| rv = apr_get_os_error(); |
| } |
| else { |
| rv = 0; |
| } |
| } |
| |
| return rv; |
| } |
| |
| APR_DECLARE(apr_status_t) apr_thread_rwlock_destroy(apr_thread_rwlock_t *rwlock) |
| { |
| return apr_pool_cleanup_run(rwlock->pool, rwlock, thread_rwlock_cleanup); |
| } |
| |
| APR_POOL_IMPLEMENT_ACCESSOR(thread_rwlock) |