-
Notifications
You must be signed in to change notification settings - Fork 1
Lock Objects
The vwpp
library defines several lock classes that acquire resources in their constructor and release them in their destructor. You can control the duration of ownership by defining the scope in which the lock is defined.
NOTE: Starting with v2.7, we introduced a nested, version namespace to enforce matching APIs when building and deploying. In the following code examples, we refer to the namespace as "VWPP". This translates to vwpp
for pre-2.7 libraries and to vwpp::v2_7
for v2.7.
VWPP::AbsPriority<unsigned>()
An AbsPriority<>
object sets the priority of the current task during its lifetime. When the object is destroyed, the previous priority is restored. This class can be used to temporarily change the task's priority. For instance, to run some code at priority 100, you can do something like this:
// Running at some other priority.
{
VWPP::AbsPriority<100> prio;
// Now running at priority 100.
}
// Now running at previous priority.
CountingSemaphore::Lock<sem>(int = WAIT_FOREVER)
This locking class is nested in the CountingSemaphore classes' namespace. It is used to claim ownership of one count of a counting semaphore. The constructor takes an optional integer specifying the timeout value (in milliseconds). If a timeout occurs, a VWPP::timeout_error
exception is thrown[^1]. If no timeout value is provided, the task will block until at least one count of the semaphore is available.
The template parameter, sem
, specifies the only counting semaphore that this lock can acquire. Limitations in C++ template parameters require that the semaphore be of global scope.
VWPP::IntLock()
An IntLock
object will block interrupts during its lifetime. Since this object wraps intLock()
/intUnlock()
, it also has the same properties of those functions. Specifically, VxWorks will re-enable interrupts when a task that disables interrupts becomes blocked. The interrupts are disabled when the task is unblocked.
Note also that, if you call into the VxWorks kernel or standard library, interrupts may briefly get re-enabled. Also, if calling into the kernel causes a higher process to run, interrupts will get enabled. To truly create a "critical section" of code, you need to create both an VWPP::IntLock
and VWPP::SchedLock
object and then not call into the kernel.
All these constraints may seem limiting but they really aren't; keeping interrupts off for extended periods increases latency in responding to hardware events and can even affect the hard real-time properties of the OS. Disabling interrupts should be very brief and be only used to atomically access data shared with the interrupt.
Mutex::Lock<mtx>(int = WAIT_FOREVER)
The purpose of the Mutex::Lock<>
class is to claim ownership of a mutex for the duration of the lock's life. The constructor takes an optional integer specifying the timeout value (in milliseconds). If a timeout occurs, a VWPP::timeout_error
exception is thrown[^1]. If no timeout value is provided, the task will block until the mutex is available.
The template parameter, mtx
, specifies the only mutex that this lock can acquire. The C++ conditions of using an address for a template parameter require that the mutex be of global scope.
If you want to prevent code that takes a reference to your lock from unlocking it (e.g. by using VWPP::Mutex::Unlock<VWPP::Mutex&>
), make it const
.
Mutex::LockWithInt<mtx>(int = WAIT_FOREVER)
The purpose of the Mutex::LockWithInt<>
class is to claim ownership of a mutex and disable interrupts for the duration of the lock's life. This lock is used to serial access to a resource between multiple tasks and interrupt handlers. The constructor takes an optional integer specifying the timeout value (in milliseconds). If a timeout occurs, a VWPP::timeout_error
exception is thrown[^1]. If no timeout value is provided, the task will block until the mutex is available.
The template parameter, mtx
, specifies the only mutex that this lock can acquire. The C++ conditions of using an address for a template parameter require that the mutex be of global scope.
Two typecast operators are defined allowing these locks to be passed to functions requiring const VWPP::Mutex::Lock<>
or const VWPP:IntLock
references.
Locks of this type can't be unlocked using VWPP::Mutex::Unlock<>
.
VWPP::MinAbsPriority<unsigned Prio>()
The MinAbsPriority<>
object sets the priority of the current task to at least Prio
during its lifetime. If the task is already at or higher than the request priority level, nothing happens. When the object is destroyed, the previous priority is restored.
Mutex::PMLock<>(T* obj, int = WAIT_FOREVER)
Mutex::PMLock<>
is a form of lock similar to Mutex::Lock<>
except it manages acquisition of mutexes contained in an object. The first parameter of the lock's constructor is a pointer to the object. In most cases, the programmer will specify this
. The second parameter is an optional integer specifying the timeout value (in milliseconds). If a timeout occurs, a VWPP::timeout_error
exception is thrown[^1]. If no timeout value is provided, the task will block until the mutex is available.
The first template parameter, T
, specifies the class of the target object. The second parameter is a pointer-to-member whose target is a Mutex
. You can make the code clearer using a typedef
as shown below.
class Object
{
private:
VWPP::Mutex mtx;
typedef VWPP::Mutex::PMLock<Object, &Object::mtx> ObjLock;
public:
void f()
{
ObjLock lock(this);
// Object's mutex is acquired until end of method.
}
}
It's disappointing that there needs to be two, uniquely named templates to handle locks. If anyone is able to find a way to support mutexes in objects using partial specialization of the VWPP::Mutex::Lock
template, I'd be very grateful!
Mutex::PMLockWithInt<>(T* obj, int = WAIT_FOREVER)
Mutex::PMLockWithInt<>
is a form of lock similar to Mutex::LockWithInt<>
except it manages acquisition of mutexes contained in an object. The first parameter of the lock's constructor is a pointer to the object. In most cases, the programmer will specify this
. The second parameter is an optional integer specifying the timeout value (in milliseconds). If a timeout occurs, a VWPP::timeout_error
exception is thrown[^1]. If no timeout value is provided, the task will block until the mutex is available.
The first template parameter, T
, specifies the class of the target object. The second parameter is a pointer-to-member whose target is a Mutex
. You can make the code clearer using a typedef
as shown below.
class Object
{
private:
VWPP::Mutex mtx;
typedef VWPP::Mutex::PMLockWithInt<Object, &Object::mtx> ObjLock;
public:
void f()
{
ObjLock lock(this);
// Object's mutex is acquired, and interrupts are disabled,
// until the end of the method.
}
}
Two typecast operators are defined allowing these locks to be passed to functions requiring const VWPP::Mutex::PMLock<>
or const VWPP:IntLock
references.
Locks of this type can't be unlocked using VWPP::Mutex::PMUnlock<>
.
VWPP::ProtLock()
The ProtLock
object protects the task that created it from being deleted during the lifetime of the object. It is a wrapper around the VxWorks taskSafe()
/taskUnsafe()
functions. Use this lock around critical sections of code in which deleting the task would leave the system in an invalid state.
NOTE: Code executing with an active mutex lock is already safe from being deleted.
VWPP::RelPriority<int>()
The RelPriority<>
object makes a relative adjustment to the priority of the current task. For instance, to temporarily raise the priority of a task by 1, do the following:
// Running at some priority.
{
VWPP::RelPriority<1> prio;
// Now running with 1 higher priority.
}
// Now running at previous priority.
VWPP::SchedLock()
The SchedLock
object disables task scheduling during its lifetime. This object is included for completeness and it could be useful. However, shutting off the scheduler is a very intrusive way to guarantee mutual exclusion. Mutexes and IntLock
are much better alternatives. Use SchedLocks
sparingly, if at all.
Mutex::PMUnlock<>(T* obj, Mutex::PMLock<T, pmtx>& lock)
NOTE: This class should never be used in normal code. Always look for a better solution than using this class.
Mutex::PMUnlock<>
is a form of lock similar to Mutex::Unlock<>
except it manages ownership of mutexes contained in an object. The first parameter of the object's constructor is a pointer to the object. In most cases, the programmer will specify this
. The second parameter is an instance of a lock that already owns the mutex. Since this object unlocks the mutex, the lock reference must be non-const.
The first template parameter, T
, specifies the class of the target object. The second parameter is a pointer-to-member whose target is a Mutex
.
Mutex::Unlock<mtx>(Mutex::Lock<mtx>& lock)
NOTE: This class should never be used in normal code. Always look for a better solution than using this class.
The purpose of the Mutex::Unlock<>
class is to relinquish ownership of a mutex for the duration of the object's life. When the object goes out of scope, the mutex is again owned. The only parameter to the constructor is a lock to the mutex to be released. Since this object unlocks the mutex, the lock reference must be non-const.
[^1]: In pre-v2.5 libraries, timeouts throw std::runtime_error
.