]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/rdwr.c
60e2a420b69da768127daef7d327b1addcc3c40f
[openldap] / libraries / libldap_r / rdwr.c
1 /*
2 ** This is an improved implementation of Reader/Writer locks does
3 ** not protect writers from starvation.  That is, if a writer is
4 ** currently waiting on a reader, any new reader will get
5 ** the lock before the writer.
6 **
7 ** Does not support cancellation nor does any status checking.
8 */
9
10 /********************************************************
11  * Adapted from:
12  *      "Programming with Posix Threads"
13  *              by David R Butenhof
14  *              Addison-Wesley 
15  ********************************************************
16  */
17
18 #include "portable.h"
19
20 #include <stdlib.h>
21
22 #include <ac/errno.h>
23 #include <ac/string.h>
24
25 #include "ldap_pvt_thread.h"
26
27 int 
28 ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rw )
29 {
30         assert( rw != NULL );
31
32         memset( rw, 0, sizeof(ldap_pvt_thread_rdwr_t) );
33
34         /* we should check return results */
35         ldap_pvt_thread_mutex_init( &rw->ltrw_mutex );
36         ldap_pvt_thread_cond_init( &rw->ltrw_read );
37         ldap_pvt_thread_cond_init( &rw->ltrw_write );
38
39         rw->ltrw_valid = LDAP_PVT_THREAD_RDWR_VALID;
40         return 0;
41 }
42
43 int 
44 ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rw )
45 {
46         assert( rw != NULL );
47         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
48
49         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
50                 return LDAP_PVT_THREAD_EINVAL;
51
52         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
53
54         /* active threads? */
55         if( rw->ltrw_r_active > 0 || rw->ltrw_w_active > 1) {
56                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
57                 return LDAP_PVT_THREAD_EBUSY;
58         }
59
60         /* waiting threads? */
61         if( rw->ltrw_r_wait > 0 || rw->ltrw_w_wait > 0) {
62                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
63                 return LDAP_PVT_THREAD_EBUSY;
64         }
65
66         rw->ltrw_valid = 0;
67
68         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
69
70         ldap_pvt_thread_mutex_destroy( &rw->ltrw_mutex );
71         ldap_pvt_thread_cond_destroy( &rw->ltrw_read );
72         ldap_pvt_thread_cond_destroy( &rw->ltrw_write );
73
74         return 0;
75 }
76
77 int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rw )
78 {
79         assert( rw != NULL );
80         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
81
82         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
83                 return LDAP_PVT_THREAD_EINVAL;
84
85         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
86
87         if( rw->ltrw_w_active > 1 ) {
88                 /* writer is active */
89
90                 rw->ltrw_r_wait++;
91
92                 do {
93                         ldap_pvt_thread_cond_wait(
94                                 &rw->ltrw_read, &rw->ltrw_mutex );
95                 } while( rw->ltrw_w_active > 1 );
96
97                 rw->ltrw_r_wait--;
98         }
99
100         rw->ltrw_r_active++;
101
102         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
103
104         return 0;
105 }
106
107 int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rw )
108 {
109         assert( rw != NULL );
110         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
111
112         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
113                 return LDAP_PVT_THREAD_EINVAL;
114
115         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
116
117         if( rw->ltrw_w_active > 1) {
118                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
119                 return LDAP_PVT_THREAD_EBUSY;
120         }
121
122         rw->ltrw_r_active++;
123
124         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
125
126         return 0;
127 }
128
129 int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rw )
130 {
131         assert( rw != NULL );
132         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
133
134         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
135                 return LDAP_PVT_THREAD_EINVAL;
136
137         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
138
139         rw->ltrw_r_active--;
140
141         if (rw->ltrw_r_active == 0 && rw->ltrw_w_wait > 0 ) {
142                 ldap_pvt_thread_cond_signal( &rw->ltrw_write );
143         }
144
145         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
146
147         return 0;
148 }
149
150 int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rw )
151 {
152         assert( rw != NULL );
153         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
154
155         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
156                 return LDAP_PVT_THREAD_EINVAL;
157
158         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
159
160         if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
161                 rw->ltrw_w_wait++;
162
163                 do {
164                         ldap_pvt_thread_cond_wait(
165                                 &rw->ltrw_write, &rw->ltrw_mutex );
166                 } while ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 );
167
168                 rw->ltrw_w_wait--;
169         }
170
171         rw->ltrw_w_active++;
172
173         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
174
175         return 0;
176 }
177
178 int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rw )
179 {
180         assert( rw != NULL );
181         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
182
183         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
184                 return LDAP_PVT_THREAD_EINVAL;
185
186         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
187
188         if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
189                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
190                 return LDAP_PVT_THREAD_EBUSY;
191         }
192
193         rw->ltrw_w_active++;
194
195         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
196
197         return 0;
198 }
199
200 int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw )
201 {
202         assert( rw != NULL );
203         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
204
205         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
206                 return LDAP_PVT_THREAD_EINVAL;
207
208         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
209
210         rw->ltrw_w_active--;
211
212         if (rw->ltrw_r_wait > 0) {
213                 ldap_pvt_thread_cond_broadcast( &rw->ltrw_read );
214
215         } else if (rw->ltrw_w_wait > 0) {
216                 ldap_pvt_thread_cond_signal( &rw->ltrw_write );
217         }
218
219         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
220
221         return 0;
222 }
223
224 #ifdef LDAP_DEBUG
225
226 /* just for testing, 
227  * return 0 if false, suitable for assert(ldap_pvt_thread_rdwr_Xchk(rdwr))
228  * 
229  * Currently they don't check if the calling thread is the one 
230  * that has the lock, just that there is a reader or writer.
231  *
232  * Basically sufficent for testing that places that should have
233  * a lock are caught.
234  */
235
236 int ldap_pvt_thread_rdwr_readers(ldap_pvt_thread_rdwr_t *rw)
237 {
238         assert( rw != NULL );
239         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
240
241         return( rw->ltrw_r_active );
242 }
243
244 int ldap_pvt_thread_rdwr_writers(ldap_pvt_thread_rdwr_t *rw)
245 {
246         assert( rw != NULL );
247         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
248
249         return( rw->ltrw_w_active );
250 }
251
252 int ldap_pvt_thread_rdwr_active(ldap_pvt_thread_rdwr_t *rw)
253 {
254         assert( rw != NULL );
255         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
256
257         return(ldap_pvt_thread_rdwr_readers(rw) +
258                ldap_pvt_thread_rdwr_writers(rw));
259 }
260
261 #endif /* LDAP_DEBUG */