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