/*
Bacula® - The Network Backup Solution
- Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
+ Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
The main author of Bacula is Kern Sibbald, with contributions from
many others, a complete list can be found in the file AUTHORS.
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
- Bacula® is a registered trademark of John Walker.
+ Bacula® is a registered trademark of Kern Sibbald.
The licensor of Bacula is the Free Software Foundation Europe
(FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
Switzerland, email:ftf@fsfeurope.org.
*
* Kern Sibbald, 2000-2007. June 2007
*
- * Version $Id$
*/
#include "bacula.h" /* pull in global headers */
#endif
-void DEVICE::block(int why)
+/*
+ *
+ * The Storage daemon has three locking concepts that must be
+ * understood:
+ *
+ * 1. dblock blocking the device, which means that the device
+ * is "marked" in use. When setting and removing the
+ block, the device is locked, but after dblock is
+ called the device is unlocked.
+ * 2. dlock() simple mutex that locks the device structure. A dlock
+ * can be acquired while a device is blocked if it is not
+ * locked.
+ * 3. r_dlock(locked) "recursive" dlock, when means that a dlock (mutex)
+ * will be acquired on the device if it is not blocked
+ * by some other thread. If the device was blocked by
+ * the current thread, it will acquire the lock.
+ * If some other thread has set a block on the device,
+ * this call will wait until the device is unblocked.
+ * Can be called with locked true, which means the
+ * dlock is already set
+ *
+ * A lock is normally set when modifying the device structure.
+ * A r_lock is normally acquired when you want to block the device
+ * i.e. it will wait until the device is not blocked.
+ * A block is normally set during long operations like writing to
+ * the device.
+ * If you are writing the device, you will normally block and
+ * lock it.
+ * A lock cannot be violated. No other thread can touch the
+ * device while a lock is set.
+ * When a block is set, every thread accept the thread that set
+ * the block will block if r_dlock is called.
+ * A device can be blocked for multiple reasons, labeling, writing,
+ * acquiring (opening) the device, waiting for the operator, unmounted,
+ * ...
+ * Under certain conditions the block that is set on a device can be
+ * stolen and the device can be used by another thread. For example,
+ * a device is blocked because it is waiting for the operator to
+ * mount a tape. The operator can then unmount the device, and label
+ * a tape, re-mount it, give back the block, and the job will continue.
+ *
+ *
+ * Functions:
+ *
+ * DEVICE::dlock() does P(m_mutex) (in dev.h)
+ * DEVICE::dunlock() does V(m_mutex)
+ *
+ * DEVICE::r_dlock(locked) allows locking the device when this thread
+ * already has the device blocked.
+ * if (!locked)
+ * dlock()
+ * if blocked and not same thread that locked
+ * pthread_cond_wait
+ * leaves device locked
+ *
+ * DEVICE::r_dunlock() unlocks but does not unblock
+ * same as dunlock();
+ *
+ * DEVICE::dblock(why) does
+ * r_dlock(); (recursive device lock)
+ * block_device(this, why)
+ * r_dunlock()
+ *
+ * DEVICE::dunblock does
+ * dlock()
+ * unblock_device()
+ * dunlock()
+ *
+ * block_device() does (must be locked and not blocked at entry)
+ * set blocked status
+ * set our pid
+ *
+ * unblock_device() does (must be blocked at entry)
+ * (locked on entry)
+ * (locked on exit)
+ * set unblocked status
+ * clear pid
+ * if waiting threads
+ * pthread_cond_broadcast
+ *
+ * steal_device_lock() does (must be locked and blocked at entry)
+ * save status
+ * set new blocked status
+ * set new pid
+ * unlock()
+ *
+ * give_back_device_lock() does (must be blocked but not locked)
+ * dlock()
+ * reset blocked status
+ * save previous blocked
+ * reset pid
+ * if waiting threads
+ * pthread_cond_broadcast
+ *
+ */
+
+
+void DEVICE::dblock(int why)
{
r_dlock(); /* need recursive lock to block */
block_device(this, why);
r_dunlock();
}
-void DEVICE::unblock(bool locked)
+void DEVICE::dunblock(bool locked)
{
if (!locked) {
dlock();
#ifdef SD_DEBUG_LOCK
+void DCR::_dlock(const char *file, int line)
+{
+ dev->_dlock(file, line);
+ m_dev_locked = true;
+}
+void DCR::_dunlock(const char *file, int line)
+{
+ m_dev_locked = false;
+ dev->_dunlock(file, line);
+
+}
+
void DEVICE::_dlock(const char *file, int line)
{
- Dmsg4(sd_dbglvl, "dlock from %s:%d precnt=%d JobId=%u\n", file, line,
- m_count, get_jobid_from_tid());
+ Dmsg3(sd_dbglvl, "dlock from %s:%d precnt=%d\n", file, line, m_count);
/* Note, this *really* should be protected by a mutex, but
* since it is only debug code we don't worry too much.
*/
if (m_count > 0 && pthread_equal(m_pid, pthread_self())) {
- Dmsg5(sd_dbglvl, "Possible DEADLOCK!! lock held by JobId=%u from %s:%d m_count=%d JobId=%u\n",
+ Dmsg4(sd_dbglvl, "Possible DEADLOCK!! lock held by JobId=%u from %s:%d m_count=%d\n",
get_jobid_from_tid(m_pid),
- file, line, m_count, get_jobid_from_tid());
+ file, line, m_count);
}
P(m_mutex);
m_pid = pthread_self();
void DEVICE::_dunlock(const char *file, int line)
{
m_count--;
- Dmsg4(sd_dbglvl+1, "dunlock from %s:%d postcnt=%d JobId=%u\n", file, line,
- m_count, get_jobid_from_tid());
+ Dmsg3(sd_dbglvl+1, "dunlock from %s:%d postcnt=%d\n", file, line, m_count);
V(m_mutex);
}
* and preparing the label.
*/
#ifdef SD_DEBUG_LOCK
-void DEVICE::_r_dlock(const char *file, int line)
-#else
-void DEVICE::r_dlock()
-#endif
+void DEVICE::_r_dlock(const char *file, int line, bool locked)
{
- int stat;
-#ifdef SD_DEBUG_LOCK
- Dmsg4(sd_dbglvl+1, "r_dlock blked=%s from %s:%d JobId=%u\n", this->print_blocked(),
- file, line, get_jobid_from_tid());
+ Dmsg3(sd_dbglvl+1, "r_dlock blked=%s from %s:%d\n", this->print_blocked(),
+ file, line);
#else
- Dmsg1(sd_dbglvl, "reclock blked=%s\n", this->print_blocked());
+void DEVICE::r_dlock(bool locked)
+{
#endif
- this->dlock();
+ int stat;
+ if (!locked) {
+ P(m_mutex); /* this->dlock(); */
+ m_count++; /* this->dlock() */
+ }
if (this->blocked() && !pthread_equal(this->no_wait_id, pthread_self())) {
this->num_waiting++; /* indicate that I am waiting */
while (this->blocked()) {
+#ifndef HAVE_WIN32
+ /* thread id on Win32 may be a struct */
Dmsg3(sd_dbglvl, "r_dlock blked=%s no_wait=%p me=%p\n", this->print_blocked(),
this->no_wait_id, pthread_self());
+#endif
if ((stat = pthread_cond_wait(&this->wait, &m_mutex)) != 0) {
berrno be;
this->dunlock();
*/
void _block_device(const char *file, int line, DEVICE *dev, int state)
{
+// ASSERT(lmgr_mutex_is_locked(&dev->m_mutex) == 1);
ASSERT(dev->blocked() == BST_NOT_BLOCKED);
dev->set_blocked(state); /* make other threads wait */
dev->no_wait_id = pthread_self(); /* allow us to continue */
void _unblock_device(const char *file, int line, DEVICE *dev)
{
Dmsg3(sd_dbglvl, "unblock %s from %s:%d\n", dev->print_blocked(), file, line);
+// ASSERT(lmgr_mutex_is_locked(&dev->m_mutex) == 1);
ASSERT(dev->blocked());
dev->set_blocked(BST_NOT_BLOCKED);
- dev->no_wait_id = 0;
+ clear_thread_id(dev->no_wait_id);
if (dev->num_waiting > 0) {
pthread_cond_broadcast(&dev->wait); /* wake them up */
}
return "BST_UNMOUNTED_WAITING_FOR_SYSOP";
case BST_MOUNT:
return "BST_MOUNT";
+ case BST_DESPOOLING:
+ return "BST_DESPOOLING";
+ case BST_RELEASING:
+ return "BST_RELEASING";
default:
return _("unknown blocked code");
}
/*
* Check if the device is blocked or not
*/
-bool is_device_unmounted(DEVICE *dev)
+bool DEVICE::is_device_unmounted()
{
bool stat;
- int blocked = dev->blocked();
- stat = (blocked == BST_UNMOUNTED) ||
- (blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
+ int blk = blocked();
+ stat = (blk == BST_UNMOUNTED) ||
+ (blk == BST_UNMOUNTED_WAITING_FOR_SYSOP);
return stat;
}
-