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