1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "apr_general.h" 18#include "apr_lib.h" 19#include "apr_strings.h" 20#include "apr_portable.h" 21#include "apr_arch_thread_rwlock.h" 22#include "apr_arch_file_io.h" 23#include <string.h> 24 25static apr_status_t thread_rwlock_cleanup(void *therwlock) 26{ 27 apr_thread_rwlock_t *rwlock = therwlock; 28 return apr_thread_rwlock_destroy(rwlock); 29} 30 31 32 33APR_DECLARE(apr_status_t) apr_thread_rwlock_create(apr_thread_rwlock_t **rwlock, 34 apr_pool_t *pool) 35{ 36 apr_thread_rwlock_t *new_rwlock; 37 ULONG rc; 38 39 new_rwlock = (apr_thread_rwlock_t *)apr_palloc(pool, sizeof(apr_thread_rwlock_t)); 40 new_rwlock->pool = pool; 41 new_rwlock->readers = 0; 42 43 rc = DosCreateMutexSem(NULL, &(new_rwlock->write_lock), 0, FALSE); 44 45 if (rc) 46 return APR_FROM_OS_ERROR(rc); 47 48 rc = DosCreateEventSem(NULL, &(new_rwlock->read_done), 0, FALSE); 49 50 if (rc) 51 return APR_FROM_OS_ERROR(rc); 52 53 *rwlock = new_rwlock; 54 55 if (!rc) 56 apr_pool_cleanup_register(pool, new_rwlock, thread_rwlock_cleanup, 57 apr_pool_cleanup_null); 58 59 return APR_FROM_OS_ERROR(rc); 60} 61 62 63 64APR_DECLARE(apr_status_t) apr_thread_rwlock_rdlock(apr_thread_rwlock_t *rwlock) 65{ 66 ULONG rc, posts; 67 68 rc = DosRequestMutexSem(rwlock->write_lock, SEM_INDEFINITE_WAIT); 69 70 if (rc) 71 return APR_FROM_OS_ERROR(rc); 72 73 /* We've successfully acquired the writer mutex so we can't be locked 74 * for write which means it's ok to add a reader lock. The writer mutex 75 * doubles as race condition protection for the readers counter. 76 */ 77 rwlock->readers++; 78 DosResetEventSem(rwlock->read_done, &posts); 79 rc = DosReleaseMutexSem(rwlock->write_lock); 80 return APR_FROM_OS_ERROR(rc); 81} 82 83 84 85APR_DECLARE(apr_status_t) apr_thread_rwlock_tryrdlock(apr_thread_rwlock_t *rwlock) 86{ 87 /* As above but with different wait time */ 88 ULONG rc, posts; 89 90 rc = DosRequestMutexSem(rwlock->write_lock, SEM_IMMEDIATE_RETURN); 91 92 if (rc) 93 return APR_FROM_OS_ERROR(rc); 94 95 rwlock->readers++; 96 DosResetEventSem(rwlock->read_done, &posts); 97 rc = DosReleaseMutexSem(rwlock->write_lock); 98 return APR_FROM_OS_ERROR(rc); 99} 100 101 102 103APR_DECLARE(apr_status_t) apr_thread_rwlock_wrlock(apr_thread_rwlock_t *rwlock) 104{ 105 ULONG rc; 106 107 rc = DosRequestMutexSem(rwlock->write_lock, SEM_INDEFINITE_WAIT); 108 109 if (rc) 110 return APR_FROM_OS_ERROR(rc); 111 112 /* We've got the writer lock but we have to wait for all readers to 113 * unlock before it's ok to use it 114 */ 115 116 if (rwlock->readers) { 117 rc = DosWaitEventSem(rwlock->read_done, SEM_INDEFINITE_WAIT); 118 119 if (rc) 120 DosReleaseMutexSem(rwlock->write_lock); 121 } 122 123 return APR_FROM_OS_ERROR(rc); 124} 125 126 127 128APR_DECLARE(apr_status_t) apr_thread_rwlock_trywrlock(apr_thread_rwlock_t *rwlock) 129{ 130 ULONG rc; 131 132 rc = DosRequestMutexSem(rwlock->write_lock, SEM_IMMEDIATE_RETURN); 133 134 if (rc) 135 return APR_FROM_OS_ERROR(rc); 136 137 /* We've got the writer lock but we have to wait for all readers to 138 * unlock before it's ok to use it 139 */ 140 141 if (rwlock->readers) { 142 /* There are readers active, give up */ 143 DosReleaseMutexSem(rwlock->write_lock); 144 rc = ERROR_TIMEOUT; 145 } 146 147 return APR_FROM_OS_ERROR(rc); 148} 149 150 151 152APR_DECLARE(apr_status_t) apr_thread_rwlock_unlock(apr_thread_rwlock_t *rwlock) 153{ 154 ULONG rc; 155 156 /* First, guess that we're unlocking a writer */ 157 rc = DosReleaseMutexSem(rwlock->write_lock); 158 159 if (rc == ERROR_NOT_OWNER) { 160 /* Nope, we must have a read lock */ 161 if (rwlock->readers) { 162 DosEnterCritSec(); 163 rwlock->readers--; 164 165 if (rwlock->readers == 0) { 166 DosPostEventSem(rwlock->read_done); 167 } 168 169 DosExitCritSec(); 170 rc = 0; 171 } 172 } 173 174 return APR_FROM_OS_ERROR(rc); 175} 176 177 178 179APR_DECLARE(apr_status_t) apr_thread_rwlock_destroy(apr_thread_rwlock_t *rwlock) 180{ 181 ULONG rc; 182 183 if (rwlock->write_lock == 0) 184 return APR_SUCCESS; 185 186 while (DosReleaseMutexSem(rwlock->write_lock) == 0); 187 188 rc = DosCloseMutexSem(rwlock->write_lock); 189 190 if (!rc) { 191 rwlock->write_lock = 0; 192 DosCloseEventSem(rwlock->read_done); 193 return APR_SUCCESS; 194 } 195 196 return APR_FROM_OS_ERROR(rc); 197} 198 199APR_POOL_IMPLEMENT_ACCESSOR(thread_rwlock) 200 201