2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
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
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.
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
23 Bacula® is a registered trademark of John Walker.
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.
29 * Collection of Bacula Storage daemon locking software
31 * Kern Sibbald, 2000-2007. June 2007
36 #include "bacula.h" /* pull in global headers */
37 #include "stored.h" /* pull in Storage Deamon headers */
42 const int dbglvl = 500;
46 void DEVICE::block(int why)
48 r_dlock(); /* need recursive lock to block */
49 block_device(this, why);
53 void DEVICE::unblock(bool locked)
64 void DEVICE::_dlock(const char *file, int line)
66 Dmsg4(sd_dbglvl, "dlock from %s:%d precnt=%d JobId=%u\n", file, line,
67 m_count, get_jobid_from_tid());
68 /* Note, this *really* should be protected by a mutex, but
69 * since it is only debug code we don't worry too much.
71 if (m_count > 0 && pthread_equal(m_pid, pthread_self())) {
72 Dmsg5(sd_dbglvl, "Possible DEADLOCK!! lock held by JobId=%u from %s:%d m_count=%d JobId=%u\n",
73 get_jobid_from_tid(m_pid),
74 file, line, m_count, get_jobid_from_tid());
77 m_pid = pthread_self();
81 void DEVICE::_dunlock(const char *file, int line)
84 Dmsg4(sd_dbglvl+1, "dunlock from %s:%d postcnt=%d JobId=%u\n", file, line,
85 m_count, get_jobid_from_tid());
89 void DEVICE::_r_dunlock(const char *file, int line)
91 this->_dunlock(file, line);
98 * This is a recursive lock that checks if the device is blocked.
100 * When blocked is set, all threads EXCEPT thread with id no_wait_id
101 * must wait. The no_wait_id thread is out obtaining a new volume
102 * and preparing the label.
105 void DEVICE::_r_dlock(const char *file, int line)
107 void DEVICE::r_dlock()
112 Dmsg4(sd_dbglvl+1, "r_dlock blked=%s from %s:%d JobId=%u\n", this->print_blocked(),
113 file, line, get_jobid_from_tid());
115 Dmsg1(sd_dbglvl, "reclock blked=%s\n", this->print_blocked());
118 if (this->blocked() && !pthread_equal(this->no_wait_id, pthread_self())) {
119 this->num_waiting++; /* indicate that I am waiting */
120 while (this->blocked()) {
121 Dmsg3(sd_dbglvl, "r_dlock blked=%s no_wait=%p me=%p\n", this->print_blocked(),
122 this->no_wait_id, pthread_self());
123 if ((stat = pthread_cond_wait(&this->wait, &m_mutex)) != 0) {
126 Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"),
130 this->num_waiting--; /* no longer waiting */
135 * Block all other threads from using the device
136 * Device must already be locked. After this call,
137 * the device is blocked to any thread calling dev->r_lock(),
138 * but the device is not locked (i.e. no P on device). Also,
139 * the current thread can do slip through the dev->r_lock()
140 * calls without blocking.
142 void _block_device(const char *file, int line, DEVICE *dev, int state)
144 ASSERT(dev->blocked() == BST_NOT_BLOCKED);
145 dev->set_blocked(state); /* make other threads wait */
146 dev->no_wait_id = pthread_self(); /* allow us to continue */
147 Dmsg3(sd_dbglvl, "set blocked=%s from %s:%d\n", dev->print_blocked(), file, line);
151 * Unblock the device, and wake up anyone who went to sleep.
152 * Enter: device locked
153 * Exit: device locked
155 void _unblock_device(const char *file, int line, DEVICE *dev)
157 Dmsg3(sd_dbglvl, "unblock %s from %s:%d\n", dev->print_blocked(), file, line);
158 ASSERT(dev->blocked());
159 dev->set_blocked(BST_NOT_BLOCKED);
161 if (dev->num_waiting > 0) {
162 pthread_cond_broadcast(&dev->wait); /* wake them up */
167 * Enter with device locked and blocked
168 * Exit with device unlocked and blocked by us.
170 void _steal_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state)
173 Dmsg3(sd_dbglvl, "steal lock. old=%s from %s:%d\n", dev->print_blocked(),
175 hold->dev_blocked = dev->blocked();
176 hold->dev_prev_blocked = dev->dev_prev_blocked;
177 hold->no_wait_id = dev->no_wait_id;
178 dev->set_blocked(state);
179 Dmsg1(sd_dbglvl, "steal lock. new=%s\n", dev->print_blocked());
180 dev->no_wait_id = pthread_self();
185 * Enter with device blocked by us but not locked
186 * Exit with device locked, and blocked by previous owner
188 void _give_back_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold)
190 Dmsg3(sd_dbglvl, "return lock. old=%s from %s:%d\n",
191 dev->print_blocked(), file, line);
193 dev->set_blocked(hold->dev_blocked);
194 dev->dev_prev_blocked = hold->dev_prev_blocked;
195 dev->no_wait_id = hold->no_wait_id;
196 Dmsg1(sd_dbglvl, "return lock. new=%s\n", dev->print_blocked());
197 if (dev->num_waiting > 0) {
198 pthread_cond_broadcast(&dev->wait); /* wake them up */
202 const char *DEVICE::print_blocked() const
205 case BST_NOT_BLOCKED:
206 return "BST_NOT_BLOCKED";
208 return "BST_UNMOUNTED";
209 case BST_WAITING_FOR_SYSOP:
210 return "BST_WAITING_FOR_SYSOP";
211 case BST_DOING_ACQUIRE:
212 return "BST_DOING_ACQUIRE";
213 case BST_WRITING_LABEL:
214 return "BST_WRITING_LABEL";
215 case BST_UNMOUNTED_WAITING_FOR_SYSOP:
216 return "BST_UNMOUNTED_WAITING_FOR_SYSOP";
220 return _("unknown blocked code");
226 * Check if the device is blocked or not
228 bool is_device_unmounted(DEVICE *dev)
231 int blocked = dev->blocked();
232 stat = (blocked == BST_UNMOUNTED) ||
233 (blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);