thread_rwlock.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. /*Read/Write locking implementation based on the MultiLock code from
  17. * Stephen Beaulieu <hippo@be.com>
  18. */
  19. #include "fspr_arch_thread_rwlock.h"
  20. #include "fspr_strings.h"
  21. #include "fspr_portable.h"
  22. #define BIG_NUM 100000
  23. static fspr_status_t _thread_rw_cleanup(void * data)
  24. {
  25. fspr_thread_rwlock_t *mutex = (fspr_thread_rwlock_t*)data;
  26. if (mutex->ReadCount != 0) {
  27. while (atomic_add(&mutex->ReadCount , -1) > 1){
  28. release_sem (mutex->Read);
  29. }
  30. }
  31. if (mutex->WriteCount != 0) {
  32. while (atomic_add(&mutex->WriteCount , -1) > 1){
  33. release_sem (mutex->Write);
  34. }
  35. }
  36. if (mutex->LockCount != 0) {
  37. while (atomic_add(&mutex->LockCount , -1) > 1){
  38. release_sem (mutex->Lock);
  39. }
  40. }
  41. delete_sem(mutex->Read);
  42. delete_sem(mutex->Write);
  43. delete_sem(mutex->Lock);
  44. return APR_SUCCESS;
  45. }
  46. APR_DECLARE(fspr_status_t) fspr_thread_rwlock_create(fspr_thread_rwlock_t **rwlock,
  47. fspr_pool_t *pool)
  48. {
  49. fspr_thread_rwlock_t *new;
  50. new = (fspr_thread_rwlock_t *)fspr_pcalloc(pool, sizeof(fspr_thread_rwlock_t));
  51. if (new == NULL){
  52. return APR_ENOMEM;
  53. }
  54. new->pool = pool;
  55. /* we need to make 3 locks... */
  56. new->ReadCount = 0;
  57. new->WriteCount = 0;
  58. new->LockCount = 0;
  59. new->Read = create_sem(0, "APR_ReadLock");
  60. new->Write = create_sem(0, "APR_WriteLock");
  61. new->Lock = create_sem(0, "APR_Lock");
  62. if (new->Lock < 0 || new->Read < 0 || new->Write < 0) {
  63. _thread_rw_cleanup(new);
  64. return -1;
  65. }
  66. fspr_pool_cleanup_register(new->pool, (void *)new, _thread_rw_cleanup,
  67. fspr_pool_cleanup_null);
  68. (*rwlock) = new;
  69. return APR_SUCCESS;
  70. }
  71. APR_DECLARE(fspr_status_t) fspr_thread_rwlock_rdlock(fspr_thread_rwlock_t *rwlock)
  72. {
  73. int32 rv = APR_SUCCESS;
  74. if (find_thread(NULL) == rwlock->writer) {
  75. /* we're the writer - no problem */
  76. rwlock->Nested++;
  77. } else {
  78. /* we're not the writer */
  79. int32 r = atomic_add(&rwlock->ReadCount, 1);
  80. if (r < 0) {
  81. /* Oh dear, writer holds lock, wait for sem */
  82. rv = acquire_sem_etc(rwlock->Read, 1, B_DO_NOT_RESCHEDULE,
  83. B_INFINITE_TIMEOUT);
  84. }
  85. }
  86. return rv;
  87. }
  88. APR_DECLARE(fspr_status_t) fspr_thread_rwlock_tryrdlock(fspr_thread_rwlock_t *rwlock)
  89. {
  90. return APR_ENOTIMPL;
  91. }
  92. APR_DECLARE(fspr_status_t) fspr_thread_rwlock_wrlock(fspr_thread_rwlock_t *rwlock)
  93. {
  94. int rv = APR_SUCCESS;
  95. if (find_thread(NULL) == rwlock->writer) {
  96. rwlock->Nested++;
  97. } else {
  98. /* we're not the writer... */
  99. if (atomic_add(&rwlock->LockCount, 1) >= 1) {
  100. /* we're locked - acquire the sem */
  101. rv = acquire_sem_etc(rwlock->Lock, 1, B_DO_NOT_RESCHEDULE,
  102. B_INFINITE_TIMEOUT);
  103. }
  104. if (rv == APR_SUCCESS) {
  105. /* decrement the ReadCount to a large -ve number so that
  106. * we block on new readers...
  107. */
  108. int32 readers = atomic_add(&rwlock->ReadCount, -BIG_NUM);
  109. if (readers > 0) {
  110. /* readers are holding the lock */
  111. rv = acquire_sem_etc(rwlock->Write, readers, B_DO_NOT_RESCHEDULE,
  112. B_INFINITE_TIMEOUT);
  113. }
  114. if (rv == APR_SUCCESS)
  115. rwlock->writer = find_thread(NULL);
  116. }
  117. }
  118. return rv;
  119. }
  120. APR_DECLARE(fspr_status_t) fspr_thread_rwlock_trywrlock(fspr_thread_rwlock_t *rwlock)
  121. {
  122. return APR_ENOTIMPL;
  123. }
  124. APR_DECLARE(fspr_status_t) fspr_thread_rwlock_unlock(fspr_thread_rwlock_t *rwlock)
  125. {
  126. fspr_status_t rv = APR_SUCCESS;
  127. int32 readers;
  128. /* we know we hold the lock, so don't check it :) */
  129. if (find_thread(NULL) == rwlock->writer) {
  130. /* we know we hold the lock, so don't check it :) */
  131. if (rwlock->Nested > 1) {
  132. /* we're recursively locked */
  133. rwlock->Nested--;
  134. return APR_SUCCESS;
  135. }
  136. /* OK so we need to release the sem if we have it :) */
  137. readers = atomic_add(&rwlock->ReadCount, BIG_NUM) + BIG_NUM;
  138. if (readers > 0) {
  139. rv = release_sem_etc(rwlock->Read, readers, B_DO_NOT_RESCHEDULE);
  140. }
  141. if (rv == APR_SUCCESS) {
  142. rwlock->writer = -1;
  143. if (atomic_add(&rwlock->LockCount, -1) > 1) {
  144. rv = release_sem_etc(rwlock->Lock, 1, B_DO_NOT_RESCHEDULE);
  145. }
  146. }
  147. } else {
  148. /* We weren't the Writer, so just release the ReadCount... */
  149. if (atomic_add(&rwlock->ReadCount, -1) < 0) {
  150. /* we have a writer waiting for the lock, so release it */
  151. rv = release_sem_etc(rwlock->Write, 1, B_DO_NOT_RESCHEDULE);
  152. }
  153. }
  154. return rv;
  155. }
  156. APR_DECLARE(fspr_status_t) fspr_thread_rwlock_destroy(fspr_thread_rwlock_t *rwlock)
  157. {
  158. fspr_status_t stat;
  159. if ((stat = _thread_rw_cleanup(rwlock)) == APR_SUCCESS) {
  160. fspr_pool_cleanup_kill(rwlock->pool, rwlock, _thread_rw_cleanup);
  161. return APR_SUCCESS;
  162. }
  163. return stat;
  164. }
  165. APR_POOL_IMPLEMENT_ACCESSOR(thread_rwlock)