]> git.sur5r.net Git - openldap/blob - libraries/libldap_r/rdwr.c
b0613c07514a0f1bca8442007bdd3fe9d40790e8
[openldap] / libraries / libldap_r / rdwr.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2004 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 #ifdef LDAP_RDWR_DEBUG
63         /* keep track of who has these locks */
64 #define MAX_READERS     32
65         ldap_pvt_thread_t ltrw_readers[MAX_READERS];
66         ldap_pvt_thread_t ltrw_writer;
67 #endif
68 };
69
70 int 
71 ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
72 {
73         struct ldap_int_thread_rdwr_s *rw;
74
75         assert( rwlock != NULL );
76
77         rw = (struct ldap_int_thread_rdwr_s *) LDAP_CALLOC( 1,
78                 sizeof( struct ldap_int_thread_rdwr_s ) );
79
80         /* we should check return results */
81         ldap_pvt_thread_mutex_init( &rw->ltrw_mutex );
82         ldap_pvt_thread_cond_init( &rw->ltrw_read );
83         ldap_pvt_thread_cond_init( &rw->ltrw_write );
84
85         rw->ltrw_valid = LDAP_PVT_THREAD_RDWR_VALID;
86
87         *rwlock = rw;
88         return 0;
89 }
90
91 int 
92 ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
93 {
94         struct ldap_int_thread_rdwr_s *rw;
95
96         assert( rwlock != NULL );
97         rw = *rwlock;
98
99         assert( rw != NULL );
100         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
101
102         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
103                 return LDAP_PVT_THREAD_EINVAL;
104
105         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
106
107         assert( rw->ltrw_w_active >= 0 ); 
108         assert( rw->ltrw_w_wait >= 0 ); 
109         assert( rw->ltrw_r_active >= 0 ); 
110         assert( rw->ltrw_r_wait >= 0 ); 
111
112         /* active threads? */
113         if( rw->ltrw_r_active > 0 || rw->ltrw_w_active > 0) {
114                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
115                 return LDAP_PVT_THREAD_EBUSY;
116         }
117
118         /* waiting threads? */
119         if( rw->ltrw_r_wait > 0 || rw->ltrw_w_wait > 0) {
120                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
121                 return LDAP_PVT_THREAD_EBUSY;
122         }
123
124         rw->ltrw_valid = 0;
125
126         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
127
128         ldap_pvt_thread_mutex_destroy( &rw->ltrw_mutex );
129         ldap_pvt_thread_cond_destroy( &rw->ltrw_read );
130         ldap_pvt_thread_cond_destroy( &rw->ltrw_write );
131
132         LDAP_FREE(rw);
133         *rwlock = NULL;
134         return 0;
135 }
136
137 int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock )
138 {
139         struct ldap_int_thread_rdwr_s *rw;
140
141         assert( rwlock != NULL );
142         rw = *rwlock;
143
144         assert( rw != NULL );
145         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
146
147         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
148                 return LDAP_PVT_THREAD_EINVAL;
149
150         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
151
152         assert( rw->ltrw_w_active >= 0 ); 
153         assert( rw->ltrw_w_wait >= 0 ); 
154         assert( rw->ltrw_r_active >= 0 ); 
155         assert( rw->ltrw_r_wait >= 0 ); 
156
157         if( rw->ltrw_w_active > 0 ) {
158                 /* writer is active */
159
160                 rw->ltrw_r_wait++;
161
162                 do {
163                         ldap_pvt_thread_cond_wait(
164                                 &rw->ltrw_read, &rw->ltrw_mutex );
165                 } while( rw->ltrw_w_active > 0 );
166
167                 rw->ltrw_r_wait--;
168                 assert( rw->ltrw_r_wait >= 0 ); 
169         }
170
171 #ifdef LDAP_RDWR_DEBUG
172         rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self();
173 #endif
174         rw->ltrw_r_active++;
175
176
177         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
178
179         return 0;
180 }
181
182 int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock )
183 {
184         struct ldap_int_thread_rdwr_s *rw;
185
186         assert( rwlock != NULL );
187         rw = *rwlock;
188
189         assert( rw != NULL );
190         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
191
192         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
193                 return LDAP_PVT_THREAD_EINVAL;
194
195         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
196
197         assert( rw->ltrw_w_active >= 0 ); 
198         assert( rw->ltrw_w_wait >= 0 ); 
199         assert( rw->ltrw_r_active >= 0 ); 
200         assert( rw->ltrw_r_wait >= 0 ); 
201
202         if( rw->ltrw_w_active > 0) {
203                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
204                 return LDAP_PVT_THREAD_EBUSY;
205         }
206
207 #ifdef LDAP_RDWR_DEBUG
208         rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self();
209 #endif
210         rw->ltrw_r_active++;
211
212         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
213
214         return 0;
215 }
216
217 int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock )
218 {
219         struct ldap_int_thread_rdwr_s *rw;
220
221         assert( rwlock != NULL );
222         rw = *rwlock;
223
224         assert( rw != NULL );
225         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
226
227         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
228                 return LDAP_PVT_THREAD_EINVAL;
229
230         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
231
232 #ifdef LDAP_RDWR_DEBUG
233         /* Remove us from the list of readers */
234         { int i, j;
235         ldap_pvt_thread_t self = ldap_pvt_thread_self();
236
237         for (i=0; i<rw->ltrw_r_active;i++)
238         {
239                 if (rw->ltrw_readers[i] == self) {
240                         for (j=i; j<rw->ltrw_r_active-1; j++)
241                                 rw->ltrw_readers[j] = rw->ltrw_readers[j+1];
242                         rw->ltrw_readers[j] = 0;
243                         break;
244                 }
245         }
246         }
247 #endif
248         rw->ltrw_r_active--;
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_r_active == 0 && rw->ltrw_w_wait > 0 ) {
256                 ldap_pvt_thread_cond_signal( &rw->ltrw_write );
257         }
258
259         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
260
261         return 0;
262 }
263
264 int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock )
265 {
266         struct ldap_int_thread_rdwr_s *rw;
267
268         assert( rwlock != NULL );
269         rw = *rwlock;
270
271         assert( rw != NULL );
272         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
273
274         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
275                 return LDAP_PVT_THREAD_EINVAL;
276
277         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
278
279         assert( rw->ltrw_w_active >= 0 ); 
280         assert( rw->ltrw_w_wait >= 0 ); 
281         assert( rw->ltrw_r_active >= 0 ); 
282         assert( rw->ltrw_r_wait >= 0 ); 
283
284         if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
285                 rw->ltrw_w_wait++;
286
287                 do {
288                         ldap_pvt_thread_cond_wait(
289                                 &rw->ltrw_write, &rw->ltrw_mutex );
290                 } while ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 );
291
292                 rw->ltrw_w_wait--;
293                 assert( rw->ltrw_w_wait >= 0 ); 
294         }
295
296 #ifdef LDAP_RDWR_DEBUG
297         rw->ltrw_writer = ldap_pvt_thread_self();
298 #endif
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_wtrylock( 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         assert( rw->ltrw_w_active >= 0 ); 
322         assert( rw->ltrw_w_wait >= 0 ); 
323         assert( rw->ltrw_r_active >= 0 ); 
324         assert( rw->ltrw_r_wait >= 0 ); 
325
326         if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
327                 ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
328                 return LDAP_PVT_THREAD_EBUSY;
329         }
330
331 #ifdef LDAP_RDWR_DEBUG
332         rw->ltrw_writer = ldap_pvt_thread_self();
333 #endif
334         rw->ltrw_w_active++;
335
336         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
337
338         return 0;
339 }
340
341 int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
342 {
343         struct ldap_int_thread_rdwr_s *rw;
344
345         assert( rwlock != NULL );
346         rw = *rwlock;
347
348         assert( rw != NULL );
349         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
350
351         if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
352                 return LDAP_PVT_THREAD_EINVAL;
353
354         ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
355
356         rw->ltrw_w_active--;
357
358         assert( rw->ltrw_w_active >= 0 ); 
359         assert( rw->ltrw_w_wait >= 0 ); 
360         assert( rw->ltrw_r_active >= 0 ); 
361         assert( rw->ltrw_r_wait >= 0 ); 
362
363         if (rw->ltrw_r_wait > 0) {
364                 ldap_pvt_thread_cond_broadcast( &rw->ltrw_read );
365
366         } else if (rw->ltrw_w_wait > 0) {
367                 ldap_pvt_thread_cond_signal( &rw->ltrw_write );
368         }
369
370 #ifdef LDAP_RDWR_DEBUG
371         rw->ltrw_writer = 0;
372 #endif
373         ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
374
375         return 0;
376 }
377
378 #ifdef LDAP_RDWR_DEBUG
379
380 /* just for testing, 
381  * return 0 if false, suitable for assert(ldap_pvt_thread_rdwr_Xchk(rdwr))
382  * 
383  * Currently they don't check if the calling thread is the one 
384  * that has the lock, just that there is a reader or writer.
385  *
386  * Basically sufficent for testing that places that should have
387  * a lock are caught.
388  */
389
390 int ldap_pvt_thread_rdwr_readers(ldap_pvt_thread_rdwr_t *rwlock)
391 {
392         struct ldap_int_thread_rdwr_s *rw;
393
394         assert( rwlock != NULL );
395         rw = *rwlock;
396
397         assert( rw != NULL );
398         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
399         assert( rw->ltrw_w_active >= 0 ); 
400         assert( rw->ltrw_w_wait >= 0 ); 
401         assert( rw->ltrw_r_active >= 0 ); 
402         assert( rw->ltrw_r_wait >= 0 ); 
403
404         return( rw->ltrw_r_active );
405 }
406
407 int ldap_pvt_thread_rdwr_writers(ldap_pvt_thread_rdwr_t *rwlock)
408 {
409         struct ldap_int_thread_rdwr_s *rw;
410
411         assert( rwlock != NULL );
412         rw = *rwlock;
413
414         assert( rw != NULL );
415         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
416         assert( rw->ltrw_w_active >= 0 ); 
417         assert( rw->ltrw_w_wait >= 0 ); 
418         assert( rw->ltrw_r_active >= 0 ); 
419         assert( rw->ltrw_r_wait >= 0 ); 
420
421         return( rw->ltrw_w_active );
422 }
423
424 int ldap_pvt_thread_rdwr_active(ldap_pvt_thread_rdwr_t *rwlock)
425 {
426         struct ldap_int_thread_rdwr_s *rw;
427
428         assert( rwlock != NULL );
429         rw = *rwlock;
430
431         assert( rw != NULL );
432         assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
433         assert( rw->ltrw_w_active >= 0 ); 
434         assert( rw->ltrw_w_wait >= 0 ); 
435         assert( rw->ltrw_r_active >= 0 ); 
436         assert( rw->ltrw_r_wait >= 0 ); 
437
438         return(ldap_pvt_thread_rdwr_readers(rwlock) +
439                ldap_pvt_thread_rdwr_writers(rwlock));
440 }
441
442 #endif /* LDAP_DEBUG */
443
444 #endif /* LDAP_THREAD_HAVE_RDWR */