Files
vxWorks/h/vxwSemLib.h
2025-08-20 18:25:46 +08:00

697 lines
26 KiB
C++

// VXWSem/vxwSemLib.h - semaphore classes
// Copyright 1995-1999 Wind River Systems, Inc.
// modification history
// --------------------
// 01d,08mar99,jdi doc: fixed wrong cross-references.
// 01c,21feb99,jdi added library section, checked in documentation.
// 01b,02oct95,rhp documented.
// 01a,15jun95,srh written.
// DESCRIPTION
// Semaphores are the basis for synchronization and mutual exclusion in
// VxWorks. They are powerful in their simplicity and form the foundation
// for numerous VxWorks facilities.
//
// Different semaphore types serve different needs, and while the
// behavior of the types differs, their basic interface is the same.
// The VXWSem class provides semaphore routines common to all VxWorks
// semaphore types. For all types, the two basic operations are
// VXWSem::take() and VXWSem::give(), the acquisition or relinquishing
// of a semaphore.
//
// Semaphore creation and initialization is handled by the following classes,
// which inherit the basic operations from `VXWSem':
//
// `VXWBSem' - binary semaphores
// `VXWCSem' - counting semaphores
// `VXWMSem' - mutual exclusion semaphores
//
// Two additional semaphore classes provide semaphores that operate
// over shared memory (with the optional product VxMP). These classes
// also inherit from `VXWSmNameLib'; they are described in vxwSmLib.
// The following are the class names for these shared-memory
// semaphores:
//
// `VXWSmBSem' - shared-memory binary semaphores
// `VXWSmCSem' - shared-memory counting semaphores
//
// Binary semaphores offer the greatest speed and the broadest applicability.
//
// The `VXWSem' class provides all other semaphore operations, including
// routines for semaphore control, deletion, and information.
//
// SEMAPHORE CONTROL
// The VXWSem::take() call acquires a specified semaphore, blocking the calling
// task or making the semaphore unavailable. All semaphore types support a
// timeout on the VXWSem::take() operation. The timeout is specified as the
// number of ticks to remain blocked on the semaphore. Timeouts of
// WAIT_FOREVER and NO_WAIT codify common timeouts. If a VXWSem::take() times
// out, it returns ERROR. Refer to the library of the specific semaphore
// type for the exact behavior of this operation.
//
// The VXWSem::give() call relinquishes a specified semaphore,
// unblocking a pended task or making the semaphore available. Refer
// to the library of the specific semaphore type for the exact
// behavior of this operation.
//
// The VXWSem::flush() call may be used to atomically unblock all
// tasks pended on a semaphore queue; that is, it unblocks all tasks
// before any are allowed to run. It may be thought of as a
// broadcast operation in synchronization applications. The state of
// the semaphore is unchanged by the use of VXWSem::flush(); it is not
// analogous to VXWSem::give().
//
// SEMAPHORE DELETION
// The VXWSem::~VXWSem() destructor terminates a semaphore and
// deallocates any associated memory. The deletion of a semaphore
// unblocks tasks pended on that semaphore; the routines which were
// pended return ERROR. Take care when deleting semaphores,
// particularly those used for mutual exclusion, to avoid deleting a
// semaphore out from under a task that already has taken (owns) that
// semaphore. Applications should adopt the protocol of only deleting
// semaphores that the deleting task has successfully taken.
//
// SEMAPHORE INFORMATION
// The VXWSem::info() call is a useful debugging aid, reporting all
// tasks blocked on a specified semaphore. It provides a snapshot of
// the queue at the time of the call, but because semaphores are
// dynamic, the information may be out of date by the time it is
// available. As with the current state of the semaphore, use of the
// queue of pended tasks should be restricted to debugging uses only.
//
// INCLUDE FILES: vxwSemLib.h
//
// SEE ALSO: VXWTask, vxwSmLib,
// .pG "Basic OS"
//
// SECTION: 1C
//
#ifndef vxwSemLib_h
#define vxwSemLib_h
#include "vxWorks.h"
#include "semLib.h"
#include "vxwObject.h"
#include "vxwErr.h"
#include "private/semLibP.h"
class VXWSem : virtual public VXWIdObject
{
public:
//_ VXWSem Public Constructors
///////////////////////////////////////////////////////////////////////////////
//
// VXWSem::VXWSem - build semaphore object from semaphore ID
//
// Use this constructor to manipulate a semaphore that was not created
// using C++ interfaces. The argument <id> is the semaphore
// identifier returned and used by the C interface to the VxWorks
// semaphore facility.
//
// RETURNS: N/A
//
// SEE ALSO: semLib
VXWSem (SEM_ID id)
: sem_ (id)
{
}
///////////////////////////////////////////////////////////////////////////////
//
// VXWSem::~VXWSem - delete a semaphore
//
// This destructor terminates and deallocates any memory associated with a
// specified semaphore. Any pended tasks unblock and return ERROR.
//
// WARNING
// Take care when deleting semaphores, particularly those used for
// mutual exclusion, to avoid deleting a semaphore out from under a
// task that already has taken (owns) that semaphore. Applications
// should adopt the protocol of only deleting semaphores that the
// deleting task has successfully taken.
//
// RETURNS: N/A
//
// SEE ALSO
// .pG "Basic OS"
virtual ~VXWSem ()
{
if (semDelete (sem_) != OK)
vxwThrowErrno ();
}
//_ VXWSem Public Member Functions
///////////////////////////////////////////////////////////////////////////////
//
// VXWSem::give - give a semaphore
//
// This routine performs the give operation on a specified semaphore.
// Depending on the type of semaphore, the state of the semaphore and of the
// pending tasks may be affected. The behavior of VXWSem::give() is
// discussed fully in the constructor description for the specific semaphore
// type being used.
//
// RETURNS: OK.
//
// SEE ALSO
// VXWCSem::VXWCsem(), VXWBSem::VXWBsem(), VXWMSem::VXWMsem(),
// .pG "Basic OS"
STATUS give ()
{
return semGive (sem_);
}
///////////////////////////////////////////////////////////////////////////////
//
// VXWSem::take - take a semaphore
//
// This routine performs the take operation on a specified semaphore.
// Depending on the type of semaphore, the state of the semaphore and
// the calling task may be affected. The behavior of VXWSem::take() is
// discussed fully in the constructor description for the specific semaphore
// type being used.
//
// A timeout in ticks may be specified. If a task times out, VXWSem::take()
// returns ERROR. Timeouts of WAIT_FOREVER and NO_WAIT indicate to wait
// indefinitely or not to wait at all.
//
// When VXWSem::take() returns due to timeout, it sets the errno to
// S_objLib_OBJ_TIMEOUT (defined in objLib.h).
//
// The VXWSem::take() routine must not be called from interrupt
// service routines.
//
// RETURNS: OK, or ERROR if the task timed out.
//
// SEE ALSO
// VXWCSem::VXWCsem(), VXWBSem::VXWBsem(), VXWMSem::VXWMsem(),
// .pG "Basic OS"
STATUS take (int timeout)
{
return semTake (sem_, timeout);
}
///////////////////////////////////////////////////////////////////////////////
//
// VXWSem::flush - unblock every task pended on a semaphore
//
// This routine atomically unblocks all tasks pended on a specified
// semaphore; that is, all tasks are unblocked before any is allowed
// to run. The state of the underlying semaphore is unchanged. All
// pended tasks will enter the ready queue before having a chance to
// execute.
//
// The flush operation is useful as a means of broadcast in
// synchronization applications. Its use is illegal for mutual-exclusion
// semaphores created with VXWMSem::VXWMSem().
//
// RETURNS: OK, or ERROR if the operation is not supported.
//
// SEE ALSO
// VXWCSem::VXWCsem(), VXWBSem::VXWBsem(), VXWMSem::VXWMsem(),
// .pG "Basic OS"
STATUS flush ()
{
return semFlush (sem_);
}
///////////////////////////////////////////////////////////////////////////////
//
// VXWSem::id - reveal underlying semaphore ID
//
// This routine returns the semaphore ID corresponding to a semaphore
// object. The semaphore ID is used by the C interface to VxWorks
// semaphores.
//
// RETURNS: Semaphore ID.
//
// SEE ALSO: semLib
SEM_ID id () const
{
return sem_;
}
///////////////////////////////////////////////////////////////////////////////
//
// VXWSem::info - get a list of task IDs that are blocked on a semaphore
//
// This routine reports the tasks blocked on a specified semaphore.
// Up to <maxTasks> task IDs are copied to the array specified by <idList>.
// The array is unordered.
//
// WARNING:
// There is no guarantee that all listed tasks are still valid or that new
// tasks have not been blocked by the time VXWSem::info() returns.
//
// RETURNS: The number of blocked tasks placed in <idList>.
STATUS info (int idList[], int maxTasks) const
{
return semInfo (sem_, idList, maxTasks);
}
///////////////////////////////////////////////////////////////////////////////
//
// VXWSem::show - show information about a semaphore
//
// This routine displays (on standard output) the state and optionally
// the pended tasks of a semaphore.
//
// A summary of the state of the semaphore is displayed as follows:
// .CS
// Semaphore Id : 0x585f2
// Semaphore Type : BINARY
// Task Queuing : PRIORITY
// Pended Tasks : 1
// State : EMPTY {Count if COUNTING, Owner if MUTEX}
// .CE
//
// If <level> is 1, more detailed information is displayed.
// If tasks are blocked on the queue, they are displayed in the order
// in which they will unblock, as follows:
// .CS
// NAME TID PRI DELAY
// ---------- -------- --- -----
// tExcTask 3fd678 0 21
// tLogTask 3f8ac0 0 611
// .CE
//
// RETURNS: OK or ERROR.
STATUS show (int level) const
{
return semShow (sem_, level);
}
protected:
VXWSem ()
{
}
VXWSem & operator = (const VXWSem &)
{
return *this;
}
VXWSem (const VXWSem &)
{
}
virtual void * myValue ();
SEM_ID sem_;
};
class VXWCSem : public VXWSem
{
public:
//_ VXWCSem Public Constructors
///////////////////////////////////////////////////////////////////////////////
//
// VXWCSem::VXWCSem - create and initialize a counting semaphore
//
// This routine allocates and initializes a counting semaphore. The
// semaphore is initialized to the specified initial count.
//
// The <opts> parameter specifies the queuing style for blocked tasks.
// Tasks may be queued on a priority basis or a first-in-first-out basis.
// These options are SEM_Q_PRIORITY and SEM_Q_FIFO, respectively.
//
// A counting semaphore may be viewed as a cell in memory whose contents
// keep track of a count. When a task takes a counting semaphore, using
// VXWSem::take(), subsequent action depends on the state of the count:
// .IP (1) 4
// If the count is non-zero, it is decremented and the calling task
// continues executing.
// .IP (2)
// If the count is zero, the task is blocked, pending the availability
// of the semaphore. If a timeout is specified and the timeout expires, the
// pended task is removed from the queue of pended tasks and enters the
// ready state with an ERROR status. A pended task is ineligible for CPU
// allocation. Any number of tasks may be pended simultaneously on the same
// counting semaphore.
// .LP
// When a task gives a semaphore, using VXWSem::give(), the next
// available task in the pend queue is unblocked. If no task is
// pending on this semaphore, the semaphore count is incremented.
// Note that if a semaphore is given, and a task is unblocked that is
// of higher priority than the task that called VXWSem::give(), the
// unblocked task preempts the calling task.
//
// A VXWSem::flush() on a counting semaphore atomically unblocks all pended
// tasks in the semaphore queue. Thus, all tasks are made ready before any
// task actually executes. The count of the semaphore remains unchanged.
//
// INTERRUPT USAGE
// Counting semaphores may be given but not taken from interrupt level.
//
// CAVEATS
// There is no mechanism to give back or reclaim semaphores automatically when
// tasks are suspended or deleted. Such a mechanism, though desirable, is not
// currently feasible. Without explicit knowledge of the state of the guarded
// resource or region, reckless automatic reclamation of a semaphore could
// leave the resource in a partial state. Thus, if a task ceases execution
// unexpectedly, as with a bus error, currently owned semaphores are not
// given back, effectively leaving a resource permanently unavailable. The
// mutual-exclusion semaphores provided by `VXWMSem' offer protection from
// unexpected task deletion.
//
// RETURNS: N/A
VXWCSem (int opts, int count)
: VXWSem (semCCreate (opts, count))
{
if (sem_ == 0)
vxwThrowErrno ();
}
protected:
VXWCSem ()
{
}
VXWCSem (const VXWCSem &)
{
}
VXWCSem & operator = (const VXWCSem &)
{
return *this;
}
};
class VXWBSem : public VXWSem
{
public:
//_ VXWBSem Public Constructors
///////////////////////////////////////////////////////////////////////////////
//
// VXWBSem::VXWBSem - create and initialize a binary semaphore
//
// This routine allocates and initializes a binary semaphore. The
// semaphore is initialized to the state <iState>: either SEM_FULL
// (1) or SEM_EMPTY (0).
//
// The <opts> parameter specifies the queuing style for blocked tasks.
// Tasks can be queued on a priority basis or a first-in-first-out basis.
// These options are SEM_Q_PRIORITY and SEM_Q_FIFO, respectively.
//
// Binary semaphores are the most versatile, efficient, and conceptually
// simple type of semaphore. They can be used to: (1) control mutually
// exclusive access to shared devices or data structures, or (2) synchronize
// multiple tasks, or task-level and interrupt-level processes. Binary
// semaphores form the foundation of numerous VxWorks facilities.
//
// A binary semaphore can be viewed as a cell in memory whose contents
// are in one of two states, full or empty. When a task takes a
// binary semaphore, using VXWSem::take(), subsequent action depends
// on the state of the semaphore:
// .IP (1) 4
// If the semaphore is full, the semaphore is made empty, and the calling task
// continues executing.
// .IP (2)
// If the semaphore is empty, the task is blocked, pending the
// availability of the semaphore. If a timeout is specified and the timeout
// expires, the pended task is removed from the queue of pended tasks
// and enters the ready state with an ERROR status. A pended task
// is ineligible for CPU allocation. Any number of tasks may be pended
// simultaneously on the same binary semaphore.
// .LP
// When a task gives a binary semaphore, using VXWSem::give(), the
// next available task in the pend queue is unblocked. If no task is
// pending on this semaphore, the semaphore becomes full. Note that
// if a semaphore is given, and a task is unblocked that is of higher
// priority than the task that called VXWSem::give(), the unblocked
// task preempts the calling task.
//
// MUTUAL EXCLUSION
// To use a binary semaphore as a means of mutual exclusion, first create it
// with an initial state of full.
//
// Then guard a critical section or resource by taking the semaphore with
// VXWSem::take(), and exit the section or release the resource by giving
// the semaphore with VXWSem::give().
//
// While there is no restriction on the same semaphore being given,
// taken, or flushed by multiple tasks, it is important to ensure the
// proper functionality of the mutual-exclusion construct. While
// there is no danger in any number of processes taking a semaphore,
// the giving of a semaphore should be more carefully controlled. If
// a semaphore is given by a task that did not take it, mutual
// exclusion could be lost.
//
// SYNCHRONIZATION
// To use a binary semaphore as a means of synchronization, create it
// with an initial state of empty. A task blocks by taking a
// semaphore at a synchronization point, and it remains blocked until
// the semaphore is given by another task or interrupt service
// routine.
//
// Synchronization with interrupt service routines is a particularly
// common need. Binary semaphores can be given, but not taken, from
// interrupt level. Thus, a task can block at a synchronization point
// with VXWSem::take(), and an interrupt service routine can unblock
// that task with VXWSem::give().
//
// A semFlush() on a binary semaphore atomically unblocks all pended
// tasks in the semaphore queue; that is, all tasks are unblocked at once,
// before any actually execute.
//
// CAVEATS
// There is no mechanism to give back or reclaim semaphores automatically when
// tasks are suspended or deleted. Such a mechanism, though desirable, is not
// currently feasible. Without explicit knowledge of the state of the guarded
// resource or region, reckless automatic reclamation of a semaphore could
// leave the resource in a partial state. Thus, if a task ceases execution
// unexpectedly, as with a bus error, currently owned semaphores will not be
// given back, effectively leaving a resource permanently unavailable. The
// mutual-exclusion semaphores provided by `VXWMSem' offer protection from
// unexpected task deletion.
//
// RETURNS: N/A
VXWBSem (int opts, SEM_B_STATE iState)
: VXWSem (semBCreate (opts, iState))
{
if (sem_ == 0)
vxwThrowErrno ();
}
protected:
VXWBSem ()
{
}
VXWBSem (const VXWBSem &)
{
}
VXWBSem & operator = (const VXWBSem &)
{
return *this;
}
};
class VXWMSem : public VXWSem
{
public:
//_ VXWMSem Public Constructors
///////////////////////////////////////////////////////////////////////////////
//
// VXWMSem::VXWMSem - create and initialize a mutual-exclusion semaphore
//
// This routine allocates and initializes a mutual-exclusion semaphore. The
// semaphore state is initialized to full.
//
// Semaphore options include the following:
// .iP SEM_Q_PRIORITY 8
// Queue pended tasks on the basis of their priority.
// .iP SEM_Q_FIFO
// Queue pended tasks on a first-in-first-out basis.
// .iP SEM_DELETE_SAFE
// Protect a task that owns the semaphore from unexpected deletion.
// This option enables an implicit taskSafe() for each VXWSem::take(),
// and an implicit taskUnsafe() for each VXWSem::give().
// .iP SEM_INVERSION_SAFE
// Protect the system from priority inversion. With this option, the
// task owning the semaphore executes at the highest priority of the
// tasks pended on the semaphore, if that is higher than its current
// priority. This option must be accompanied by the SEM_Q_PRIORITY
// queuing mode.
// .LP
//
// Mutual-exclusion semaphores offer convenient options suited for
// situations that require mutually exclusive access to resources.
// Typical applications include sharing devices and protecting data
// structures. Mutual-exclusion semaphores are used by many
// higher-level VxWorks facilities.
//
// The mutual-exclusion semaphore is a specialized version of the
// binary semaphore, designed to address issues inherent in mutual
// exclusion, such as recursive access to resources, priority
// inversion, and deletion safety. The fundamental behavior of the
// mutual-exclusion semaphore is identical to the binary semaphore as
// described for VXWBSem::VXWBSem(), except for the following
// restrictions:
//
// .iP
// It can only be used for mutual exclusion.
// .iP
// It can only be given by the task that took it.
// .iP
// It may not be taken or given from interrupt level.
// .iP
// The VXWSem::flush() operation is illegal.
// .LP
//
// These last two operations have no meaning in mutual-exclusion situations.
//
// RECURSIVE RESOURCE ACCESS
//
// A special feature of the mutual-exclusion semaphore is that it may
// be taken "recursively;" that is, it can be taken more than once by
// the task that owns it before finally being released. Recursion is
// useful for a set of routines that need mutually exclusive access to
// a resource, but may need to call each other.
//
// Recursion is possible because the system keeps track of which task
// currently owns a mutual-exclusion semaphore. Before being
// released, a mutual-exclusion semaphore taken recursively must be
// given the same number of times it has been taken; this is tracked
// by means of a count which increments with each VXWSem::take()
// and decrements with each VXWSem::give().
//
// PRIORITY-INVERSION SAFETY
// If the option SEM_INVERSION_SAFE is selected, the library adopts a
// priority-inheritance protocol to resolve potential occurrences of
// "priority inversion," a problem stemming from the use semaphores for
// mutual exclusion. Priority inversion arises when a higher-priority task
// is forced to wait an indefinite period of time for the completion of a
// lower-priority task.
//
// Consider the following scenario: T1, T2, and T3 are tasks of high,
// medium, and low priority, respectively. T3 has acquired some
// resource by taking its associated semaphore. When T1 preempts T3
// and contends for the resource by taking the same semaphore, it
// becomes blocked. If we could be assured that T1 would be blocked
// no longer than the time it normally takes T3 to finish with the
// resource, the situation would not be problematic. However, the
// low-priority task is vulnerable to preemption by medium-priority
// tasks; a preempting task, T2, could inhibit T3 from relinquishing
// the resource. This condition could persist, blocking T1 for an
// indefinite period of time.
//
// The priority-inheritance protocol solves the problem of priority
// inversion by elevating the priority of T3 to the priority of T1
// during the time T1 is blocked on T3. This protects T3, and
// indirectly T1, from preemption by T2. Stated more generally, the
// priority-inheritance protocol assures that a task which owns a
// resource executes at the priority of the highest priority task
// blocked on that resource. When execution is complete, the task
// gives up the resource and returns to its normal, or standard,
// priority. Hence, the "inheriting" task is protected from
// preemption by any intermediate-priority tasks.
//
// The priority-inheritance protocol also takes into consideration a task's
// ownership of more than one mutual-exclusion semaphore at a time. Such a
// task will execute at the priority of the highest priority task blocked on
// any of the resources it owns. The task returns to its normal priority
// only after relinquishing all of its mutual-exclusion semaphores that have
// the inversion-safety option enabled.
//
// SEMAPHORE DELETION
// The VXWSem::~VXWSem() destructor terminates a semaphore and deallocates any
// associated memory. The deletion of a semaphore unblocks tasks pended
// on that semaphore; the routines which were pended return ERROR. Take
// special care when deleting mutual-exclusion semaphores to avoid
// deleting a semaphore out from under a task that already owns (has
// taken) that semaphore. Applications should adopt the protocol of only
// deleting semaphores that the deleting task owns.
//
// TASK-DELETION SAFETY
// If the option SEM_DELETE_SAFE is selected, the task owning the semaphore
// is protected from deletion as long as it owns the semaphore. This
// solves another problem endemic to mutual exclusion. Deleting a task
// executing in a critical region can be catastrophic. The resource could be
// left in a corrupted state and the semaphore guarding the resource would be
// unavailable, effectively shutting off all access to the resource.
//
// As discussed in taskLib, the primitives taskSafe() and taskUnsafe()
// offer one solution, but as this type of protection goes hand in
// hand with mutual exclusion, the mutual-exclusion semaphore provides
// the option SEM_DELETE_SAFE, which enables an implicit taskSafe()
// with each VXWSem::take(), and a taskUnsafe() with each
// VXWSem::give(). This convenience is also more efficient, as the
// resulting code requires fewer entrances to the kernel.
//
// CAVEATS
// There is no mechanism to give back or reclaim semaphores automatically when
// tasks are suspended or deleted. Such a mechanism, though desirable, is not
// currently feasible. Without explicit knowledge of the state of the guarded
// resource or region, reckless automatic reclamation of a semaphore could
// leave the resource in a partial state. Thus if a task ceases execution
// unexpectedly, as with a bus error, currently owned semaphores will not be
// given back, effectively leaving a resource permanently unavailable. The
// SEM_DELETE_SAFE option partially protects an application, to the extent
// that unexpected deletions will be deferred until the resource is released.
//
// RETURNS: N/A
//
// SEE ALSO: taskSafe(), taskUnsafe()
VXWMSem (int opts)
: VXWSem (semMCreate (opts))
{
if (sem_ == 0)
vxwThrowErrno ();
}
//_ VXWMSem Public Member Functions
//////////////////////////////////////////////////////////////////////////////
//
// VXWMSem::giveForce - give a mutual-exclusion semaphore without restrictions
//
// This routine gives a mutual-exclusion semaphore, regardless of semaphore
// ownership. It is intended as a debugging aid only.
//
// The routine is particularly useful when a task dies while holding some
// mutual-exclusion semaphore, because the semaphore can be resurrected. The
// routine gives the semaphore to the next task in the pend queue, or makes
// the semaphore full if no tasks are pending. In effect, execution
// continues as if the task owning the semaphore had actually given the
// semaphore.
//
// CAVEATS
// Use this routine should only as a debugging aid, when the condition of
// the semaphore is known.
//
// RETURNS: OK.
//
// SEE ALSO: VXWSem::give()
STATUS giveForce ()
{
return semMGiveForce (sem_);
}
protected:
VXWMSem ()
{
}
VXWMSem (const VXWMSem &)
{
}
VXWMSem & operator = (const VXWMSem &)
{
return *this;
}
};
#endif /* ifndef vxwSemLib_h */