diff options
Diffstat (limited to 'include/VBox/com/AutoLock.h')
-rw-r--r-- | include/VBox/com/AutoLock.h | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/include/VBox/com/AutoLock.h b/include/VBox/com/AutoLock.h new file mode 100644 index 00000000..88c656e6 --- /dev/null +++ b/include/VBox/com/AutoLock.h @@ -0,0 +1,704 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - Automatic locks, implementation. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef VBOX_INCLUDED_com_AutoLock_h +#define VBOX_INCLUDED_com_AutoLock_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/types.h> + + +/** @defgroup grp_com_autolock Automatic Locks + * @ingroup grp_com + * @{ + */ + +// macros for automatic lock validation; these will amount to nothing +// unless lock validation is enabled for the runtime +#if defined(RT_LOCK_STRICT) +# define VBOX_WITH_MAIN_LOCK_VALIDATION +# define COMMA_LOCKVAL_SRC_POS , RT_SRC_POS +# define LOCKVAL_SRC_POS_DECL RT_SRC_POS_DECL +# define COMMA_LOCKVAL_SRC_POS_DECL , RT_SRC_POS_DECL +# define LOCKVAL_SRC_POS_ARGS RT_SRC_POS_ARGS +# define COMMA_LOCKVAL_SRC_POS_ARGS , RT_SRC_POS_ARGS +#else +# define COMMA_LOCKVAL_SRC_POS +# define LOCKVAL_SRC_POS_DECL +# define COMMA_LOCKVAL_SRC_POS_DECL +# define LOCKVAL_SRC_POS_ARGS +# define COMMA_LOCKVAL_SRC_POS_ARGS +#endif + +namespace util +{ + +//////////////////////////////////////////////////////////////////////////////// +// +// Order classes for lock validation +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * IPRT now has a sophisticated system of run-time locking classes to validate + * locking order. Since the Main code is handled by simpler minds, we want + * compile-time constants for simplicity, and we'll look up the run-time classes + * in AutoLock.cpp transparently. These are passed to the constructors of the + * LockHandle classes. + */ +enum VBoxLockingClass +{ + LOCKCLASS_NONE = 0, + LOCKCLASS_WEBSERVICE = 1, // highest order: webservice locks + LOCKCLASS_VIRTUALBOXOBJECT = 2, // highest order within Main itself: VirtualBox object lock + LOCKCLASS_HOSTOBJECT = 3, // Host object lock + LOCKCLASS_LISTOFMACHINES = 4, // list of machines in VirtualBox object + LOCKCLASS_MACHINEOBJECT = 5, // Machine object lock + LOCKCLASS_SNAPSHOTOBJECT = 6, // snapshot object locks + // (the snapshots tree, including the child pointers in Snapshot, + // is protected by the normal Machine object lock) + LOCKCLASS_MEDIUMQUERY = 7, // lock used to protect Machine::queryInfo + LOCKCLASS_LISTOFMEDIA = 8, // list of media (hard disks, DVDs, floppies) in VirtualBox object + LOCKCLASS_LISTOFOTHEROBJECTS = 9, // any other list of objects + LOCKCLASS_OTHEROBJECT = 10, // any regular object member variable lock + LOCKCLASS_PROGRESSLIST = 11, // list of progress objects in VirtualBox; no other object lock + // may be held after this! + LOCKCLASS_OBJECTSTATE = 12, // object state lock (handled by AutoCaller classes) + LOCKCLASS_TRANSLATOR = 13 // translator internal lock +}; + +void InitAutoLockSystem(); + +/** + * Check whether the current thread holds any locks in the given class + * + * @return true if any such locks are held, false otherwise. If the lock + * validator is not compiled in, always returns false. + * @param lockClass Which lock class to check. + */ +bool AutoLockHoldsLocksInClass(VBoxLockingClass lockClass); + +//////////////////////////////////////////////////////////////////////////////// +// +// LockHandle and friends +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Abstract base class for semaphore handles (RWLockHandle and WriteLockHandle). + * Don't use this directly, but this implements lock validation for them. + */ +class LockHandle +{ +public: + LockHandle() + {} + + virtual ~LockHandle() + {} + + /** + * Returns @c true if the current thread holds a write lock on this + * read/write semaphore. Intended for debugging only. + */ + virtual bool isWriteLockOnCurrentThread() const = 0; + + /** + * Returns @c true if the current thread holds a read lock on this + * read/write semaphore. Intended for debugging only as it isn't always + * accurate given @a fWannaHear. + */ + virtual bool isReadLockedOnCurrentThread(bool fWannaHear = true) const = 0; + + /** + * Returns the current write lock level of this semaphore. The lock level + * determines the number of nested #lockWrite() calls on the given + * semaphore handle. + * + * Note that this call is valid only when the current thread owns a write + * lock on the given semaphore handle and will assert otherwise. + */ + virtual uint32_t writeLockLevel() const = 0; + + virtual void lockWrite(LOCKVAL_SRC_POS_DECL) = 0; + virtual void unlockWrite() = 0; + virtual void lockRead(LOCKVAL_SRC_POS_DECL) = 0; + virtual void unlockRead() = 0; + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + virtual const char* describe() const = 0; +#endif + +private: + // prohibit copy + assignment + LockHandle(const LockHandle&); + LockHandle& operator=(const LockHandle&); +}; + +/** + * Full-featured read/write semaphore handle implementation. + * + * This is an auxiliary base class for classes that need full-featured + * read/write locking as described in the AutoWriteLock class documentation. + * Instances of classes inherited from this class can be passed as arguments to + * the AutoWriteLock and AutoReadLock constructors. + */ +class RWLockHandle : public LockHandle +{ +public: + RWLockHandle(VBoxLockingClass lockClass); + virtual ~RWLockHandle(); + + virtual bool isWriteLockOnCurrentThread() const; + virtual bool isReadLockedOnCurrentThread(bool fWannaHear = true) const; + + virtual void lockWrite(LOCKVAL_SRC_POS_DECL); + virtual void unlockWrite(); + virtual void lockRead(LOCKVAL_SRC_POS_DECL); + virtual void unlockRead(); + + virtual uint32_t writeLockLevel() const; + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + virtual const char* describe() const; +#endif + +private: + struct Data; + Data *m; + + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(RWLockHandle); /* Shuts up MSC warning C4625. */ +}; + +/** + * Write-only semaphore handle implementation. + * + * This is an auxiliary base class for classes that need write-only (exclusive) + * locking and do not need read (shared) locking. This implementation uses a + * cheap and fast critical section for both lockWrite() and lockRead() methods + * which makes a lockRead() call fully equivalent to the lockWrite() call and + * therefore makes it pointless to use instahces of this class with + * AutoReadLock instances -- shared locking will not be possible anyway and + * any call to lock() will block if there are lock owners on other threads. + * + * Use with care only when absolutely sure that shared locks are not necessary. + */ +class WriteLockHandle : public LockHandle +{ +public: + WriteLockHandle(VBoxLockingClass lockClass); + virtual ~WriteLockHandle(); + virtual bool isWriteLockOnCurrentThread() const; + virtual bool isReadLockedOnCurrentThread(bool fWannaHear = true) const; + + virtual void lockWrite(LOCKVAL_SRC_POS_DECL); + virtual void unlockWrite(); + virtual void lockRead(LOCKVAL_SRC_POS_DECL); + virtual void unlockRead(); + virtual uint32_t writeLockLevel() const; + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + virtual const char* describe() const; +#endif + +private: + struct Data; + Data *m; + + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(WriteLockHandle); /* Shuts up MSC warning C4625. */ +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// Lockable +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Lockable interface. + * + * This is an abstract base for classes that need read/write locking. Unlike + * RWLockHandle and other classes that makes the read/write semaphore a part of + * class data, this class allows subclasses to decide which semaphore handle to + * use. + */ +class Lockable +{ +public: + virtual ~Lockable() { } /* To make VC++ 2019 happy. */ + + /** + * Returns a pointer to a LockHandle used by AutoWriteLock/AutoReadLock + * for locking. Subclasses are allowed to return @c NULL -- in this case, + * the AutoWriteLock/AutoReadLock object constructed using an instance of + * such subclass will simply turn into no-op. + */ + virtual LockHandle *lockHandle() const = 0; + + /** + * Equivalent to <tt>#lockHandle()->isWriteLockOnCurrentThread()</tt>. + * Returns @c false if lockHandle() returns @c NULL. + */ + bool isWriteLockOnCurrentThread() + { + LockHandle *h = lockHandle(); + return h ? h->isWriteLockOnCurrentThread() : false; + } + + /** + * Equivalent to <tt>#lockHandle()->isReadLockedOnCurrentThread()</tt>. + * Returns @c false if lockHandle() returns @c NULL. + * @note Use with care, simple debug assertions and similar only. + */ + bool isReadLockedOnCurrentThread(bool fWannaHear = true) const + { + LockHandle *h = lockHandle(); + return h ? h->isReadLockedOnCurrentThread(fWannaHear) : false; + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoLockBase +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Abstract base class for all autolocks. + * + * This cannot be used directly. Use AutoReadLock or AutoWriteLock or AutoMultiWriteLock2/3 + * which directly and indirectly derive from this. + * + * In the implementation, the instance data contains a list of lock handles. + * The class provides some utility functions to help locking and unlocking + * them. + */ + +class AutoLockBase +{ +protected: + AutoLockBase(uint32_t cHandles + COMMA_LOCKVAL_SRC_POS_DECL); + AutoLockBase(uint32_t cHandles, + LockHandle *pHandle + COMMA_LOCKVAL_SRC_POS_DECL); + virtual ~AutoLockBase(); + + struct Data; + Data *m; + + virtual void callLockImpl(LockHandle &l) = 0; + virtual void callUnlockImpl(LockHandle &l) = 0; + + void callLockOnAllHandles(); + void callUnlockOnAllHandles(); + + void cleanup(); + +public: + void acquire(); + void release(); + +private: + // prohibit copy + assignment + AutoLockBase(const AutoLockBase&); + AutoLockBase& operator=(const AutoLockBase&); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoReadLock +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Automatic read lock. Use this with a RWLockHandle to request a read/write + * semaphore in read mode. You can also use this with a WriteLockHandle but + * that makes little sense since they treat read mode like write mode. + * + * If constructed with a RWLockHandle or an instance of Lockable (which in + * practice means any VirtualBoxBase derivative), it autoamtically requests + * the lock in read mode and releases the read lock in the destructor. + */ +class AutoReadLock : public AutoLockBase +{ +public: + + /** + * Constructs a null instance that does not manage any read/write + * semaphore. + * + * Note that all method calls on a null instance are no-ops. This allows to + * have the code where lock protection can be selected (or omitted) at + * runtime. + */ + AutoReadLock(LOCKVAL_SRC_POS_DECL) + : AutoLockBase(1, + NULL + COMMA_LOCKVAL_SRC_POS_ARGS) + { } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a read lock. + */ + AutoReadLock(LockHandle *aHandle + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(1, + aHandle + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a read lock. + */ + AutoReadLock(LockHandle &aHandle + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(1, + &aHandle + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a read lock. + */ + AutoReadLock(const Lockable &aLockable + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(1, + aLockable.lockHandle() + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a read lock. + */ + AutoReadLock(const Lockable *aLockable + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(1, + aLockable ? aLockable->lockHandle() : NULL + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + virtual ~AutoReadLock(); + + virtual void callLockImpl(LockHandle &l); + virtual void callUnlockImpl(LockHandle &l); + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoReadLock); /* Shuts up MSC warning C4625. */ +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoWriteLockBase +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Base class for all auto write locks. + * + * This cannot be used directly. Use AutoWriteLock or AutoMultiWriteLock2/3 + * which derive from this. + * + * It has some utility methods for subclasses. + */ +class AutoWriteLockBase : public AutoLockBase +{ +protected: + AutoWriteLockBase(uint32_t cHandles + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(cHandles + COMMA_LOCKVAL_SRC_POS_ARGS) + { } + + AutoWriteLockBase(uint32_t cHandles, + LockHandle *pHandle + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(cHandles, + pHandle + COMMA_LOCKVAL_SRC_POS_ARGS) + { } + + virtual ~AutoWriteLockBase() + { } + + virtual void callLockImpl(LockHandle &l); + virtual void callUnlockImpl(LockHandle &l); + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoWriteLockBase); /* Shuts up MSC warning C4625. */ +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoWriteLock +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Automatic write lock. Use this with a RWLockHandle to request a read/write + * semaphore in write mode. There can only ever be one writer of a read/write + * semaphore: while the lock is held in write mode, no other writer or reader + * can request the semaphore and will block. + * + * If constructed with a RWLockHandle or an instance of Lockable (which in + * practice means any VirtualBoxBase derivative), it autoamtically requests + * the lock in write mode and releases the write lock in the destructor. + * + * When used with a WriteLockHandle, it requests the semaphore contained therein + * exclusively. + */ +class AutoWriteLock : public AutoWriteLockBase +{ +public: + + /** + * Constructs a null instance that does not manage any read/write + * semaphore. + * + * Note that all method calls on a null instance are no-ops. This allows to + * have the code where lock protection can be selected (or omitted) at + * runtime. + */ + AutoWriteLock(LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(1, + NULL + COMMA_LOCKVAL_SRC_POS_ARGS) + { } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a write lock. + */ + AutoWriteLock(LockHandle *aHandle + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(1, + aHandle + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a write lock. + */ + AutoWriteLock(LockHandle &aHandle + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(1, + &aHandle + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a write lock. + */ + AutoWriteLock(const Lockable &aLockable + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(1, + aLockable.lockHandle() + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a write lock. + */ + AutoWriteLock(const Lockable *aLockable + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(1, + aLockable ? aLockable->lockHandle() : NULL + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a write lock. + */ + AutoWriteLock(uint32_t cHandles, + LockHandle** pHandles + COMMA_LOCKVAL_SRC_POS_DECL); + + /** + * Release all write locks acquired by this instance through the #acquire() + * call and destroys the instance. + * + * Note that if there there are nested #acquire() calls without the + * corresponding number of #release() calls when the destructor is called, it + * will assert. This is because having an unbalanced number of nested locks + * is a program logic error which must be fixed. + */ + virtual ~AutoWriteLock() + { + cleanup(); + } + + void attach(LockHandle *aHandle); + + /** @see attach (LockHandle *) */ + void attach(LockHandle &aHandle) + { + attach(&aHandle); + } + + /** @see attach (LockHandle *) */ + void attach(const Lockable &aLockable) + { + attach(aLockable.lockHandle()); + } + + /** @see attach (LockHandle *) */ + void attach(const Lockable *aLockable) + { + attach(aLockable ? aLockable->lockHandle() : NULL); + } + + bool isWriteLockOnCurrentThread() const; + uint32_t writeLockLevel() const; + + bool isReadLockedOnCurrentThread(bool fWannaHear = true) const; + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoWriteLock); /* Shuts up MSC warning C4625. */ +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoMultiWriteLock* +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * A multi-write-lock containing two other write locks. + * + */ +class AutoMultiWriteLock2 : public AutoWriteLockBase +{ +public: + AutoMultiWriteLock2(Lockable *pl1, + Lockable *pl2 + COMMA_LOCKVAL_SRC_POS_DECL); + AutoMultiWriteLock2(LockHandle *pl1, + LockHandle *pl2 + COMMA_LOCKVAL_SRC_POS_DECL); + + virtual ~AutoMultiWriteLock2() + { + cleanup(); + } + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock2); /* Shuts up MSC warning C4625. */ +}; + +/** + * A multi-write-lock containing three other write locks. + * + */ +class AutoMultiWriteLock3 : public AutoWriteLockBase +{ +public: + AutoMultiWriteLock3(Lockable *pl1, + Lockable *pl2, + Lockable *pl3 + COMMA_LOCKVAL_SRC_POS_DECL); + AutoMultiWriteLock3(LockHandle *pl1, + LockHandle *pl2, + LockHandle *pl3 + COMMA_LOCKVAL_SRC_POS_DECL); + + virtual ~AutoMultiWriteLock3() + { + cleanup(); + } + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock3); /* Shuts up MSC warning C4625. */ +}; + +/** + * A multi-write-lock containing four other write locks. + * + */ +class AutoMultiWriteLock4 : public AutoWriteLockBase +{ +public: + AutoMultiWriteLock4(Lockable *pl1, + Lockable *pl2, + Lockable *pl3, + Lockable *pl4 + COMMA_LOCKVAL_SRC_POS_DECL); + AutoMultiWriteLock4(LockHandle *pl1, + LockHandle *pl2, + LockHandle *pl3, + LockHandle *pl4 + COMMA_LOCKVAL_SRC_POS_DECL); + + virtual ~AutoMultiWriteLock4() + { + cleanup(); + } + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock4); /* Shuts up MSC warning C4625. */ +}; + +} /* namespace util */ + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_AutoLock_h */ + +/* vi: set tabstop=4 shiftwidth=4 expandtab: */ |