CPrioritySemaphore Documentation

CPrioritySemaphore

A C++ class that provides a Win32 semaphore with priority levels.
by
Thomas Becker
thomas@styleadvisor.com


Contents

  1. Copyright and Permission Notice
  2. Overview of CPrioritySemaphore
  3. CPrioritySemaphore Public Members

Copyright and Permission Notice

COPYRIGHT (C) 1998 Thomas Becker

PERMISSION NOTICE:

PERMISSION TO USE, COPY, MODIFY, AND DISTRIBUTE THIS SOFTWARE FOR ANY PURPOSE AND WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT ALL COPIES ARE ACCOMPANIED BY THE COMPLETE MACHINE-READABLE SOURCE CODE, ALL MODIFIED FILES CARRY PROMINENT NOTICES AS TO WHEN AND BY WHOM THEY WERE MODIFIED, THE ABOVE COPYRIGHT NOTICE, THIS PERMISSION NOTICE AND THE NO-WARRANTY NOTICE BELOW APPEAR IN ALL SOURCE FILES AND IN ALL SUPPORTING DOCUMENTATION.

NO-WARRANTY NOTICE:

THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF MERCHANTABILITY ARE HEREBY DISCLAIMED.

Overview of CPrioritySemaphore

class CPrioritySemaphore

The class CPrioritySemaphore wraps a Win32 semaphore with the additional option to specify a priority when waiting on the semaphore. The interface is modelled after the Win32 functions that operate on handles to semaphores.

After construction, a CPrioritySemaphore object must be properly intialized with the Create() member function. In addition to the initial and maximal count of the semaphore, the caller must specify the number of priority levels that the semaphore object will support.

The Release() member function increments the count of the semphore object just like the Win32 function ReleaseSemaphore().

The additional functionality of the priority semaphore is provided by the Wait() member function. In addition to the wait timeout, which can be a finite number of milliseconds or the Win32 constant INFINITE, Wait() takes as an argument the desired priority level, where priorities range from zero to one less than the number of priority levels. When the current count of the semaphore object is greater than zero, the object behaves exactly like a Win32 semaphore: the wait operation completes immediately, and the current count of the semaphore decreases by one. When the count has dropped down to zero, Wait() will block just like Win32's WaitForSingleObjects() would. However, when more than one thread is blocking on a call to Wait() and the semaphore object becomes signalled, i.e., some thread calls the Release() member function, the semaphore will now release the waiting threads in the order of their wait priorities. More precisely, threads are released according to the round robin scheme: As long as there are threads waiting with priority level x, no thread on a priority level less than x can be released, regardless of the order in which the threads arrived.

CPrioritySemaphore provides a function GetNumWaitingThreads() which returns the current number of threads waiting with a given priority. This function must not be used for synchronization purposes. (See the documentation of GetNumWaitingThreads() for an explanation.) The purpose of this function is to help avoid backlogs of threads that are blocking on Wait() with low priority levels. Before an application accepts clients with higher priority levels, it may wish to check the backlog of threads with lower priorities and reject the client with higher priority if that backlog exceeds an acceptable limit.

CPrioritySemaphore Public Members

Construction
CPrioritySemaphore Constructs a CPrioritySemaphore object.
Operations
Create Creates and initializes the priority semaphore.
Wait Blocks until the semaphore count is greater than zero and all threads waiting with higher semaphore priority have been released.
Release Releases the semaphore.
GetNumWaitingThreads Returns the total number of threads that are currently blocking on the Wait() member function. Do not use this function for synchronization purposes.

CPrioritySemaphore::CPrioritySemaphore

Default constructor.
CPrioritySemaphore::CPrioritySemaphore();

Remarks

The newly constructed object is not ready for use until the Create() member function has been called successfully.

CPrioritySemaphore::Create

Creates and initializes the semaphore.
BOOL CPrioritySemaphore::Create(
    LONG lNumPriorityLevels, // number of priority levels
    LONG lInitialCount, // initial count
    LONG lMaximalCount // maximal count
    );

Parameters

lNumPriorityLevels
Specifies the number of priority levels that the semaphore will support. Priority levels will be indexed from 0 (lowest) to lNumPriorityLevels-1 (highest).

