rwlock.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /** BEGIN COPYRIGHT BLOCK
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. * END COPYRIGHT BLOCK **/
  38. #ifdef HAVE_CONFIG_H
  39. # include <config.h>
  40. #endif
  41. /*
  42. * rwlock.c - generic multiple reader, single-writer locking routines.
  43. *
  44. * The general idea is:
  45. *
  46. * If you have a data structure which you'd like to allow multiple threads
  47. * to read, but only one thread at a time to write, you include in your
  48. * data structure a pointer to an rwl structure, and call rwl_new() to
  49. * obtain an allocated and initialized rwl structure.
  50. *
  51. * Then, call the appropriate functions via the provided function pointers
  52. * to acquire/relinquish read or write locks on your data structure. You
  53. * may want to provide some convenience macros to make the code prettier.
  54. *
  55. * The semantics are:
  56. * - a thread attempting to obtain a read lock will succeed immediately as
  57. * long as there are no threads with write locks.
  58. * - a thread attempting to obtain a write lock will wait until all readers
  59. * have relinquished their read locks.
  60. * - a thread attempting to obtain a write lock blocks other threads from
  61. * obtaining read locks. As long as all readers release their locks,
  62. * the write will eventually get the lock.
  63. */
  64. #include "slap.h"
  65. #include <prlock.h>
  66. #include <prcvar.h>
  67. #include "rwlock.h"
  68. /*
  69. * Function: __rwl_acquire_read_lock
  70. *
  71. * Description: acquire a read lock.
  72. *
  73. * Arguments: rp: pointer to an rwl stucture
  74. *
  75. * Returns: 0 on success, -1 on failure.
  76. */
  77. static int
  78. __rwl_acquire_read_lock( rwl *rp )
  79. {
  80. if ( rp == NULL ) {
  81. return -1;
  82. }
  83. PR_Lock( rp->rwl_writers_mutex );
  84. PR_Lock( rp->rwl_readers_mutex );
  85. rp->rwl_num_readers++;
  86. (void)PR_Unlock( rp->rwl_readers_mutex );
  87. (void)PR_Unlock( rp->rwl_writers_mutex );
  88. return 0;
  89. }
  90. /*
  91. * Function: __rwl_acquire_write_lock
  92. *
  93. * Description: acquire a write lock.
  94. *
  95. * Arguments: rp: pointer to an rwl stucture
  96. *
  97. * Returns: 0 on success, -1 on failure.
  98. */
  99. static int
  100. __rwl_acquire_write_lock( rwl *rp )
  101. {
  102. if ( rp == NULL ) {
  103. return -1;
  104. }
  105. PR_Lock( rp->rwl_writers_mutex );
  106. PR_Lock( rp->rwl_readers_mutex );
  107. rp->rwl_writer_waiting = 1;
  108. while ( rp->rwl_num_readers > 0 ) {
  109. if ( PR_WaitCondVar( rp->rwl_writer_waiting_cv, PR_INTERVAL_NO_TIMEOUT ) != 0 ) {
  110. (void)PR_Unlock( rp->rwl_writers_mutex );
  111. (void)PR_Unlock( rp->rwl_readers_mutex );
  112. return -1;
  113. }
  114. }
  115. /* XXXggood should rwl_writer_waiting be set zero here? */
  116. return 0;
  117. }
  118. /*
  119. * Function: __rwl_relinquish_read_lock
  120. *
  121. * Description: relinquish a read lock.
  122. *
  123. * Arguments: rp: pointer to an rwl stucture
  124. *
  125. * Returns: 0 on success, -1 on failure.
  126. */
  127. static int
  128. __rwl_relinquish_read_lock( rwl *rp )
  129. {
  130. if ( rp == NULL ) {
  131. return -1;
  132. }
  133. PR_Lock( rp->rwl_readers_mutex );
  134. if ( --rp->rwl_num_readers == 0 && rp->rwl_writer_waiting ) {
  135. PR_NotifyCondVar( rp->rwl_writer_waiting_cv );
  136. }
  137. (void)PR_Unlock( rp->rwl_readers_mutex );
  138. return 0;
  139. }
  140. /*
  141. * Function: __rwl_relinquish_write_lock
  142. *
  143. * Description: relinquish a write lock.
  144. *
  145. * Arguments: rp: pointer to an rwl stucture
  146. *
  147. * Returns: 0 on success, -1 on failure.
  148. */
  149. static int
  150. __rwl_relinquish_write_lock( rwl *rp )
  151. {
  152. if ( rp == NULL ) {
  153. return -1;
  154. }
  155. rp->rwl_writer_waiting = 0;
  156. (void)PR_Unlock( rp->rwl_readers_mutex );
  157. (void)PR_Unlock( rp->rwl_writers_mutex );
  158. return 0;
  159. }
  160. /*
  161. * Function: rwl_new
  162. *
  163. * Description: allocate and initialize a wrl structure.
  164. *
  165. * Arguments: none
  166. *
  167. * Returns: on success, returns a pointer to an allocated, initialized rwl structure.
  168. * on failure, returns NULL.
  169. *
  170. */
  171. rwl *
  172. rwl_new()
  173. {
  174. rwl *rp;
  175. rp = (rwl *)slapi_ch_malloc( sizeof( rwl ));
  176. if (( rp->rwl_readers_mutex = PR_NewLock()) == NULL ) {
  177. slapi_ch_free( (void **)&rp );
  178. return NULL;
  179. }
  180. if (( rp->rwl_writers_mutex = PR_NewLock()) == NULL ) {
  181. PR_DestroyLock( rp->rwl_readers_mutex );
  182. slapi_ch_free( (void **)&rp );
  183. return NULL;
  184. }
  185. if (( rp->rwl_writer_waiting_cv = PR_NewCondVar( rp->rwl_readers_mutex )) == NULL ) {
  186. PR_DestroyLock( rp->rwl_readers_mutex );
  187. PR_DestroyLock( rp->rwl_writers_mutex );
  188. slapi_ch_free( (void **)&rp );
  189. }
  190. rp->rwl_num_readers = rp->rwl_writer_waiting = 0;
  191. rp->rwl_acquire_read_lock = __rwl_acquire_read_lock;
  192. rp->rwl_relinquish_read_lock = __rwl_relinquish_read_lock;
  193. rp->rwl_acquire_write_lock = __rwl_acquire_write_lock;
  194. rp->rwl_relinquish_write_lock = __rwl_relinquish_write_lock;
  195. return rp;
  196. }
  197. /*
  198. * Function: rwl_free
  199. *
  200. * Description: deallocates and frees an rwl structure.
  201. *
  202. * Arguments: rh: handle to an rwl structure.
  203. *
  204. * Returns: nothing
  205. */
  206. void
  207. rwl_free( rwl **rh )
  208. {
  209. rwl *rp;
  210. if ( rh == NULL || *rh == NULL ) {
  211. return;
  212. }
  213. rp = *rh;
  214. if ( rp->rwl_readers_mutex != NULL ) {
  215. PR_DestroyLock( rp->rwl_readers_mutex );
  216. }
  217. if ( rp->rwl_writers_mutex != NULL ) {
  218. PR_DestroyLock( rp->rwl_writers_mutex );
  219. }
  220. if ( rp->rwl_writer_waiting_cv != NULL ) {
  221. PR_DestroyCondVar( rp->rwl_writer_waiting_cv );
  222. }
  223. memset( rp, '\0', sizeof( rwl ));
  224. slapi_ch_free( (void **)&rp );
  225. *rh = NULL;
  226. }