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