]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/rdwr.c
225d97b9413224ab0a323a5fe9ed1ca8a7404002
[openldap] / libraries / libldap_r / rdwr.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2003 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 /* This work was initially developed by Kurt D. Zeilenga for inclusion
16  * in OpenLDAP Software.  Additional significant contributors include:
17  *     Stuart Lynne
18  */
19
20 /*
21  * This is an improved implementation of Reader/Writer locks does
22  * not protect writers from starvation.  That is, if a writer is
23  * currently waiting on a reader, any new reader will get
24  * the lock before the writer.
25  *
26  * Does not support cancellation nor does any status checking.
27  */
28 /* Adapted from publically available examples for:
29  *      "Programming with Posix Threads"
30  *              by David R Butenhof, Addison-Wesley 
31  *              http://cseng.aw.com/bookpage.taf?ISBN=0-201-63392-2
32  */
33
34 #include "portable.h"
35
36 #include <ac/stdlib.h>
37
38 #include <ac/errno.h>
39 #include <ac/string.h>
40 #include <ac/time.h>
41
42 #include "ldap-int.h"
43 #include "ldap_pvt_thread.h"
44
45 /*
46  * implementations that provide their own compatible 
47  * reader/writer locks define LDAP_THREAD_HAVE_RDWR
48  * in ldap_pvt_thread.h
49  */
50 #ifndef LDAP_THREAD_HAVE_RDWR
51
52 struct ldap_int_thread_rdwr_s {
53         ldap_pvt_thread_mutex_t ltrw_mutex;
54         ldap_pvt_thread_cond_t ltrw_read;       /* wait for read */
55         ldap_pvt_thread_cond_t ltrw_write;      /* wait for write */
56         int ltrw_valid;
57 #define LDAP_PVT_THREAD_RDWR_VALID 0x0bad
58         int ltrw_r_active;
59         int ltrw_w_active;
60         int ltrw_r_wait;
61         int ltrw_w_wait;
62 };
63
64 int 
65 ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
66 {
67         struct ldap_int_thread_rdwr_s *rw;
68
69         assert( rwlock != NULL );
70
71         rw = (struct ldap_int_thread_rdwr_s *) LDAP_CALLOC( 1,
72                 sizeof( struct ldap_int_thread_rdwr_s ) );
73
74         /* we should check return results */
75         ldap_pvt_thread_mutex_init( &rw->ltrw_mutex );
76         ldap_pvt_thread_cond_init( &rw->ltrw_read );
77         ldap_pvt_thread_cond_init( &rw->ltrw_write );
78
79         rw->ltrw_valid = LDAP_PVT_THREAD_RDWR_VALID;
80
81         *rwlock = rw;
82         return 0;
83 }
84
85 int 
86 ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
87 {
88         struct ldap_int_thread_rdwr_s *rw;
89
90         assert( rwlock != NULL );
91         rw = *rwlock;
92
93         assert( rw != NULL );
94         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
95
96         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
97                 return LDAP_PVT_THREAD_EINVAL;
98
99         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
100
101         assert( rw->ltrw_w_active >= 0 ); 
102         assert( rw->ltrw_w_wait >= 0 ); 
103         assert( rw->ltrw_r_active >= 0 ); 
104         assert( rw->ltrw_r_wait >= 0 ); 
105
106         /* active threads? */
107         if( rw->ltrw_r_active > 0 || rw->ltrw_w_active > 0) {
108                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
109                 return LDAP_PVT_THREAD_EBUSY;
110         }
111
112         /* waiting threads? */
113         if( rw->ltrw_r_wait > 0 || rw->ltrw_w_wait > 0) {
114                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
115                 return LDAP_PVT_THREAD_EBUSY;
116         }
117
118         rw->ltrw_valid = 0;
119
120         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
121
122         ldap_pvt_thread_mutex_destroy( &rw->ltrw_mutex );
123         ldap_pvt_thread_cond_destroy( &rw->ltrw_read );
124         ldap_pvt_thread_cond_destroy( &rw->ltrw_write );
125
126         LDAP_FREE(rw);
127         *rwlock = NULL;
128         return 0;
129 }
130
131 int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock )
132 {
133         struct ldap_int_thread_rdwr_s *rw;
134
135         assert( rwlock != NULL );
136         rw = *rwlock;
137
138         assert( rw != NULL );
139         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
140
141         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
142                 return LDAP_PVT_THREAD_EINVAL;
143
144         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
145
146         assert( rw->ltrw_w_active >= 0 ); 
147         assert( rw->ltrw_w_wait >= 0 ); 
148         assert( rw->ltrw_r_active >= 0 ); 
149         assert( rw->ltrw_r_wait >= 0 ); 
150
151         if( rw->ltrw_w_active > 0 ) {
152                 /* writer is active */
153
154                 rw->ltrw_r_wait++;
155
156                 do {
157                         ldap_pvt_thread_cond_wait(
158                                 &rw->ltrw_read, &rw->ltrw_mutex );
159                 } while( rw->ltrw_w_active > 0 );
160
161                 rw->ltrw_r_wait--;
162                 assert( rw->ltrw_r_wait >= 0 ); 
163         }
164
165         rw->ltrw_r_active++;
166
167         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
168
169         return 0;
170 }
171
172 int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock )
173 {
174         struct ldap_int_thread_rdwr_s *rw;
175
176         assert( rwlock != NULL );
177         rw = *rwlock;
178
179         assert( rw != NULL );
180         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
181
182         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
183                 return LDAP_PVT_THREAD_EINVAL;
184
185         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
186
187         assert( rw->ltrw_w_active >= 0 ); 
188         assert( rw->ltrw_w_wait >= 0 ); 
189         assert( rw->ltrw_r_active >= 0 ); 
190         assert( rw->ltrw_r_wait >= 0 ); 
191
192         if( rw->ltrw_w_active > 0) {
193                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
194                 return LDAP_PVT_THREAD_EBUSY;
195         }
196
197         rw->ltrw_r_active++;
198
199         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
200
201         return 0;
202 }
203
204 int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock )
205 {
206         struct ldap_int_thread_rdwr_s *rw;
207
208         assert( rwlock != NULL );
209         rw = *rwlock;
210
211         assert( rw != NULL );
212         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
213
214         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
215                 return LDAP_PVT_THREAD_EINVAL;
216
217         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
218
219         rw->ltrw_r_active--;
220
221         assert( rw->ltrw_w_active >= 0 ); 
222         assert( rw->ltrw_w_wait >= 0 ); 
223         assert( rw->ltrw_r_active >= 0 ); 
224         assert( rw->ltrw_r_wait >= 0 ); 
225
226         if (rw->ltrw_r_active == 0 && rw->ltrw_w_wait > 0 ) {
227                 ldap_pvt_thread_cond_signal( &rw->ltrw_write );
228         }
229
230         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
231
232         return 0;
233 }
234
235 int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock )
236 {
237         struct ldap_int_thread_rdwr_s *rw;
238
239         assert( rwlock != NULL );
240         rw = *rwlock;
241
242         assert( rw != NULL );
243         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
244
245         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
246                 return LDAP_PVT_THREAD_EINVAL;
247
248         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
249
250         assert( rw->ltrw_w_active >= 0 ); 
251         assert( rw->ltrw_w_wait >= 0 ); 
252         assert( rw->ltrw_r_active >= 0 ); 
253         assert( rw->ltrw_r_wait >= 0 ); 
254
255         if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
256                 rw->ltrw_w_wait++;
257
258                 do {
259                         ldap_pvt_thread_cond_wait(
260                                 &rw->ltrw_write, &rw->ltrw_mutex );
261                 } while ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 );
262
263                 rw->ltrw_w_wait--;
264                 assert( rw->ltrw_w_wait >= 0 ); 
265         }
266
267         rw->ltrw_w_active++;
268
269         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
270
271         return 0;
272 }
273
274 int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock )
275 {
276         struct ldap_int_thread_rdwr_s *rw;
277
278         assert( rwlock != NULL );
279         rw = *rwlock;
280
281         assert( rw != NULL );
282         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
283
284         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
285                 return LDAP_PVT_THREAD_EINVAL;
286
287         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
288
289         assert( rw->ltrw_w_active >= 0 ); 
290         assert( rw->ltrw_w_wait >= 0 ); 
291         assert( rw->ltrw_r_active >= 0 ); 
292         assert( rw->ltrw_r_wait >= 0 ); 
293
294         if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
295                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
296                 return LDAP_PVT_THREAD_EBUSY;
297         }
298
299         rw->ltrw_w_active++;
300
301         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
302
303         return 0;
304 }
305
306 int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
307 {
308         struct ldap_int_thread_rdwr_s *rw;
309
310         assert( rwlock != NULL );
311         rw = *rwlock;
312
313         assert( rw != NULL );
314         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
315
316         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
317                 return LDAP_PVT_THREAD_EINVAL;
318
319         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
320
321         rw->ltrw_w_active--;
322
323         assert( rw->ltrw_w_active >= 0 ); 
324         assert( rw->ltrw_w_wait >= 0 ); 
325         assert( rw->ltrw_r_active >= 0 ); 
326         assert( rw->ltrw_r_wait >= 0 ); 
327
328         if (rw->ltrw_r_wait > 0) {
329                 ldap_pvt_thread_cond_broadcast( &rw->ltrw_read );
330
331         } else if (rw->ltrw_w_wait > 0) {
332                 ldap_pvt_thread_cond_signal( &rw->ltrw_write );
333         }
334
335         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
336
337         return 0;
338 }
339
340 #ifdef LDAP_RDWR_DEBUG
341
342 /* just for testing, 
343  * return 0 if false, suitable for assert(ldap_pvt_thread_rdwr_Xchk(rdwr))
344  * 
345  * Currently they don't check if the calling thread is the one 
346  * that has the lock, just that there is a reader or writer.
347  *
348  * Basically sufficent for testing that places that should have
349  * a lock are caught.
350  */
351
352 int ldap_pvt_thread_rdwr_readers(ldap_pvt_thread_rdwr_t *rwlock)
353 {
354         struct ldap_int_thread_rdwr_s *rw;
355
356         assert( rwlock != NULL );
357         rw = *rwlock;
358
359         assert( rw != NULL );
360         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
361         assert( rw->ltrw_w_active >= 0 ); 
362         assert( rw->ltrw_w_wait >= 0 ); 
363         assert( rw->ltrw_r_active >= 0 ); 
364         assert( rw->ltrw_r_wait >= 0 ); 
365
366         return( rw->ltrw_r_active );
367 }
368
369 int ldap_pvt_thread_rdwr_writers(ldap_pvt_thread_rdwr_t *rwlock)
370 {
371         struct ldap_int_thread_rdwr_s *rw;
372
373         assert( rwlock != NULL );
374         rw = *rwlock;
375
376         assert( rw != NULL );
377         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
378         assert( rw->ltrw_w_active >= 0 ); 
379         assert( rw->ltrw_w_wait >= 0 ); 
380         assert( rw->ltrw_r_active >= 0 ); 
381         assert( rw->ltrw_r_wait >= 0 ); 
382
383         return( rw->ltrw_w_active );
384 }
385
386 int ldap_pvt_thread_rdwr_active(ldap_pvt_thread_rdwr_t *rwlock)
387 {
388         struct ldap_int_thread_rdwr_s *rw;
389
390         assert( rwlock != NULL );
391         rw = *rwlock;
392
393         assert( rw != NULL );
394         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
395         assert( rw->ltrw_w_active >= 0 ); 
396         assert( rw->ltrw_w_wait >= 0 ); 
397         assert( rw->ltrw_r_active >= 0 ); 
398         assert( rw->ltrw_r_wait >= 0 ); 
399
400         return(ldap_pvt_thread_rdwr_readers(rw) +
401                ldap_pvt_thread_rdwr_writers(rw));
402 }
403
404 #endif /* LDAP_DEBUG */
405
406 #endif /* LDAP_THREAD_HAVE_RDWR */