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