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