]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/lock.c
Merge branch 'master' into basejobv3
[bacula/bacula] / bacula / src / stored / lock.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Collection of Bacula Storage daemon locking software
30  *
31  *  Kern Sibbald, 2000-2007.  June 2007
32  *
33  *   Version $Id$
34  */
35
36 #include "bacula.h"                   /* pull in global headers */
37 #include "stored.h"                   /* pull in Storage Deamon headers */
38
39 #ifdef SD_DEBUG_LOCK
40 const int dbglvl = 0;
41 #else
42 const int dbglvl = 500;
43 #endif
44
45
46 /*
47  *
48  * The Storage daemon has three locking concepts that must be
49  *   understood:
50  *
51  *  1. dblock    blocking the device, which means that the device
52  *               is "marked" in use.  When setting and removing the
53                  block, the device is locked, but after dblock is
54                  called the device is unlocked.
55  *  2. dlock()   simple mutex that locks the device structure. A dlock
56  *               can be acquired while a device is blocked if it is not
57  *               locked.      
58  *  3. r_dlock   "recursive" dlock, when means that a dlock (mutex)
59  *               will be acquired on the device if it is not blocked
60  *               by some other thread. If the device was blocked by
61  *               the current thread, it will acquire the lock.
62  *               If some other thread has set a block on the device,
63  *               this call will wait until the device is unblocked.
64  *
65  *  A lock is normally set when modifying the device structure.
66  *  A r_lock is normally acquired when you want to block the device
67  *    i.e. it will wait until the device is not blocked.
68  *  A block is normally set during long operations like writing to
69  *    the device.
70  *  If you are writing the device, you will normally block and 
71  *    lock it.
72  *  A lock cannot be violated. No other thread can touch the
73  *    device while a lock is set.  
74  *  When a block is set, every thread accept the thread that set
75  *    the block will block if r_dlock is called.
76  *  A device can be blocked for multiple reasons, labeling, writing,
77  *    acquiring (opening) the device, waiting for the operator, unmounted,
78  *    ...
79  *  Under certain conditions the block that is set on a device can be    
80  *    stolen and the device can be used by another thread. For example,
81  *    a device is blocked because it is waiting for the operator to  
82  *    mount a tape.  The operator can then unmount the device, and label
83  *    a tape, re-mount it, give back the block, and the job will continue.
84  *
85  *
86  * Functions:
87  *
88  *   DEVICE::dlock()   does P(m_mutex)     (in dev.h)
89  *   DEVICE::dunlock() does V(m_mutex)
90  *
91  *   DEVICE::r_dlock() allows locking the device when this thread
92                          already has the device blocked.
93  *                    dlock()
94  *                    if blocked and not same thread that locked
95  *                       pthread_cond_wait
96  *                    leaves device locked 
97  *
98  *   DEVICE::r_dunlock() unlocks but does not unblock
99  *                    same as dunlock();
100  *
101  *   DEVICE::dblock(why)  does 
102  *                    r_dlock();         (recursive device lock)
103  *                    block_device(this, why) 
104  *                    r_dunlock()
105  *
106  *   DEVICE::dunblock does
107  *                    dlock()
108  *                    unblock_device()
109  *                    dunlock()
110  *
111  *   block_device() does  (must be locked and not blocked at entry)  
112  *                    set blocked status
113  *                    set our pid
114  *
115  *   unblock_device() does (must be blocked at entry)
116  *                        (locked on entry)
117  *                        (locked on exit)
118  *                    set unblocked status
119  *                    clear pid
120  *                    if waiting threads
121  *                       pthread_cond_broadcast
122  *
123  *   steal_device_lock() does (must be locked and blocked at entry)
124  *                    save status
125  *                    set new blocked status
126  *                    set new pid
127  *                    unlock()
128  *
129  *   give_back_device_lock() does (must be blocked but not locked)
130  *                    dlock()
131  *                    reset blocked status
132  *                    save previous blocked
133  *                    reset pid
134  *                    if waiting threads
135  *                       pthread_cond_broadcast
136  *
137  */
138
139
140 void DEVICE::dblock(int why)
141 {
142    r_dlock();              /* need recursive lock to block */
143    block_device(this, why);
144    r_dunlock();
145 }
146
147 void DEVICE::dunblock(bool locked)
148 {
149    if (!locked) {
150       dlock();
151    }
152    unblock_device(this);
153    dunlock();
154 }
155
156
157 #ifdef SD_DEBUG_LOCK
158 void DCR::_dlock(const char *file, int line)
159 {
160    dev->_dlock(file, line);
161    m_dev_locked = true;
162 }
163 void DCR::_dunlock(const char *file, int line)
164 {
165    m_dev_locked = false;
166    dev->_dunlock(file, line);
167
168 }
169
170 void DEVICE::_dlock(const char *file, int line)
171 {
172    Dmsg3(sd_dbglvl, "dlock from %s:%d precnt=%d\n", file, line, m_count); 
173    /* Note, this *really* should be protected by a mutex, but
174     *  since it is only debug code we don't worry too much.  
175     */
176    if (m_count > 0 && pthread_equal(m_pid, pthread_self())) {
177       Dmsg4(sd_dbglvl, "Possible DEADLOCK!! lock held by JobId=%u from %s:%d m_count=%d\n", 
178             get_jobid_from_tid(m_pid),
179             file, line, m_count);
180    }
181    P(m_mutex);
182    m_pid = pthread_self();
183    m_count++; 
184 }
185
186 void DEVICE::_dunlock(const char *file, int line)
187 {
188    m_count--; 
189    Dmsg3(sd_dbglvl+1, "dunlock from %s:%d postcnt=%d\n", file, line, m_count); 
190    V(m_mutex);   
191 }
192
193 void DEVICE::_r_dunlock(const char *file, int line)
194 {
195    this->_dunlock(file, line);
196 }
197
198 #endif
199
200
201 /*
202  * This is a recursive lock that checks if the device is blocked.
203  *
204  * When blocked is set, all threads EXCEPT thread with id no_wait_id
205  * must wait. The no_wait_id thread is out obtaining a new volume
206  * and preparing the label.
207  */
208 #ifdef SD_DEBUG_LOCK
209 void DEVICE::_r_dlock(const char *file, int line, bool locked)
210 {
211    Dmsg3(sd_dbglvl+1, "r_dlock blked=%s from %s:%d\n", this->print_blocked(),
212          file, line);
213 #else
214 void DEVICE::r_dlock(bool locked)
215 {
216 #endif
217    int stat;
218    if (!locked) {
219       P(m_mutex); /*    this->dlock();   */
220       m_count++;  /*    this->dlock() */
221    }
222    if (this->blocked() && !pthread_equal(this->no_wait_id, pthread_self())) {
223       this->num_waiting++;             /* indicate that I am waiting */
224       while (this->blocked()) {
225 #ifndef HAVE_WIN32
226          /* thread id on Win32 may be a struct */
227          Dmsg3(sd_dbglvl, "r_dlock blked=%s no_wait=%p me=%p\n", this->print_blocked(),
228                this->no_wait_id, pthread_self());
229 #endif
230          if ((stat = pthread_cond_wait(&this->wait, &m_mutex)) != 0) {
231             berrno be;
232             this->dunlock();
233             Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"),
234                be.bstrerror(stat));
235          }
236       }
237       this->num_waiting--;             /* no longer waiting */
238    }
239 }
240
241 /*
242  * Block all other threads from using the device
243  *  Device must already be locked.  After this call,
244  *  the device is blocked to any thread calling dev->r_lock(),
245  *  but the device is not locked (i.e. no P on device).  Also,
246  *  the current thread can do slip through the dev->r_lock()
247  *  calls without blocking.
248  */
249 void _block_device(const char *file, int line, DEVICE *dev, int state)
250 {
251    ASSERT(dev->blocked() == BST_NOT_BLOCKED);
252    dev->set_blocked(state);           /* make other threads wait */
253    dev->no_wait_id = pthread_self();  /* allow us to continue */
254    Dmsg3(sd_dbglvl, "set blocked=%s from %s:%d\n", dev->print_blocked(), file, line);
255 }
256
257 /*
258  * Unblock the device, and wake up anyone who went to sleep.
259  * Enter: device locked
260  * Exit:  device locked
261  */
262 void _unblock_device(const char *file, int line, DEVICE *dev)
263 {
264    Dmsg3(sd_dbglvl, "unblock %s from %s:%d\n", dev->print_blocked(), file, line);
265    ASSERT(dev->blocked());
266    dev->set_blocked(BST_NOT_BLOCKED);
267    clear_thread_id(dev->no_wait_id);
268    if (dev->num_waiting > 0) {
269       pthread_cond_broadcast(&dev->wait); /* wake them up */
270    }
271 }
272
273 /*
274  * Enter with device locked and blocked
275  * Exit with device unlocked and blocked by us.
276  */
277 void _steal_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state)
278 {
279
280    Dmsg3(sd_dbglvl, "steal lock. old=%s from %s:%d\n", dev->print_blocked(),
281       file, line);
282    hold->dev_blocked = dev->blocked();
283    hold->dev_prev_blocked = dev->dev_prev_blocked;
284    hold->no_wait_id = dev->no_wait_id;
285    dev->set_blocked(state);
286    Dmsg1(sd_dbglvl, "steal lock. new=%s\n", dev->print_blocked());
287    dev->no_wait_id = pthread_self();
288    dev->dunlock();
289 }
290
291 /*
292  * Enter with device blocked by us but not locked
293  * Exit with device locked, and blocked by previous owner
294  */
295 void _give_back_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold)
296 {
297    Dmsg3(sd_dbglvl, "return lock. old=%s from %s:%d\n",
298       dev->print_blocked(), file, line);
299    dev->dlock();
300    dev->set_blocked(hold->dev_blocked);
301    dev->dev_prev_blocked = hold->dev_prev_blocked;
302    dev->no_wait_id = hold->no_wait_id;
303    Dmsg1(sd_dbglvl, "return lock. new=%s\n", dev->print_blocked());
304    if (dev->num_waiting > 0) {
305       pthread_cond_broadcast(&dev->wait); /* wake them up */
306    }
307 }
308
309 const char *DEVICE::print_blocked() const 
310 {
311    switch (m_blocked) {
312    case BST_NOT_BLOCKED:
313       return "BST_NOT_BLOCKED";
314    case BST_UNMOUNTED:
315       return "BST_UNMOUNTED";
316    case BST_WAITING_FOR_SYSOP:
317       return "BST_WAITING_FOR_SYSOP";
318    case BST_DOING_ACQUIRE:
319       return "BST_DOING_ACQUIRE";
320    case BST_WRITING_LABEL:
321       return "BST_WRITING_LABEL";
322    case BST_UNMOUNTED_WAITING_FOR_SYSOP:
323       return "BST_UNMOUNTED_WAITING_FOR_SYSOP";
324    case BST_MOUNT:
325       return "BST_MOUNT";
326    case BST_DESPOOLING:
327       return "BST_DESPOOLING";
328    default:
329       return _("unknown blocked code");
330    }
331 }
332
333
334 /*
335  * Check if the device is blocked or not
336  */
337 bool DEVICE::is_device_unmounted()
338 {
339    bool stat;
340    int blk = blocked();
341    stat = (blk == BST_UNMOUNTED) ||
342           (blk == BST_UNMOUNTED_WAITING_FOR_SYSOP);
343    return stat;
344 }