lInitialCount
Specifies the initial count for the semaphore object. See the documentation of the Win32 API CreateSemaphore() for more information.

lMaximalCount
Specifies the maximal count for the semaphore object. See the documentation of the Win32 API CreateSemaphore() for more information.

Return Value

If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. Failure can be due to lack of heap memory, failure of the operating system to create a semaphore or an event, or the fact that the semaphore has already been succesfully initialized. Call GetLastError() to obtain more information. After a failed call to this function, all memory that was allocated has been freed and all handles to kernel objects have been closed.

If the calling program's new handler is such that a failed new throws an exception, the function will rethrow such exceptions after cleaning up.

Remarks

This function must be called before the semaphore can be used.

CPrioritySemaphore::Wait

Blocks until either the timeout has been reached, or the semaphore becomes signalled and all threads waiting with higher wait priorities have been released.
DWORD CPrioritySemaphore::Wait(
  LONG lWaitPriority, // wait priority level
  DWORD dwMillisecondsTimeout /* = INFINITE, timeout in milliseconds

Parameters

lWaitPriority
Specifies the priority level for this wait operation. Priority levels range from 0 (lowest) to one less than the number of priority levels specified in the Create member function.

dwMilliseconds
Specifies the timeout value in milliseconds or INFINITE.

Return Value

If the function returned because the wait on the semaphore completed successfully, the return value is WAIT_OBJECT_0.
If the function returned because the timeout was reached, the function returns WAIT_TIMEOUT.
In all other cases, the function returns WAIT_FAILED. This should always be considered a fatal, non-recoverable error. Call GetLastError() for more information.

Remarks

When the semaphore is signalled, i.e., its current count is greater than 0, threads that are blocking on the Wait() function will be released according to the round robin scheme: a thread waiting with priority level x can be released only if the semaphore is signalled and there is no thread waiting blocking with a priority level greater than x.

Note that this can lead to "starving" of threads. If there are threads blocking with priority level x, and they keep arriving at a rate that is greater than or equal to the rate at which they are released, then no thread that is waiting with a priority level less than x can be released.


BOOL CPrioritySemaphore::Release

Increases the current count of the semaphore.
void CPrioritySemaphore::Release(
  LONG lReleaseCount, // amount to add to the current count
  LPLONG lpPreviousCount) // memory location to receive previous count
  );

Parameters

lReleaseCount
Specifies the number by which the semaphore object’s current count is to be increased. See the Win32 API ReleaseSemaphore() for more information.

lpPreviousCount
Specifies the memory location to receive the previous count of the semaphore.

Return Value

If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. Call GetLastError() to obtain more information.

Remarks

This function is very similiar to the Win32 API ReleaseSemaphore(). See the Win32 documentation for more information.

int CPrioritySemaphore::GetNumWaitingThreads

Returns the number of threads that are currently waiting with a specified priority level. Do not use this function for synchronization purposes.
void CPrioritySemaphore::GetNumWaitingThreads(
  LONG lWaitPriority // priority level to check
  );

Parameters

lWaitPriority
Specifies the priority level for which the number of waiting threads is requested.

Return Value

The return value is the number of threads that are currently blocking on the Wait() member function with the specified priority level.

Remarks

Do not use this function for synchronization purposes. By the time you look at the return level, the number of waiting threads may have changed dramatically due to thread preemption. For example, a dangerously flawed scenario would be as follows: Your main thread has stopped creating new threads that wait on the semaphore. A call to GetNumWaitingThreads() returns 0. You conclude that no more threads will emerge from waiting on the semaphore with this priority level. This is of course an invalid conclusion, because a thread may have been preempted just around the time it entered the semaphore's Wait() function. Although you are not creating any more threads and the number of currently waiting threads is zero, there is still a thread out there that is going to aquire the semaphore.

The purpose of this function is to help avoid backlogs of threads that are blocking on Wait() with low priority levels. Before an application accepts clients with higher priority levels, it may wish to check the backlog of threads with lower priorities and reject the client with higher priority if that backlog exceeds an acceptable limit.