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