1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2001-2011 Free Software Foundation Europe e.V.
5 
6    This program is Free Software; you can redistribute it and/or
7    modify it under the terms of version three of the GNU Affero General Public
8    License as published by the Free Software Foundation and included
9    in the file LICENSE.
10 
11    This program is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14    Affero General Public License for more details.
15 
16    You should have received a copy of the GNU Affero General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19    02110-1301, USA.
20 */
21 /*
22  * BAREOS Thread Read/Write locking code. It permits
23  * multiple readers but only one writer.  Note, however,
24  * that the writer thread is permitted to make multiple
25  * nested write lock calls.
26  *
27  * Kern Sibbald, January MMI
28  *
29  * This code adapted from "Programming with POSIX Threads", by
30  * David R. Butenhof
31  */
32 
33 #include "include/bareos.h"
34 #include "lib/devlock.h"
35 
36 /*
37  * Initialize a read/write lock
38  *
39  *  Returns: 0 on success
40  *           errno on failure
41  */
42 
new_devlock()43 DevLock* DevLock::new_devlock()
44 {
45   DevLock* lock;
46   lock = (DevLock*)malloc(sizeof(DevLock));
47   lock->mutex = PTHREAD_MUTEX_INITIALIZER;
48   lock->read = PTHREAD_COND_INITIALIZER;
49   lock->write = PTHREAD_COND_INITIALIZER;
50   lock->writer_id = 0;
51   lock->priority = 0;
52   lock->valid = 0;
53   lock->r_active = 0;
54   lock->w_active = 0;
55   lock->r_wait = 0;
56   lock->w_wait = 0;
57   lock->reason = 0;
58   lock->prev_reason = 0;
59   lock->can_take = false;
60   return lock;
61 }
62 
init(int initial_priority)63 int DevLock::init(int initial_priority)
64 {
65   int status;
66   DevLock* rwl = this;
67 
68   rwl->r_active = rwl->w_active = 0;
69   rwl->r_wait = rwl->w_wait = 0;
70   rwl->priority = initial_priority;
71   if ((status = pthread_mutex_init(&rwl->mutex, NULL)) != 0) { return status; }
72   if ((status = pthread_cond_init(&rwl->read, NULL)) != 0) {
73     pthread_mutex_destroy(&rwl->mutex);
74     return status;
75   }
76   if ((status = pthread_cond_init(&rwl->write, NULL)) != 0) {
77     pthread_cond_destroy(&rwl->read);
78     pthread_mutex_destroy(&rwl->mutex);
79     return status;
80   }
81   rwl->valid = DEVLOCK_VALID;
82   return 0;
83 }
84 
85 /*
86  * Destroy a read/write lock
87  *
88  * Returns: 0 on success
89  *          errno on failure
90  */
destroy()91 int DevLock::destroy()
92 {
93   DevLock* rwl = this;
94   int status, status1, status2;
95 
96   if (rwl->valid != DEVLOCK_VALID) { return EINVAL; }
97   if ((status = pthread_mutex_lock(&rwl->mutex)) != 0) { return status; }
98 
99   /*
100    * If any threads are active, report EBUSY
101    */
102   if (rwl->r_active > 0 || rwl->w_active) {
103     pthread_mutex_unlock(&rwl->mutex);
104     return EBUSY;
105   }
106 
107   /*
108    * If any threads are waiting, report EBUSY
109    */
110   if (rwl->r_wait > 0 || rwl->w_wait > 0) {
111     pthread_mutex_unlock(&rwl->mutex);
112     return EBUSY;
113   }
114 
115   rwl->valid = 0;
116   if ((status = pthread_mutex_unlock(&rwl->mutex)) != 0) { return status; }
117   status = pthread_mutex_destroy(&rwl->mutex);
118   status1 = pthread_cond_destroy(&rwl->read);
119   status2 = pthread_cond_destroy(&rwl->write);
120   return (status != 0 ? status : (status1 != 0 ? status1 : status2));
121 }
122 
123 /*
124  * Handle cleanup when the read lock condition variable
125  * wait is released.
126  */
DevlockReadRelease(void * arg)127 static void DevlockReadRelease(void* arg)
128 {
129   DevLock* rwl = (DevLock*)arg;
130   rwl->ReadRelease();
131 }
132 
ReadRelease()133 void DevLock::ReadRelease()
134 {
135   r_wait--;
136   pthread_mutex_unlock(&mutex);
137 }
138 
139 /*
140  * Handle cleanup when the write lock condition variable wait
141  * is released.
142  */
DevlockWriteRelease(void * arg)143 static void DevlockWriteRelease(void* arg)
144 {
145   DevLock* rwl = (DevLock*)arg;
146   rwl->WriteRelease();
147 }
148 
WriteRelease()149 void DevLock::WriteRelease()
150 {
151   w_wait--;
152   pthread_mutex_unlock(&mutex);
153 }
154 
155 /*
156  * Lock for read access, wait until locked (or error).
157  */
readlock()158 int DevLock::readlock()
159 {
160   DevLock* rwl = this;
161   int status;
162 
163   if (rwl->valid != DEVLOCK_VALID) { return EINVAL; }
164   if ((status = pthread_mutex_lock(&rwl->mutex)) != 0) { return status; }
165   if (rwl->w_active) {
166     rwl->r_wait++; /* indicate that we are waiting */
167     pthread_cleanup_push(DevlockReadRelease, (void*)rwl);
168     while (rwl->w_active) {
169       status = pthread_cond_wait(&rwl->read, &rwl->mutex);
170       if (status != 0) { break; /* error, bail out */ }
171     }
172     pthread_cleanup_pop(0);
173     rwl->r_wait--; /* we are no longer waiting */
174   }
175   if (status == 0) { rwl->r_active++; /* we are running */ }
176   pthread_mutex_unlock(&rwl->mutex);
177   return status;
178 }
179 
180 /*
181  * Attempt to lock for read access, don't wait
182  */
readtrylock()183 int DevLock::readtrylock()
184 {
185   DevLock* rwl = this;
186   int status, status2;
187 
188   if (rwl->valid != DEVLOCK_VALID) { return EINVAL; }
189   if ((status = pthread_mutex_lock(&rwl->mutex)) != 0) { return status; }
190   if (rwl->w_active) {
191     status = EBUSY;
192   } else {
193     rwl->r_active++; /* we are running */
194   }
195   status2 = pthread_mutex_unlock(&rwl->mutex);
196   return (status == 0 ? status2 : status);
197 }
198 
199 /*
200  * Unlock read lock
201  */
readunlock()202 int DevLock::readunlock()
203 {
204   DevLock* rwl = this;
205   int status, status2;
206 
207   if (rwl->valid != DEVLOCK_VALID) { return EINVAL; }
208   if ((status = pthread_mutex_lock(&rwl->mutex)) != 0) { return status; }
209   rwl->r_active--;
210   if (rwl->r_active == 0 && rwl->w_wait > 0) { /* if writers waiting */
211     status = pthread_cond_broadcast(&rwl->write);
212   }
213   status2 = pthread_mutex_unlock(&rwl->mutex);
214   return (status == 0 ? status2 : status);
215 }
216 
217 
218 /*
219  * Lock for write access, wait until locked (or error).
220  *   Multiple nested write locking is permitted.
221  */
Writelock(int areason,bool acan_take)222 int DevLock::Writelock(int areason, bool acan_take)
223 {
224   DevLock* rwl = this;
225   int status;
226 
227   if (rwl->valid != DEVLOCK_VALID) { return EINVAL; }
228   if ((status = pthread_mutex_lock(&rwl->mutex)) != 0) { return status; }
229   if (rwl->w_active && pthread_equal(rwl->writer_id, pthread_self())) {
230     rwl->w_active++;
231     pthread_mutex_unlock(&rwl->mutex);
232     return 0;
233   }
234   if (rwl->w_active || rwl->r_active > 0) {
235     rwl->w_wait++; /* indicate that we are waiting */
236     pthread_cleanup_push(DevlockWriteRelease, (void*)rwl);
237     while (rwl->w_active || rwl->r_active > 0) {
238       if ((status = pthread_cond_wait(&rwl->write, &rwl->mutex)) != 0) {
239         break; /* error, bail out */
240       }
241     }
242     pthread_cleanup_pop(0);
243     rwl->w_wait--; /* we are no longer waiting */
244   }
245   if (status == 0) {
246     rwl->w_active++;                 /* we are running */
247     rwl->writer_id = pthread_self(); /* save writer thread's id */
248   }
249   rwl->reason = areason;
250   rwl->can_take = acan_take;
251   pthread_mutex_unlock(&rwl->mutex);
252   return status;
253 }
254 
255 /*
256  * Attempt to lock for write access, don't wait
257  */
writetrylock()258 int DevLock::writetrylock()
259 {
260   DevLock* rwl = this;
261   int status, status2;
262 
263   if (rwl->valid != DEVLOCK_VALID) { return EINVAL; }
264   if ((status = pthread_mutex_lock(&rwl->mutex)) != 0) { return status; }
265   if (rwl->w_active && pthread_equal(rwl->writer_id, pthread_self())) {
266     rwl->w_active++;
267     pthread_mutex_unlock(&rwl->mutex);
268     return 0;
269   }
270   if (rwl->w_active || rwl->r_active > 0) {
271     status = EBUSY;
272   } else {
273     rwl->w_active = 1;               /* we are running */
274     rwl->writer_id = pthread_self(); /* save writer thread's id */
275   }
276   status2 = pthread_mutex_unlock(&rwl->mutex);
277   return (status == 0 ? status2 : status);
278 }
279 
280 /*
281  * Unlock write lock
282  *  Start any waiting writers in preference to waiting readers
283  */
writeunlock()284 int DevLock::writeunlock()
285 {
286   DevLock* rwl = this;
287   int status, status2;
288 
289   if (rwl->valid != DEVLOCK_VALID) { return EINVAL; }
290   if ((status = pthread_mutex_lock(&rwl->mutex)) != 0) { return status; }
291   if (rwl->w_active <= 0) {
292     pthread_mutex_unlock(&rwl->mutex);
293     Jmsg0(NULL, M_ABORT, 0, _("writeunlock called too many times.\n"));
294   }
295   rwl->w_active--;
296   if (!pthread_equal(pthread_self(), rwl->writer_id)) {
297     pthread_mutex_unlock(&rwl->mutex);
298     Jmsg0(NULL, M_ABORT, 0, _("writeunlock by non-owner.\n"));
299   }
300   if (rwl->w_active > 0) {
301     status = 0; /* writers still active */
302   } else {
303     /* No more writers, awaken someone */
304     if (rwl->r_wait > 0) { /* if readers waiting */
305       status = pthread_cond_broadcast(&rwl->read);
306     } else if (rwl->w_wait > 0) {
307       status = pthread_cond_broadcast(&rwl->write);
308     }
309   }
310   status2 = pthread_mutex_unlock(&rwl->mutex);
311   return (status == 0 ? status2 : status);
312 }
313 
TakeLock(take_lock_t * hold,int areason)314 int DevLock::TakeLock(take_lock_t* hold, int areason)
315 {
316   int status;
317 
318   if (valid != DEVLOCK_VALID) { return EINVAL; }
319   if ((status = pthread_mutex_lock(&mutex)) != 0) { return status; }
320   hold->reason = reason;
321   hold->prev_reason = prev_reason;
322   hold->writer_id = writer_id;
323   reason = areason;
324   writer_id = pthread_self();
325   status = pthread_mutex_unlock(&mutex);
326   return status;
327 }
328 
ReturnLock(take_lock_t * hold)329 int DevLock::ReturnLock(take_lock_t* hold)
330 {
331   int status, status2;
332 
333   if (valid != DEVLOCK_VALID) { return EINVAL; }
334   if ((status = pthread_mutex_lock(&mutex)) != 0) { return status; }
335   reason = hold->reason;
336   prev_reason = hold->prev_reason;
337   writer_id = hold->writer_id;
338   writer_id = pthread_self();
339   status2 = pthread_mutex_unlock(&mutex);
340   if (w_active || w_wait) { status = pthread_cond_broadcast(&write); }
341   return (status == 0 ? status2 : status);
342 }
343