CPGuide - Semaphores: Difference between revisions
No edit summary |
m culled |
||
Line 2: | Line 2: | ||
{{CPGuide}} | {{CPGuide}} | ||
Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Semaphores are one of three forms of interprocess communication (IPC), the other forms of IPC being pipes and queues. | Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Semaphores are one of three forms of interprocess communication (IPC), the other forms of IPC being pipes and queues. | ||
This chapter describes how to create and use semaphores. Semaphores enable an application to signal the completion of tasks and control access to resources that are shared between more than one thread or process. | This chapter describes how to create and use semaphores. Semaphores enable an application to signal the completion of tasks and control access to resources that are shared between more than one thread or process. | ||
The following topics are related to the information in this chapter: | |||
*Memory (shared memory) | |||
*Program execution and control | |||
*Pipes | |||
*Queues | |||
==About Semaphores== | ==About Semaphores== | ||
Semaphores signal the beginning or ending of an operation and provide mutually exclusive ownership of resources. Typically, semaphores are used to prevent more than one process or thread within a process from accessing a resource, such as shared memory, at the same time | Semaphores signal the beginning or ending of an operation and provide mutually exclusive ownership of resources. Typically, semaphores are used to prevent more than one process or thread within a process from accessing a resource, such as shared memory, at the same time. | ||
Semaphores are defined by OS/2 and reside in an internal memory buffer. They are divided into three types, according to the functionality they provide: | |||
*Event semaphores enable a thread to notify waiting threads that an event has occurred. The waiting threads then resume execution, performing operations that are dependent on the completion of the signaled event. | |||
*Mutual exclusion (mutex) semaphores enable threads to serialize their access to shared resources. That is, ownership of a mutex semaphore is used by cooperating threads as a prerequisite for performing operations on a resource. (Threads cooperate by using the mutex semaphore functions to ensure that access to the resource is mutually exclusive.) | |||
*Multiple wait (muxwait) semaphores enable threads to wait either for multiple events to occur, or for multiple resources to become available. Alternatively, a flag can be set so that a thread waits for any one of multiple events to occur, or for any one of multiple resources to become available. | |||
===Event Semaphores=== | ===Event Semaphores=== | ||
An event semaphore provides a signaling mechanism among threads or processes, ensuring that events occur in the desired sequence. Event semaphores are used by one thread to signal other threads that an event has occurred. An application can use this type of semaphore to block a thread or process until the event has occurred. | An event semaphore provides a signaling mechanism among threads or processes, ensuring that events occur in the desired sequence. Event semaphores are used by one thread to signal other threads that an event has occurred. An application can use this type of semaphore to block a thread or process until the event has occurred. | ||
An event semaphore has two states, reset and posted. When an event semaphore is in the reset state, OS/2 blocks any thread or process that is waiting on the semaphore. When an event semaphore is in the posted state, all threads or processes waiting on the semaphore resume execution. | An event semaphore has two states, reset and posted. When an event semaphore is in the reset state, OS/2 blocks any thread or process that is waiting on the semaphore. When an event semaphore is in the posted state, all threads or processes waiting on the semaphore resume execution. | ||
For example, assume thread 1 is allocating a shared memory object and threads 2 and 3 must wait for the memory to be allocated before they attempt to examine its contents. Before thread 1 allocates the memory, it creates an event semaphore, specifying the initial state of the semaphore as reset. (If the event semaphore has already been created, thread 1 simply resets the semaphore.) Threads 2 and 3 use DosWaitEventSem to wait for the semaphore to signal that the event, in this case the allocation and preparation of the shared memory object, has been completed. Because the semaphore was reset by thread 1, threads 2 and 3 are blocked when they call DosWaitEventSem. After thread 1 has finished allocating and placing data in the shared memory object, it signals the completion of its task by posting the event semaphore. The posting of the event semaphore unblocks threads 2 and 3, enabling them to resume execution. They can then proceed to examine the contents of the allocated memory. | For example, assume thread 1 is allocating a shared memory object and threads 2 and 3 must wait for the memory to be allocated before they attempt to examine its contents. Before thread 1 allocates the memory, it creates an event semaphore, specifying the initial state of the semaphore as reset. (If the event semaphore has already been created, thread 1 simply resets the semaphore.) Threads 2 and 3 use DosWaitEventSem to wait for the semaphore to signal that the event, in this case the allocation and preparation of the shared memory object, has been completed. Because the semaphore was reset by thread 1, threads 2 and 3 are blocked when they call DosWaitEventSem. After thread 1 has finished allocating and placing data in the shared memory object, it signals the completion of its task by posting the event semaphore. The posting of the event semaphore unblocks threads 2 and 3, enabling them to resume execution. They can then proceed to examine the contents of the allocated memory. | ||
In the example above, one thread controls the resetting and posting of the event semaphore, while other threads merely wait on the semaphore. Another approach could be for an application or thread to reset an event semaphore, then block itself on that semaphore. At a later time, another application or thread would post the event semaphore, unblocking the first thread. | In the example above, one thread controls the resetting and posting of the event semaphore, while other threads merely wait on the semaphore. Another approach could be for an application or thread to reset an event semaphore, then block itself on that semaphore. At a later time, another application or thread would post the event semaphore, unblocking the first thread. | ||
===Mutual Exclusion (Mutex) Semaphores=== | ===Mutual Exclusion (Mutex) Semaphores=== | ||
A mutual exclusion (mutex) semaphore protects resources (such as files, data in memory, and peripheral devices) from simultaneous access by several processes. Mutex semaphores enable threads to serialize their access to resources. It does so by preventing the processes from concurrently executing the sections of code through which access is made. These sections of code are called critical sections. For example, a mutex semaphore could be used to prevent two or more threads from simultaneously writing to the same file on a disk. | A mutual exclusion (mutex) semaphore protects resources (such as files, data in memory, and peripheral devices) from simultaneous access by several processes. Mutex semaphores enable threads to serialize their access to resources. It does so by preventing the processes from concurrently executing the sections of code through which access is made. These sections of code are called critical sections. For example, a mutex semaphore could be used to prevent two or more threads from simultaneously writing to the same file on a disk. | ||
Before a thread can execute a mutex-protected critical section, it must request and receive ownership of the mutex semaphore. Only the thread that has gained ownership of the mutex semaphore is permitted to perform operations on the protected resource. Only one thread at a time can own the mutex semaphore, and the owner thread retains ownership until it finishes executing its critical section. When finished, the owner thread releases the mutex semaphore, enabling another thread to become the owner. | Before a thread can execute a mutex-protected critical section, it must request and receive ownership of the mutex semaphore. Only the thread that has gained ownership of the mutex semaphore is permitted to perform operations on the protected resource. Only one thread at a time can own the mutex semaphore, and the owner thread retains ownership until it finishes executing its critical section. When finished, the owner thread releases the mutex semaphore, enabling another thread to become the owner. | ||
When a thread requests ownership of a mutex semaphore that is already owned, OS/2 blocks the thread. When more than one thread requests ownership of the same semaphore, OS/2 queues the requests and grants subsequent ownership based on the thread's priority and the order in which the requests were received. | When a thread requests ownership of a mutex semaphore that is already owned, OS/2 blocks the thread. When more than one thread requests ownership of the same semaphore, OS/2 queues the requests and grants subsequent ownership based on the thread's priority and the order in which the requests were received. | ||
If more than one thread is blocked on a DosRequestMutexSem request, then ownership is given to the thread that has the highest priority level. If more than one of the waiting threads have the same priority level, then FIFO ordering is used to determine which thread is unblocked and given ownership of the semaphore. | If more than one thread is blocked on a DosRequestMutexSem request, then ownership is given to the thread that has the highest priority level. If more than one of the waiting threads have the same priority level, then FIFO ordering is used to determine which thread is unblocked and given ownership of the semaphore. | ||
For example, both thread 1 and thread 2 must write information to the same disk file. Thread 1 claims ownership of an agreed-upon mutex semaphore and starts writing its information to the file. If thread 2 also requests ownership of the semaphore, it will be blocked. When thread 1 has finished writing to the file, it releases the semaphore. OS/2 then unblocks thread 2 and designates it as the new owner of the semaphore so that it can write to the file. | For example, both thread 1 and thread 2 must write information to the same disk file. Thread 1 claims ownership of an agreed-upon mutex semaphore and starts writing its information to the file. If thread 2 also requests ownership of the semaphore, it will be blocked. When thread 1 has finished writing to the file, it releases the semaphore. OS/2 then unblocks thread 2 and designates it as the new owner of the semaphore so that it can write to the file. | ||
During process termination, after delivery of process-termination exceptions and unwind exceptions, if any threads in the process aside from Thread 1 (the main thread) own a mutex semaphore, ownership of the semaphore (and therefore, the shared resource) passes to Thread 1. This gives Thread 1 a last chance to clean up the semaphore and the shared resource before the process ends. If Thread 1 ends without releasing the semaphore, all threads that are currently waiting on ownership of the semaphore will be unblocked with the SEM_OWNER_DIED return code. Any thread that attempts to open it or request ownership of the semaphore will receive a SEM_OWNER_DIED return code. | During process termination, after delivery of process-termination exceptions and unwind exceptions, if any threads in the process aside from Thread 1 (the main thread) own a mutex semaphore, ownership of the semaphore (and therefore, the shared resource) passes to Thread 1. This gives Thread 1 a last chance to clean up the semaphore and the shared resource before the process ends. If Thread 1 ends without releasing the semaphore, all threads that are currently waiting on ownership of the semaphore will be unblocked with the SEM_OWNER_DIED return code. Any thread that attempts to open it or request ownership of the semaphore will receive a SEM_OWNER_DIED return code. | ||
The recommended way to clean up semaphores, and other resources, is for each thread, especially Thread 1, to have an exception handler to handle clean up during process termination (the XCPT_PROCESS_TERMINATE or XCPT_ASYNC_PROCESS_TERMINATE exceptions). When it is not possible to register an exception handler for a thread, (a DLL, for example, must de-register its exception handlers when it returns control to the thread that called it), you should add a clean up routine to the exit list of the process. | The recommended way to clean up semaphores, and other resources, is for each thread, especially Thread 1, to have an exception handler to handle clean up during process termination (the XCPT_PROCESS_TERMINATE or XCPT_ASYNC_PROCESS_TERMINATE exceptions). When it is not possible to register an exception handler for a thread, (a DLL, for example, must de-register its exception handlers when it returns control to the thread that called it), you should add a clean up routine to the exit list of the process. | ||
===Multiple Wait (Muxwait) Semaphores=== | ===Multiple Wait (Muxwait) Semaphores=== | ||
A multiple wait (muxwait) semaphore enables a thread to wait on several event or mutex semaphores simultaneously. A muxwait semaphore is a compound semaphore that consists of a list of up to 64 event semaphores or mutex semaphores (the two types cannot be mixed). | A multiple wait (muxwait) semaphore enables a thread to wait on several event or mutex semaphores simultaneously. A muxwait semaphore is a compound semaphore that consists of a list of up to 64 event semaphores or mutex semaphores (the two types cannot be mixed). | ||
*Threads can wait for all of the mutex semaphores to be released, or for all of the event semaphores to be posted. | A flag is set when the muxwait semaphore is created to enable threads to use the semaphore in either of two ways: | ||
*Threads can wait for any one of the mutex semaphores in the list to be released, or for any one of the event semaphores in the list to be posted. | *Threads can wait for all of the mutex semaphores to be released, or for all of the event semaphores to be posted. | ||
*Threads can wait for any one of the mutex semaphores in the list to be released, or for any one of the event semaphores in the list to be posted. | |||
Depending on the value of the flag, a muxwait semaphore is said to have cleared when either any or all of the semaphores in the muxwait list have been posted or released. | |||
For example, suppose a thread requires access to several regions of shared memory at the same time. OS/2 blocks the thread until the thread acquires ownership of all the mutex semaphores protecting the shared regions. The thread can then access all the memory regions. Meanwhile, OS/2 prevents access by other threads. | |||
===Named and Anonymous Semaphores=== | ===Named and Anonymous Semaphores=== | ||
A semaphore can be either named or anonymous. A named semaphore is always shared; that is, it is always available to any process that knows the name. An anonymous semaphore can be either private to a process or shared among processes, depending on whether the application includes the DC_SEM_SHARED flag in the function that creates the semaphore. A semaphore intended for use solely among threads of the same process can be anonymous and private. | A semaphore can be either named or anonymous. A named semaphore is always shared; that is, it is always available to any process that knows the name. An anonymous semaphore can be either private to a process or shared among processes, depending on whether the application includes the DC_SEM_SHARED flag in the function that creates the semaphore. A semaphore intended for use solely among threads of the same process can be anonymous and private. | ||
OS/2 creates a named semaphore when an application specifies a name in the function that creates the semaphore. The name must have the following form: | |||
\SEM32\SemName | |||
The "\SEM32\" is required, though it need not be uppercase. The semaphore name must conform to the rules for OS/2 file names, although no actual file is created for the semaphore. If the application does not specify a name in the function that creates the semaphore, OS/2 creates an anonymous semaphore. | |||
OS/2 permits a system-wide maximum of 65536 (64K) shared semaphores. In addition, each process can use up to 65536 (64K) private semaphores. | |||
OS/2 | A shared muxwait semaphore must contain either all shared event semaphores or all shared mutex semaphores. However, a private muxwait semaphore can contain a combination of shared and private event or mutex semaphores. OS/2 generates a unique handle when it creates a semaphore. Processes must obtain this handle before they can access the semaphore. A semaphore's handle is always available to the process that created the semaphore. A process can obtain the handle of a named semaphore created in another process by using the appropriate semaphore-opening function. A process that requires access to an anonymous shared semaphore that was created in another process must obtain the handle of the semaphore through some other form of interprocess communication, such as a pipe or a queue. | ||
===Semaphore Management=== | ===Semaphore Management=== | ||
After one process creates a semaphore, threads in other processes must open the semaphore before they can access it. (Creating a semaphore automatically opens it for the creating process.) The open operation ensures that the process is a valid user of the semaphore. OS/2 keeps track of the number of open operations that each process performs on a semaphore. A process can have up to 65535 (64K - 1) open operations performed on a semaphore at any one time. | After one process creates a semaphore, threads in other processes must open the semaphore before they can access it. (Creating a semaphore automatically opens it for the creating process.) The open operation ensures that the process is a valid user of the semaphore. OS/2 keeps track of the number of open operations that each process performs on a semaphore. A process can have up to 65535 (64K - 1) open operations performed on a semaphore at any one time. | ||
If a process finishes using a semaphore and will not use it again, the process should close the semaphore so that OS/2 can free the memory the semaphore is using. OS/2 returns the ERROR_SEM_BUSY error value if a thread tries to close a semaphore that has another thread in the same process still waiting for it. | If a process finishes using a semaphore and will not use it again, the process should close the semaphore so that OS/2 can free the memory the semaphore is using. OS/2 returns the ERROR_SEM_BUSY error value if a thread tries to close a semaphore that has another thread in the same process still waiting for it. | ||
If a process terminates with open semaphores, OS/2 automatically closes the semaphores for that process. | If a process terminates with open semaphores, OS/2 automatically closes the semaphores for that process. | ||
Semaphores reside in a memory buffer rather than a disk file. Therefore, when the last process that has a semaphore open exits or closes that semaphore, OS/2 frees the associated handle or name. | Semaphores reside in a memory buffer rather than a disk file. Therefore, when the last process that has a semaphore open exits or closes that semaphore, OS/2 frees the associated handle or name. | ||
When an application calls a function that causes a thread to wait on a semaphore, the application can specify the amount of time for the thread to wait. When the interval elapses without the semaphore being posted or released, the function returns the ERROR_TIMEOUT error value and the thread continues running. The application can provide a specific time-out value in milliseconds, or it can specify either the SEM_INDEFINITE_WAIT or the SEM_IMMEDIATE_RETURN flag. If a thread is interrupted while it is waiting on a semaphore, the ERROR_INTERRUPT error value is returned to the caller. | When an application calls a function that causes a thread to wait on a semaphore, the application can specify the amount of time for the thread to wait. When the interval elapses without the semaphore being posted or released, the function returns the ERROR_TIMEOUT error value and the thread continues running. The application can provide a specific time-out value in milliseconds, or it can specify either the SEM_INDEFINITE_WAIT or the SEM_IMMEDIATE_RETURN flag. If a thread is interrupted while it is waiting on a semaphore, the ERROR_INTERRUPT error value is returned to the caller. | ||
==Using Event Semaphores== | ==Using Event Semaphores== | ||
An application can use an event semaphore to trigger execution of other processes. This is useful if, for example, one process provides data to many other processes. Using an event semaphore frees the other process from the trouble of polling to determine when new data is available | An application can use an event semaphore to trigger execution of other processes. This is useful if, for example, one process provides data to many other processes. Using an event semaphore frees the other process from the trouble of polling to determine when new data is available. | ||
;Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. | |||
===Creating an Event Semaphore=== | ===Creating an Event Semaphore=== | ||
Processes create an event semaphore by using DosCreateEventSem. The process that controls the event or resource is usually the one that creates the semaphore, but it does not have to be | Processes create an event semaphore by using DosCreateEventSem. The process that controls the event or resource is usually the one that creates the semaphore, but it does not have to be. | ||
Threads in the process that creates the semaphore do not have to open the semaphore before using it. DosCreateEventSem obtains access to the semaphore for the calling process and its threads. Threads in other processes must call DosOpenEventSem to open the semaphore before they can use it. | |||
In the following code fragment, the controlling process creates a named event semaphore and posts the semaphore after writing data to a shared file: | Event semaphores can be defined as either private or shared: | ||
*Private semaphores are always unnamed and are therefore always identified by their handles. They can be used only by threads within a single process. | |||
*Shared semaphores can be either named or unnamed. If named, they can be opened using either the name or the handle. The handle returned by DosOpenEventSem is then used to identify the semaphore for all other functions. Semaphore names must include the prefix \SEM32\ and must conform to file system naming conventions. Shared semaphores can be used by threads in multiple processes. | |||
In the following code fragment, the controlling process creates a named event semaphore and posts the semaphore after writing data to a shared file: | |||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
HEV hevWriteEvent; | |||
DosCreateEventSem("\\sem32\\wrtevent", /* Named-shared semaphore */ | |||
&hevWriteEvent, 0, FALSE); /* Initially reset */ | |||
. | |||
. | |||
/* Write data to shared file. */ | |||
. | |||
. | |||
DosPostEventSem(hevWriteEvent); /* Posts the event */ | |||
. | |||
. /* Continue execution. */ | |||
. | |||
</pre> | </pre> | ||
There is a system-wide limit of 65536 (64K) shared semaphores (including mutex, event, and muxwait semaphores); in addition, each process can have up to 65536 (64K) private semaphores. | |||
When an event semaphore is created, a flag is used to specify the initial state of the event semaphore, either reset or posted. If the initial state is reset, a thread that calls DosWaitEventSem will be blocked until a process that has access to the semaphore uses DosPostEventSem to post the event semaphore. If the initial state is posted, then a thread that calls DosWaitEventSem will return immediately to continue its execution. If the thread calling DosWaitEventSem is not in the process that created the semaphore, the thread must open the semaphore with DosOpenEventSem before calling DosWaitEventSem. | |||
OS/2 maintains a usage count for each semaphore. DosCreateEventSem initializes the usage count to 1. Thereafter, each call to DosOpenEventSem increments the count, and each call to DosCloseEventSem decrements it. | |||
===Opening an Event Semaphore=== | ===Opening an Event Semaphore=== | ||
When a process creates an event semaphore, all of the threads that belong to the process have immediate access to the semaphore | When a process creates an event semaphore, all of the threads that belong to the process have immediate access to the semaphore. | ||
Threads in other processes must open the semaphore by calling DosOpenEventSem before they can use the semaphore in any other event semaphore function. | |||
The following code fragment shows how processes can open an event semaphore that was created in a different process and then wait for the event to be posted: | |||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
HEV hevEventHandle = 0; /* Must be 0 because we are opening */ | |||
/* the semaphore by name */ | |||
DosOpenEventSem("\\sem32\\wrtevent", &hevEventHandle); | |||
DosWaitEventSem(hevEventHandle, | |||
SEM_INDEFINITE_WAIT); /* Waits until event is posted */ | |||
. | |||
. /* Read from file when event is posted. */ | |||
. | |||
</pre> | </pre> | ||
Applications can open an event semaphore by name or by handle. If the name is used to open the semaphore, as in the code fragment above, the handle parameter must be 0. If the handle is used to open the semaphore, the name parameter must be NULL. | |||
Access to semaphores is on a per-process basis. Therefore, a semaphore that has been opened by one thread in a process is open to all other threads in that process as well. | |||
DosOpenEventSem merely provides access to an event semaphore. In order to wait for an event semaphore to be posted, a thread must call DosWaitEventSem. In order to post or reset an open event semaphore, a thread uses DosPostEventSem or DosResetEventSem respectively. | |||
When a process no longer requires access to an event semaphore, it closes the semaphore by calling DosCloseEventSem. If a process ends without closing an open semaphore, the semaphore is closed by OS/2. | |||
Each call to DosOpenEventSem increments the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is decremented by each call to DosCloseEventSem. When the usage count reaches 0, the semaphore is deleted by OS/2. | |||
Calls to DosOpenEventSem and DosCloseEventSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. | |||
===Closing an Event Semaphore=== | ===Closing an Event Semaphore=== | ||
When a process no longer requires access to an event semaphore, it closes the semaphore by calling DosCloseEventSem | When a process no longer requires access to an event semaphore, it closes the semaphore by calling DosCloseEventSem. | ||
The following code fragment closes an event semaphore. Assume that the handle of the semaphore has been placed into HEV already. | |||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HEV hev; /* Event semaphore handle */ | |||
APIRET ulrc; /* Return code */ | |||
ulrc = DosCloseEventSem(hev); | |||
if (ulrc != 0) { | |||
printf("DosCloseEventSem error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
Calls to DosOpenEventSem and DosCloseEventSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. | |||
If a process ends without closing an open semaphore, the semaphore is closed by OS/2. | |||
Each call to DosCloseEventSem decrements the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is incremented by each call to DosOpenEventSem. When the usage count reaches 0, the semaphore is deleted from OS/2. The call to DosCloseEventSem that decrements the usage count to 0 and causes the semaphore to be deleted is referred to as the final close. If a thread attempts to perform the final close for a semaphore while another thread in the same process is still waiting for it, ERROR_SEM_BUSY is returned. | |||
===Resetting an Event Semaphore=== | ===Resetting an Event Semaphore=== | ||
DosResetEventSem resets an event semaphore if it is not already reset, and returns the number of times the semaphore was posted since it was last reset. All threads that subsequently call DosWaitEventSem for this semaphore will be blocked | DosResetEventSem resets an event semaphore if it is not already reset, and returns the number of times the semaphore was posted since it was last reset. All threads that subsequently call DosWaitEventSem for this semaphore will be blocked. | ||
Any thread belonging to the process that created the event semaphore can change the state of the semaphore to reset by calling DosResetEventSem. Threads in other processes can also call DosResetEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem. | |||
When an event semaphore is in the reset state, any thread that calls DosWaitEventSem to wait for the semaphore will be blocked. When the event semaphore is posted, all of the threads that are waiting for the semaphore are released to continue execution. | |||
The following code fragment resets an event semaphore. Assume that the handle of the semaphore has been placed into HEV already. | |||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HEV hev; /* Event semaphore handle */ | |||
ULONG ulPostCt; /* Post count for the event semaphore (returned) */ | |||
APIRET ulrc; /* Return code */ | |||
ulrc = DosResetEventSem(hev, &ulPostCt); | |||
if (ulrc != 0) { | |||
printf("DosResetEventSem error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
DosResetEventSem returns the post count of the event semaphore and resets the post count to 0. The post count is the number of times the semaphore has been posted (using DosPostEventSem) since the last time the semaphore was in the reset state. (An event semaphore can be reset when it is created, as well as by calling DosResetEventSem.) The post count can also be obtained by calling DosQueryEventSem. | |||
If the event semaphore is already reset when DosResetEventSem is called, ERROR_ALREADY_RESET is returned, along with a post count of 0. The semaphore is not reset a second time. | |||
===Posting an Event Semaphore=== | ===Posting an Event Semaphore=== | ||
DosPostEventSem posts the semaphore, if it is not already posted, and increments the post count. All threads that have called DosWaitEventSem for this semaphore are unblocked and resume execution. Threads that call DosWaitEventSem after the event semaphore has been posted and before the next time it is reset, will return immediately from a call to DosWaitEventSem and continue execution. If the semaphore is subsequently reset, threads that call DosWaitEventSem will again be blocked | DosPostEventSem posts the semaphore, if it is not already posted, and increments the post count. All threads that have called DosWaitEventSem for this semaphore are unblocked and resume execution. Threads that call DosWaitEventSem after the event semaphore has been posted and before the next time it is reset, will return immediately from a call to DosWaitEventSem and continue execution. If the semaphore is subsequently reset, threads that call DosWaitEventSem will again be blocked. | ||
Any thread in the process that created an event semaphore can post the semaphore by calling DosPostEventSem. Threads in other processes can also call DosPostEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem. | |||
The following code fragment posts a system event semaphore. Assume that the handle of the semaphore has been placed into HEV already. | |||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HEV hev; /* Event semaphore handle */ | |||
APIRET ulrc; /* Return code */ | |||
ulrc = DosPostEventSem(hev); | |||
if (ulrc != 0) { | |||
printf("DosPostEventSem error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
OS/2 maintains a post count for each event semaphore. The post count is the number of times the semaphore has been posted (with DosPostEventSem) since the last time the semaphore was in the reset state. | |||
If the event semaphore is reset when DosPostEventSem is called, the semaphore is posted and the post count is set to 1. If the event semaphore is already posted when DosPostEventSem is called, the post count is incremented, and ERROR_ALREADY_POSTED is returned to the calling thread. | |||
The post count is returned as output by DosResetEventSem; it can also be obtained by calling DosQueryEventSem. | |||
The post count is | The maximum number of times an event semaphore can be posted is 65535. The value of the post count cannot exceed 65535. If an attempt is made to exceed this number, DosPostEventSem returns ERROR_TOO_MANY_POSTS. | ||
===Waiting for an Event Semaphore=== | ===Waiting for an Event Semaphore=== | ||
Any thread in the process that created an event semaphore can wait for the semaphore to be posted by calling DosWaitEventSem. Threads in other processes can also call DosWaitEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem | Any thread in the process that created an event semaphore can wait for the semaphore to be posted by calling DosWaitEventSem. Threads in other processes can also call DosWaitEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem. | ||
If the semaphore is already posted when DosWaitEventSem is called, the function returns immediately, and the thread continues to run. Otherwise, the thread is blocked until the semaphore is posted. | |||
The following code fragment causes the calling thread to wait until the specified event semaphore is posted. Assume that the handle of the semaphore has been placed into HEV already. ulTimeout is the number of milliseconds that the calling thread will wait for the event semaphore to be posted. If the specified event semaphore is not posted during this time interval, the request times out. | |||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#define INCL_DOSERRORS /* error codes */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HEV hev; /* Event semaphore handle */ | |||
ULONG ulTimeout; /* Number of milliseconds to wait */ | |||
APIRET ulrc; /* Return code */ | |||
ulTimeout = 60000; /* Wait for a maximum of 1 minute */ | |||
ulrc = DosWaitEventSem(hev, | |||
ulTimeout); | |||
if (ulrc == ERROR_TIMEOUT) { | |||
printf("DosWaitEventSem call timed out"); | |||
return; | |||
} | |||
if (ulrc == ERROR_INTERRUPT) { | |||
printf("DosWaitEventSem call was interrupted"); | |||
return; | |||
} | |||
if (ulrc != 0) { | |||
printf("DosWaitEventSem error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
If the time limit specified in ulTimeout is reached before the semaphore is posted, ERROR_TIMEOUT is returned. If the waiting period is interrupted for some reason before the semaphore is posted, ERROR_INTERRUPT is returned. If SEM_IMMEDIATE_RETURN is specified for the time limit, DosWaitEventSem returns to the calling thread immediately. If SEM_INDEFINITE_WAIT is specified for the time limit, the thread waits indefinitely. | |||
Unlike multiple event semaphores in a muxwait list, which are level-triggered, single event semaphores are edge-triggered. This means that if an event semaphore is posted and then reset before a waiting thread gets a chance to run, the semaphore is considered to be posted for the rest of that thread's waiting period; the thread does not have to wait for the semaphore to be posted again. | |||
===Querying an Event Semaphore=== | ===Querying an Event Semaphore=== | ||
DosQueryEventSem returns the current post count of a semaphore. The post count is the number of times that the semaphore has been posted (with DosPostEventSem) since the last time the semaphore was reset. A count of 0 indicates that the semaphore is in the reset state; therefore, OS/2 will block any threads that call DosWaitEventSem to wait on the semaphore. | DosQueryEventSem returns the current post count of a semaphore. The post count is the number of times that the semaphore has been posted (with DosPostEventSem) since the last time the semaphore was reset. A count of 0 indicates that the semaphore is in the reset state; therefore, OS/2 will block any threads that call DosWaitEventSem to wait on the semaphore. | ||
Any thread in the process that created an event semaphore can obtain the post count for the semaphore by calling DosQueryEventSem. Threads in other processes can also call DosQueryEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem. | Any thread in the process that created an event semaphore can obtain the post count for the semaphore by calling DosQueryEventSem. Threads in other processes can also call DosQueryEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem. | ||
The following code fragment retrieves the post count for an event semaphore. Assume that the handle of the semaphore has been placed into HEV already. | The following code fragment retrieves the post count for an event semaphore. Assume that the handle of the semaphore has been placed into HEV already. | ||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HEV hev; /* Event semaphore handle */ | |||
ULONG ulPostCt; /* Current post count for the semaphore (returned) */ | |||
APIRET ulrc; /* Return code */ | |||
ulrc = DosQueryEventSem(hev, | |||
&ulPostCt); | |||
if (ulrc != 0) { | |||
printf("DosQueryEventSem error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
If the specified event semaphore does not exist, ERROR_INVALID_HANDLE is returned. | |||
If the specified event semaphore does not exist, ERROR_INVALID_HANDLE is returned. | |||
==Using Mutex Semaphores== | ==Using Mutex Semaphores== | ||
An application can use a mutual exclusion (mutex) semaphore to protect a shared resource from simultaneous access by multiple threads or processes. For example, if several processes must write to the same disk file, the mutex semaphore ensures that only one process at a time writes to the file. | An application can use a mutual exclusion (mutex) semaphore to protect a shared resource from simultaneous access by multiple threads or processes. For example, if several processes must write to the same disk file, the mutex semaphore ensures that only one process at a time writes to the file. | ||
===Creating a Mutex Semaphore=== | ===Creating a Mutex Semaphore=== | ||
Mutex semaphores are created by calling DosCreateMutexSem. This function also opens the semaphore for the calling process and its threads | Mutex semaphores are created by calling DosCreateMutexSem. This function also opens the semaphore for the calling process and its threads. | ||
When a mutex semaphore is created, a flag is set to specify the initial state of the semaphore, owned or unowned. If the semaphore is owned by a thread, other threads requesting the semaphore are blocked. If the semaphore is unowned-not owned by any thread- then any thread requesting ownership will be granted ownership immediately. | |||
If the calling thread sets the initial state to owned, it owns the semaphore as soon as OS/2 creates the semaphore and can proceed to access the resource that the semaphore was created to protect. | |||
If the semaphore is unowned, any thread in the creating process can subsequently request ownership of the semaphore by calling DosRequestMutexSem. Threads in other processes can gain ownership of the semaphore, but they must call DosOpenMutexSem to acquire access to the semaphore before they can call DosRequestMutexSem. | |||
Mutex semaphores can be defined as either private or shared. | |||
*Private semaphores are always unnamed and are therefore identified by their handles. They can be used only by threads within a single process. | |||
*Shared semaphores can be either named or unnamed. If named, they can be opened using either the name or the handle. The handle returned by DosOpenMutexSem is then used to identify the semaphore for all other functions. Semaphore names must include the prefix \SEM32\ and must conform to file system naming conventions. Shared semaphores can be used by threads in multiple processes. | |||
The following code fragment creates a mutex semaphore: | |||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
HMTX hmtxProtFile; | |||
DosCreateMutexSem("\\sem32\\ProtFile", /* Named-shared semaphore */ | |||
&hmtxProtFile, | |||
0, | |||
FALSE); /* Initially unowned */ | |||
. | |||
. /* Get data to write to shared file. */ | |||
. | |||
</pre> | </pre> | ||
There is a system-wide limit of 65536 shared semaphores (including mutex, event, and muxwait semaphores); in addition, each process can have up to 65536 private semaphores. | |||
OS/2 maintains a usage count for each semaphore. DosCreateMutexSem initializes the usage count to 1. Thereafter, each call to DosOpenMutexSem increments the count, and each call to DosCloseMutexSem decrements it. | |||
OS/2 maintains a usage count for each semaphore. DosCreateMutexSem initializes the usage count to 1. Thereafter, each call to DosOpenMutexSem increments the count, and each call to DosCloseMutexSem decrements it. | |||
===Opening a Mutex Semaphore=== | ===Opening a Mutex Semaphore=== | ||
All of the threads belonging to the process that creates a mutex semaphore have immediate access to the semaphore. Threads in other processes must request access to the semaphore by calling DosOpenMutexSem before they can use the semaphore in other mutex semaphore functions. | All of the threads belonging to the process that creates a mutex semaphore have immediate access to the semaphore. Threads in other processes must request access to the semaphore by calling DosOpenMutexSem before they can use the semaphore in other mutex semaphore functions. | ||
Access to system resources is granted on a per-process basis. Therefore, a semaphore that has been opened by one thread in a process is open to all other threads in that process as well. | Access to system resources is granted on a per-process basis. Therefore, a semaphore that has been opened by one thread in a process is open to all other threads in that process as well. | ||
DosOpenMutexSem merely provides access to a mutex semaphore. To request ownership of a mutex semaphore, a thread must call DosRequestMutexSem. | DosOpenMutexSem merely provides access to a mutex semaphore. To request ownership of a mutex semaphore, a thread must call DosRequestMutexSem. | ||
When a process no longer requires access to a mutex semaphore, it should close the semaphore by calling DosCloseMutexSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. | When a process no longer requires access to a mutex semaphore, it should close the semaphore by calling DosCloseMutexSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. | ||
Each call to DosOpenMutexSem. increments the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is decremented by each call to DosCloseMutexSem. When the usage count reaches 0, the semaphore is deleted by the system. | Each call to DosOpenMutexSem. increments the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is decremented by each call to DosCloseMutexSem. When the usage count reaches 0, the semaphore is deleted by the system. | ||
Calls to DosOpenMutexSem and DosCloseMutexSem. can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. | Calls to DosOpenMutexSem and DosCloseMutexSem. can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. | ||
If a process ends without releasing a mutex semaphore that it owns, any other thread that subsequently tries to open the semaphore will receive ERROR_SEM_OWNER_DIED. This return code indicates that the owning process ended abnormally, leaving the protected resource in an indeterminate state. However, the semaphore is still opened for the calling thread, enabling the thread to call DosQueryMutexSem to find out which process ended without releasing the semaphore. The thread can then take appropriate action concerning the semaphore and the protected resource. | If a process ends without releasing a mutex semaphore that it owns, any other thread that subsequently tries to open the semaphore will receive ERROR_SEM_OWNER_DIED. This return code indicates that the owning process ended abnormally, leaving the protected resource in an indeterminate state. However, the semaphore is still opened for the calling thread, enabling the thread to call DosQueryMutexSem to find out which process ended without releasing the semaphore. The thread can then take appropriate action concerning the semaphore and the protected resource. | ||
===Requesting a Mutex Semaphore=== | ===Requesting a Mutex Semaphore=== | ||
In order to access a shared resource, a process must own the mutex semaphore that is protecting the shared resource. Ownership is obtained by first opening the mutex semaphore with DosOpenMutexSem, then using DosRequestMutexSem to request ownership of the semaphore. If another process already owns the semaphore, the requesting process is blocked. If the semaphore is not owned, OS/2 grants ownership to the requesting process and the process can access the shared resource. When the process is finished using the shared resource, it uses DosReleaseMutexSem to relinquish its ownership of the semaphore, thereby enabling another process to gain ownership | In order to access a shared resource, a process must own the mutex semaphore that is protecting the shared resource. Ownership is obtained by first opening the mutex semaphore with DosOpenMutexSem, then using DosRequestMutexSem to request ownership of the semaphore. If another process already owns the semaphore, the requesting process is blocked. If the semaphore is not owned, OS/2 grants ownership to the requesting process and the process can access the shared resource. When the process is finished using the shared resource, it uses DosReleaseMutexSem to relinquish its ownership of the semaphore, thereby enabling another process to gain ownership. | ||
A process can gain ownership of a mutex semaphore in three ways: | |||
#The thread that creates a mutex semaphore can designate itself as the owner by setting a flag when it calls DosCreateMutexSem. | |||
#Any thread in the process that created the semaphore can request ownership by calling DosRequestMutexSem. | |||
#A thread in another process must request access to the semaphore with DosOpenMutexSem before it can call DosRequestMutexSem. | |||
Note that ownership of a mutex semaphore is given only to the requesting thread; it is not shared by other threads in the same process. | |||
If a mutex semaphore is unowned, DosRequestMutexSem sets it as owned and returns immediately to the caller. If the semaphore is already owned, the calling thread is blocked until either the owning thread calls DosReleaseMutexSem to release the semaphore, or a specified time limit is reached. | |||
The following code fragment shows how a process opens a mutex semaphore, requests it, and, after writing to the shared file, releases and closes the semaphore: | |||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
HMTX hmtxProtFile; | |||
DosOpenMutexSem("\\sem32\\ProtFile", | |||
&hmtxProtFile); /* Opens for this process */ | |||
DosRequestMutexSem(hmtxProtFile, | |||
5000); /* Returns in 5 seconds if */ | |||
. /* Ownership not obtained */ | |||
. | |||
. /* Write data to shared file */ | |||
. | |||
. | |||
DosReleaseMutexSem(hmtxProtFile); /* Releases ownership */ | |||
. | |||
. /* Continue execution */ | |||
. | |||
. | |||
DosCloseMutexSem(hmtxProtFile); /* Finished with shared file */ | |||
</pre> | </pre> | ||
If more than one thread is blocked on a DosRequestMutexSem request, the thread with the highest priority level is the first to be unblocked and given ownership of the semaphore. If more than 1 of the waiting threads have the same priority level, then FIFO ordering is used to determine which thread is unblocked and given ownership. | |||
The time-out parameter (5000 milliseconds in the example above) places a limit on the amount of time a thread blocks on a DosRequestMutexSem request. If the time limit is reached before the thread gains ownership of the semaphore, ERROR_TIMEOUT is returned. If SEM_IMMEDIATE_RETURN is specified for the time limit, DosRequestMutexSem returns without blocking the thread. The thread can then perform other operations and call DosRequestMutexSem again later if it still requires access to the protected resource. If SEM_INDEFINITE_WAIT is specified for the time limit, the thread waits indefinitely. If the thread is unblocked by an external event while it is waiting for the mutex semaphore (as when a No Wait I/O request has just been completed), ERROR_INTERRUPT is returned to the caller. | |||
In addition to the usage count that OS/2 maintains for all semaphores, OS/2 maintains a request count for each mutex semaphore. Each call to DosRequestMutexSem increments the count, and each call to DosReleaseMutexSem decrements it. | |||
In addition to the usage count that OS/2 maintains for all semaphores, OS/2 maintains a request count for each mutex semaphore. Each call to DosRequestMutexSem increments the count, and each call to DosReleaseMutexSem decrements it. | |||
Calls to DosRequestMutexSem and DosReleaseMutexSem can be nested, but the request count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_SEM_REQUESTS is returned. When calls to DosRequestMutexSem and DosReleaseMutexSem are nested, a call to DosReleaseMutexSem merely decrements the request count for the semaphore; the semaphore is not actually released to another thread until its request count is 0. If a process ends while it owns a mutex semaphore, all of the currently blocked DosRequestMutexSem requests, as well as any future requests for the semaphore, return ERROR_SEM_OWNER_DIED. This return code indicates that the owning process ended abnormally, leaving the protected resource in an indeterminate state. An application that receives this error should close the mutex semaphore (so that it can be deleted from OS/2), because it is no longer valid. Appropriate action should also be taken concerning the protected resource. | Calls to DosRequestMutexSem and DosReleaseMutexSem can be nested, but the request count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_SEM_REQUESTS is returned. When calls to DosRequestMutexSem and DosReleaseMutexSem are nested, a call to DosReleaseMutexSem merely decrements the request count for the semaphore; the semaphore is not actually released to another thread until its request count is 0. If a process ends while it owns a mutex semaphore, all of the currently blocked DosRequestMutexSem requests, as well as any future requests for the semaphore, return ERROR_SEM_OWNER_DIED. This return code indicates that the owning process ended abnormally, leaving the protected resource in an indeterminate state. An application that receives this error should close the mutex semaphore (so that it can be deleted from OS/2), because it is no longer valid. Appropriate action should also be taken concerning the protected resource. | ||
===Releasing a Mutex Semaphore=== | ===Releasing a Mutex Semaphore=== | ||
A thread can release ownership of a mutex semaphore by calling DosReleaseMutexSem. Each call to DosReleaseMutexSem decrements the request count that is maintained for the semaphore by OS/2. Each call to DosRequestMutexSem increments the count | A thread can release ownership of a mutex semaphore by calling DosReleaseMutexSem. Each call to DosReleaseMutexSem decrements the request count that is maintained for the semaphore by OS/2. Each call to DosRequestMutexSem increments the count. | ||
The following code fragment relinquishes ownership of a mutex semaphore. Assume that the handle of the semaphore has been placed into hmtx already. | |||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HMTX hmtx; /* Mutex semaphore handle */ | |||
APIRET ulrc; /* Return code */ | |||
ulrc = DosReleaseMutexSem(hmtx); | |||
if (ulrc != 0) { | |||
printf("DosReleaseMutexSem error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
Calls to DosRequestMutexSem and DosReleaseMutexSem can be nested, but the request count cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_SEM_REQUESTS is returned. When calls to DosRequestMutexSem and DosReleaseMutexSem are nested, a call to DosReleaseMutexSem merely decrements the request count for the semaphore; the semaphore is not actually released to another thread until its request count is 0. | |||
Calls to DosRequestMutexSem and DosReleaseMutexSem can be nested, but the request count cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_SEM_REQUESTS is returned. When calls to DosRequestMutexSem and DosReleaseMutexSem are nested, a call to DosReleaseMutexSem merely decrements the request count for the semaphore; the semaphore is not actually released to another thread until its request count is 0. | |||
===Closing a Mutex Semaphore=== | ===Closing a Mutex Semaphore=== | ||
When a process no longer requires access to a mutex semaphore, it can close the semaphore by calling DosCloseMutexSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. | When a process no longer requires access to a mutex semaphore, it can close the semaphore by calling DosCloseMutexSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. | ||
The following code fragment closes a mutex semaphore. Assume that the handle of the semaphore has been placed into hmtx already. | The following code fragment closes a mutex semaphore. Assume that the handle of the semaphore has been placed into hmtx already. | ||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HMTX hmtx; /* Mutex semaphore handle */ | |||
APIRET ulrc; /* Return code */ | |||
ulrc = DosCloseMutexSem(hmtx); | |||
if (ulrc != 0) { | |||
printf("DosCloseMutexSem error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
Each call to DosCloseMutexSem decrements the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is incremented by each call to DosOpenMutexSem. When the usage count reaches 0, the semaphore is deleted by OS/2. | |||
The call to DosCloseMutexSem that decrements the usage count to 0 and causes the semaphore to be deleted is referred to as the final close. The final close will not succeed if either of the following conditions exists: | |||
*The semaphore is owned by another thread in the same process. | |||
*Another thread in the same process is still blocked on a DosRequestMutexSem request for the semaphore. | |||
For both conditions, ERROR_SEM_BUSY is returned. | |||
ERROR_SEM_BUSY is also returned if a thread tries to close a mutex semaphore that it still owns. The thread must first relinquish ownership of the semaphore by calling DosReleaseMutexSem. | |||
ERROR_SEM_BUSY is also returned if a thread tries to close a mutex semaphore that it still owns. The thread must first relinquish ownership of the semaphore by calling DosReleaseMutexSem. | |||
Calls to DosOpenMutexSem and DosCloseMutexSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. | Calls to DosOpenMutexSem and DosCloseMutexSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. | ||
===Querying a Mutex Semaphore=== | ===Querying a Mutex Semaphore=== | ||
An application can use DosQueryMutexSem to determine the current owner of a mutex semaphore and to obtain a count of the number of requests on it. If the mutex semaphore is not owned, the request count is 0. | An application can use DosQueryMutexSem to determine the current owner of a mutex semaphore and to obtain a count of the number of requests on it. If the mutex semaphore is not owned, the request count is 0. | ||
Any thread in the process that created a mutex semaphore can obtain information about the semaphore by calling DosQueryMutexSem. Threads in other processes can also call DosQueryMutexSem, but they must first gain access to the semaphore by calling DosOpenMutexSem. | Any thread in the process that created a mutex semaphore can obtain information about the semaphore by calling DosQueryMutexSem. Threads in other processes can also call DosQueryMutexSem, but they must first gain access to the semaphore by calling DosOpenMutexSem. | ||
If the mutex semaphore exists and is owned, DosQueryMutexSem returns the process and thread identifications of the owner, as well as the request count for the semaphore. The request count is the number of DosRequestMutexSem calls minus the number of DosReleaseMutexSem calls that have been made for the semaphore by the owning thread. | If the mutex semaphore exists and is owned, DosQueryMutexSem returns the process and thread identifications of the owner, as well as the request count for the semaphore. The request count is the number of DosRequestMutexSem calls minus the number of DosReleaseMutexSem calls that have been made for the semaphore by the owning thread. | ||
If DosQueryMutexSem returns a request count of 0, the mutex semaphore is unowned. | If DosQueryMutexSem returns a request count of 0, the mutex semaphore is unowned. | ||
If the owning process ended without calling DosCloseMutexSem, then ERROR_SEM_OWNER_DIED is returned, and the output parameters contain information about the ended owning process. | If the owning process ended without calling DosCloseMutexSem, then ERROR_SEM_OWNER_DIED is returned, and the output parameters contain information about the ended owning process. | ||
==Using Muxwait Semaphores== | ==Using Muxwait Semaphores== | ||
A process that requires exclusive use of several shared resources at once can use a multiple wait (muxwait) semaphore to obtain ownership of all the mutex semaphores protecting the shared resources. A process can also use a muxwait semaphore to wait on a group of event semaphores so that the process continues running whenever events of interest occur. | A process that requires exclusive use of several shared resources at once can use a multiple wait (muxwait) semaphore to obtain ownership of all the mutex semaphores protecting the shared resources. A process can also use a muxwait semaphore to wait on a group of event semaphores so that the process continues running whenever events of interest occur. | ||
A muxwait semaphore can refer to up to 64 event or mutex semaphores. An application cannot refer to event and mutex semaphores in a single muxwait semaphore, or include a muxwait semaphore in another muxwait semaphore. | A muxwait semaphore can refer to up to 64 event or mutex semaphores. An application cannot refer to event and mutex semaphores in a single muxwait semaphore, or include a muxwait semaphore in another muxwait semaphore. | ||
===Creating a Muxwait Semaphore=== | ===Creating a Muxwait Semaphore=== | ||
DosCreateMuxWaitSem is used to create muxwait semaphores. This function also opens (obtains access to) the semaphore for the calling process and its threads. Threads in other processes must call DosOpenMuxWaitSem to open the semaphore before they can use it in any other muxwait semaphore function | DosCreateMuxWaitSem is used to create muxwait semaphores. This function also opens (obtains access to) the semaphore for the calling process and its threads. Threads in other processes must call DosOpenMuxWaitSem to open the semaphore before they can use it in any other muxwait semaphore function. | ||
All the semaphores in the muxwait list must be created and opened before the muxwait list can be created. | |||
The following code fragment creates five event semaphores and a corresponding array of semaphore records. The array is used to specify the semaphores included in the muxwait semaphore in the subsequent call to DosCreateMuxWaitSem. | |||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* DOS semaphore values */ | #define INCL_DOSSEMAPHORES /* DOS semaphore values */ | ||
#define INCL_DOSERRORS /* DOS error values */ | #define INCL_DOSERRORS /* DOS error values */ | ||
Line 584: | Line 539: | ||
return NO_ERROR; | return NO_ERROR; | ||
} | } | ||
</pre> | </pre> | ||
Muxwait semaphores can be defined as either private or shared: | |||
*Private semaphores are always unnamed and are therefore always identified by their handles. They can be used only by threads within a single process. | |||
*Shared semaphores can be either named or unnamed. If named, they can be opened using either the name or the handle. The handle returned by DosOpenMuxWaitSem is then used to identify the semaphore for all other functions. Semaphore names must include the prefix \SEM32\ and must conform to file system naming conventions. Shared semaphores can be used by threads in multiple processes. | |||
There is a system-wide limit of 65536 (64K) shared semaphores (including mutex, event, and muxwait semaphores); in addition, each process can have up to 65536 (64K) private semaphores. | |||
The following conditions apply to the kinds of semaphores that can be included in a muxwait-semaphore list: | |||
*The list must contain either mutex semaphores or event semaphores. It cannot contain both at the same time and it cannot contain other muxwait semaphores. | |||
*If the muxwait semaphore is shared, then all the semaphores in the list must also be shared. | |||
*If the muxwait semaphore is private, then the semaphores in its list can be either private or shared. | |||
If any of these conditions is violated, ERROR_WRONG_TYPE is returned. | |||
The following conditions apply to the kinds of semaphores that can be included in a muxwait-semaphore list: | |||
*The list must contain either mutex semaphores or event semaphores. It cannot contain both at the same time and it cannot contain other muxwait semaphores. | |||
*If the muxwait semaphore is shared, then all the semaphores in the list must also be shared. | |||
*If the muxwait semaphore is private, then the semaphores in its list can be either private or shared. | |||
If any of these conditions is violated, ERROR_WRONG_TYPE is returned. | |||
The muxwait list can contain a maximum of 64 event semaphores or mutex semaphores. If an attempt is made to exceed this maximum, ERROR_TOO_MANY_SEMAPHORES is returned. | The muxwait list can contain a maximum of 64 event semaphores or mutex semaphores. If an attempt is made to exceed this maximum, ERROR_TOO_MANY_SEMAPHORES is returned. | ||
If the owners of any of the mutex semaphores in the muxwait semaphore list have ended without releasing them, ERROR_SEM_OWNER_DIED is returned. The thread should call DosQueryMutexSem for each mutex semaphore in the muxwait-semaphore list so that it can determine which semaphores are in the Owner Died state. | If the owners of any of the mutex semaphores in the muxwait semaphore list have ended without releasing them, ERROR_SEM_OWNER_DIED is returned. The thread should call DosQueryMutexSem for each mutex semaphore in the muxwait-semaphore list so that it can determine which semaphores are in the Owner Died state. | ||
Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed must be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem. | Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed must be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem. | ||
OS/2 maintains a usage count for each semaphore. DosCreateMuxWaitSem initializes the usage count to 1. Thereafter, each call to DosOpenMuxWaitSem increments the count, and each call to DosCloseMuxWaitSem decrements it. | OS/2 maintains a usage count for each semaphore. DosCreateMuxWaitSem initializes the usage count to 1. Thereafter, each call to DosOpenMuxWaitSem increments the count, and each call to DosCloseMuxWaitSem decrements it. | ||
One parameter of this function is a pointer to an array of SEMRECORD data structures. Each data structure contains one semaphore record for each of the semaphores to be included in the muxwait semaphore. A semaphore record contains the handle and a programmer-defined identifier for that semaphore. | One parameter of this function is a pointer to an array of SEMRECORD data structures. Each data structure contains one semaphore record for each of the semaphores to be included in the muxwait semaphore. A semaphore record contains the handle and a programmer-defined identifier for that semaphore. | ||
===Opening a Muxwait Semaphore=== | ===Opening a Muxwait Semaphore=== | ||
Processes other than the semaphore-creating process must use DosOpenMuxWaitSem to gain access to the muxwait semaphore before they can use the semaphore in any other muxwait semaphore function. All of the threads that belong to the process that creates the muxwait semaphore have immediate access to the | Processes other than the semaphore-creating process must use DosOpenMuxWaitSem to gain access to the muxwait semaphore before they can use the semaphore in any other muxwait semaphore function. All of the threads that belong to the process that creates the muxwait semaphore have immediate access to the semaphore. | ||
The following code fragment opens a system muxwait semaphore. | |||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
UCHAR ucName[40]; /* Semaphore name */ | |||
HMUX hmux; /* Muxwait semaphore handle */ | |||
APIRET ulrc; /* Return code */ | |||
strcpy(ucName, | |||
"\\SEM32\\MUXWAIT1"); /* Name of the system muxwait semaphore */ | |||
ulrc = DosOpenMuxWaitSem(ucName, | |||
&hmux); | |||
if (ulrc != 0) { | |||
printf("DosOpenMuxWaitSem error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
On successful return, hmux contains the handle of the system muxwait semaphore. | |||
Opening a muxwait semaphore does not open the semaphores in its muxwait list. A process must open each of the semaphores included in a muxwait semaphore before it opens the muxwait semaphore. Otherwise, DosOpenMuxWaitSem returns the ERROR_INVALID_HANDLE error value to the calling function. | |||
Access to semaphores is on a per-process basis. Therefore, a semaphore that has been opened by one thread in a process is open to all other threads in that process as well. | |||
Access to semaphores is on a per-process basis. Therefore, a semaphore that has been opened by one thread in a process is open to all other threads in that process as well. | |||
Note that DosOpenMuxWaitSem merely provides access to a muxwait semaphore. In order to wait for a muxwait semaphore to clear, a thread must call DosWaitMuxWaitSem. | Note that DosOpenMuxWaitSem merely provides access to a muxwait semaphore. In order to wait for a muxwait semaphore to clear, a thread must call DosWaitMuxWaitSem. | ||
When a process no longer requires access to a muxwait semaphore, it closes the semaphore by calling DosCloseMuxWaitSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. | When a process no longer requires access to a muxwait semaphore, it closes the semaphore by calling DosCloseMuxWaitSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. | ||
Each call to DosOpenMuxWaitSem increments the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is decremented by each call to DosCloseMuxWaitSem. When the usage count reaches 0, the semaphore is deleted by OS/2. | Each call to DosOpenMuxWaitSem increments the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is decremented by each call to DosCloseMuxWaitSem. When the usage count reaches 0, the semaphore is deleted by OS/2. | ||
Calls to DosOpenMuxWaitSem and DosCloseMuxWaitSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. | Calls to DosOpenMuxWaitSem and DosCloseMuxWaitSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. | ||
Even if the owner of a mutex semaphore in a muxwait-semaphore list has ended without releasing the semaphore, the muxwait semaphore is still opened. Subsequent calls to the muxwait semaphore will return ERROR_SEM_OWNER_DIED. But because the process has opened the semaphore, it can then call DosQueryMuxWaitSem to identify all the mutex semaphores in the muxwait list. Next, the process can call DosQueryMutexSem for each mutex semaphore in the list to find out which ones are in the Owner Died state. Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed should be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem. | Even if the owner of a mutex semaphore in a muxwait-semaphore list has ended without releasing the semaphore, the muxwait semaphore is still opened. Subsequent calls to the muxwait semaphore will return ERROR_SEM_OWNER_DIED. But because the process has opened the semaphore, it can then call DosQueryMuxWaitSem to identify all the mutex semaphores in the muxwait list. Next, the process can call DosQueryMutexSem for each mutex semaphore in the list to find out which ones are in the Owner Died state. Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed should be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem. | ||
===Closing a Muxwait Semaphore=== | ===Closing a Muxwait Semaphore=== | ||
When a process no longer requires access to a muxwait semaphore, it closes the semaphore by calling DosCloseMuxWaitSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. | When a process no longer requires access to a muxwait semaphore, it closes the semaphore by calling DosCloseMuxWaitSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. | ||
Each call to DosCloseMuxWaitSem decrements the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is incremented by each call to DosOpenMuxWaitSem. When the usage count reaches 0, the semaphore is deleted by OS/2. | Each call to DosCloseMuxWaitSem decrements the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is incremented by each call to DosOpenMuxWaitSem. When the usage count reaches 0, the semaphore is deleted by OS/2. | ||
The call to DosCloseMuxWaitSem that decrements the usage count to 0 and causes the semaphore to be deleted is referred to as the final close. If a thread attempts to perform the final close for a semaphore while another thread in the same process is still waiting for it, ERROR_SEM_BUSY is returned. | The call to DosCloseMuxWaitSem that decrements the usage count to 0 and causes the semaphore to be deleted is referred to as the final close. If a thread attempts to perform the final close for a semaphore while another thread in the same process is still waiting for it, ERROR_SEM_BUSY is returned. | ||
Calls to DosOpenMuxWaitSem and DosCloseMuxWaitSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. | Calls to DosOpenMuxWaitSem and DosCloseMuxWaitSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. | ||
===Waiting for a Muxwait Semaphore=== | ===Waiting for a Muxwait Semaphore=== | ||
A thread can wait on a muxwait semaphore by using DosWaitMuxWaitSem. | A thread can wait on a muxwait semaphore by using DosWaitMuxWaitSem. | ||
Any thread in the process that created a muxwait semaphore can wait for the semaphore to clear by calling DosWaitMuxWaitSem. Threads in other processes can also call DosWaitMuxWaitSem, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem. | Any thread in the process that created a muxwait semaphore can wait for the semaphore to clear by calling DosWaitMuxWaitSem. Threads in other processes can also call DosWaitMuxWaitSem, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem. | ||
The following code fragment waits for a muxwait semaphore to clear. Assume that the handle of the semaphore has been placed into hmux already. ulTimeout is the number of milliseconds that the calling thread will wait for the muxwait semaphore to clear. If the specified muxwait semaphore is not cleared during this time interval, the request times out. | The following code fragment waits for a muxwait semaphore to clear. Assume that the handle of the semaphore has been placed into hmux already. ulTimeout is the number of milliseconds that the calling thread will wait for the muxwait semaphore to clear. If the specified muxwait semaphore is not cleared during this time interval, the request times out. | ||
<pre> | <pre> | ||
#define INCL_DOSSEMAPHORES /* Semaphore values */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HMUX hmux; /* Muxwait semaphore handle */ | |||
ULONG ulTimeout; /* Number of milliseconds to wait */ | |||
ULONG ulUser; /* User field for the semaphore that was */ | |||
/* posted or released (returned) */ | |||
APIRET ulrc; /* Return code */ | |||
ulTimeout = 60000; /* Wait for a maximum of 1 minute */ | |||
ulrc = DosWaitMuxWaitSem(hmux, | |||
ulTimeout, | |||
&ulUser); | |||
if (ulrc == ERROR_TIMEOUT) { | |||
printf("DosWaitMuxWaitSem call timed out"); | |||
return; | |||
} | |||
if (ulrc == ERROR_INTERRUPT) { | |||
printf("DosWaitMuxWaitSem call was interrupted"); | |||
return; | |||
} | |||
if (ulrc != 0) { | |||
printf("DosWaitMuxWaitSem error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
On successful return, the ulUser variable contains the user identifier of the semaphore that caused the wait to terminate. If the caller had to wait for all the semaphores within the muxwait semaphore to clear, then the value corresponds to the last semaphore within the muxwait semaphore to clear. If the caller had to wait for any semaphore with the muxwait semaphore to clear, then the value corresponds to that semaphore. | |||
An application can use the DCMW_WAIT_ANY flag in DosCreateMuxWaitSem to block a thread until any one of the event or mutex semaphores included in the muxwait semaphore is posted or released. If the muxwait semaphore refers to mutex semaphores, the thread only gains ownership of the one mutex semaphore that was released. | |||
An application can use the DCMW_WAIT_ALL flag in DosCreateMuxWaitSem to block a thread until all of the event or mutex semaphores included in the muxwait semaphore are posted or released. If the muxwait semaphore refers to mutex semaphores, the thread does not gain ownership of any of the mutex semaphores until they are all released. When all are released, the thread becomes owner of all the mutex semaphores included in the muxwait semaphore. If the muxwait semaphore refers to event semaphores, the thread will not run until all of the event semaphores are in the posted state at the same time. This is because event semaphores in a muxwait list are level-triggered, unlike individual event semaphores, which are edge-triggered. | |||
An application can use the DCMW_WAIT_ALL flag in DosCreateMuxWaitSem to block a thread until all of the event or mutex semaphores included in the muxwait semaphore are posted or released. If the muxwait semaphore refers to mutex semaphores, the thread does not gain ownership of any of the mutex semaphores until they are all released. When all are released, the thread becomes owner of all the mutex semaphores included in the muxwait semaphore. If the muxwait semaphore refers to event semaphores, the thread will not run until all of the event semaphores are in the posted state at the same time. This is because event semaphores in a muxwait list are level-triggered, unlike individual event semaphores, which are edge-triggered | |||
For example, suppose that a thread is waiting for five event semaphores in a muxwait list to be posted. The first semaphore is posted and then reset. Next, the remaining semaphores are all posted, and they remain in the posted state. The thread that is waiting for the muxwait semaphore will not run until the first semaphore is posted again. | |||
The | |||
If an application specifies the DCMW_WAIT_ANY flag when the semaphore is created, DosWaitMuxWaitSem returns the programmer-defined identifier of the semaphore that is subsequently posted or released. If an application specifies the DCMW_WAIT_ALL flag, DosWaitMuxWaitSem returns the programmer-defined identifier of the last semaphore that was posted or released. | |||
The ulTimeout parameter places a limit on the amount of time a thread blocks on a DosWaitMuxWaitSem request. If the time limit is reached before the semaphore has cleared, ERROR_TIMEOUT is returned. If SEM_IMMEDIATE_RETURN is specified as the time limit, DosWaitMuxWaitSem returns without blocking the thread. The thread can then go on to perform other operations and call DosWaitMuxWaitSem again later to wait for the event or mutex semaphores in the muxwait list to be posted or released. If a time limit of SEM_INDEFINITE_WAIT is specified, the thread waits (is blocked) indefinitely. If the thread is unblocked by an external event while it is waiting for the muxwait semaphore (as when a "no wait" I/O request has just been completed), DosWaitMuxWaitSem returns ERROR_INTERRUPT. | |||
When a thread is waiting for any one of the semaphores in a muxwait list to be posted or released, the semaphores are checked in the order in which they are defined in the list. | |||
===Waiting for Multiple Event Semaphores=== | |||
The following information pertains only to muxwait semaphores that consist of multiple event semaphores: | |||
Unlike individual event semaphores, which are edge-triggered, event semaphores in a muxwait list are level-triggered. This means that if a thread is waiting for all of the event semaphores in the muxwait list, it will not run until all of the event semaphores are in the posted state at the same time. | |||
For example, a thread is waiting for five event semaphores in a muxwait list to be posted. The first semaphore is posted and then reset. Next, the remaining semaphores are all posted, and they remain in the posted state. The thread that is waiting for the muxwait semaphore will not run until the first semaphore is posted again. | |||
===Waiting for Multiple Mutex Semaphores=== | |||
The following information pertains only to muxwait semaphores that consist of multiple mutex semaphores: | |||
*If a thread is waiting for all of the mutex semaphores in a muxwait list to be released, it does not receive ownership of any of the semaphores until all of the semaphores have been released. | |||
*If a thread is waiting for any one of the mutex semaphores in a muxwait list, then the thread gains ownership only of the first mutex semaphore that is released. The ownership of all other mutex semaphores in the muxwait list remains unchanged. | |||
*If two threads have the same priority, then a thread that is waiting for a mutex semaphore in a muxwait list takes precedence over a thread that has requested ownership of only the individual semaphore, provided all other mutex semaphores in the muxwait list have been released. For example, a mutex semaphore that is part of a muxwait semaphore is released. One thread has requested ownership of that single mutex semaphore, and another thread with the same priority is waiting for the muxwait semaphore that contains the same mutex semaphore. If all of the other mutex semaphores in the muxwait list are unowned and ready to be given to the muxwait semaphore, then the thread that is waiting for the muxwait semaphore will run first. | |||
*If the owners of any of the mutex semaphores in the muxwait semaphore list have ended without releasing them, ERROR_SEM_OWNER_DIED is returned. The thread must then call DosQueryMuxWaitSem to obtain the records of all the semaphores in the muxwait list. Next, the thread must call DosQueryMutexSem for each mutex semaphore in the muxwait-semaphore list so that it can determine which semaphores are in the Owner Died state. | |||
Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed should be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem. | |||
*If any of the mutex semaphores in the muxwait list are owned by the calling thread, ERROR_MUTEX_OWNED is returned. | |||
===Adding a Semaphore to a Muxwait List=== | ===Adding a Semaphore to a Muxwait List=== | ||
An application uses DosAddMuxWaitSem to add semaphores to a muxwait semaphore that has already been created, even while threads are waiting on the muxwait semaphore | An application uses DosAddMuxWaitSem to add semaphores to a muxwait semaphore that has already been created, even while threads are waiting on the muxwait semaphore. | ||
Any thread in the process that created a muxwait semaphore can add a mutex semaphore or an event semaphore to the muxwait list by calling DosAddMuxWaitSem. Threads in other processes can also use this function, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem. | |||
A maximum of 64 semaphores can be included in a muxwait-semaphore list. If an attempt is made to exceed this maximum, ERROR_TOO_MANY_SEMAPHORES is returned. | |||
All of the semaphores in a muxwait-semaphore list must be of the same type. That is, if a mutex semaphore is being added, then the other semaphores in the list must be mutex semaphores. If an event semaphore is being added, then the other semaphores in the list must be event semaphores. A shared muxwait semaphore can contain only shared semaphores in its list. A private muxwait semaphore can contain both private and shared semaphores. If any of these conditions is violated, ERROR_WRONG_TYPE is returned. | |||
If the semaphore is successfully added to the muxwait list, DosAddMuxWaitSem checks to see whether each thread that is waiting for the muxwait semaphore has the newly added semaphore open in its process. The muxwait semaphore is invalid for any waiting threads that do not have the newly added semaphore open in their process; these threads are unblocked with a return code of ERROR_INVALID_HANDLE. Any processes that opened the muxwait semaphore before the add operation and that do not have the new semaphore open, will have to open the new semaphore before making any further use of the muxwait semaphore. Any future calls concerning the muxwait semaphore by processes that do not have the new semaphore open will have ERROR_INVALID_HANDLE returned until the new semaphore is opened. | |||
As soon as this semaphore is opened, the muxwait semaphore becomes valid again for the process, as long as no other changes have been made to the muxwait list to make it invalid. However, in order to successfully wait for the muxwait semaphore, the process must call DosWaitMuxWaitSem again. | A thread that receives a return code of ERROR_INVALID_HANDLE can take the following corrective action: | ||
#First, the thread can obtain the records of all the semaphores in the muxwait list by calling DosQueryMuxWaitSem. | |||
#Next, it can query each semaphore in the muxwait list, using DosQueryMutexSem or DosQueryEventSem, to find out which semaphore is not open to its process. | |||
#Finally, it can open the semaphores that are not open by calling DosOpenMutexSem or DosOpenEventSem. | |||
As soon as this semaphore is opened, the muxwait semaphore becomes valid again for the process, as long as no other changes have been made to the muxwait list to make it invalid. However, in order to successfully wait for the muxwait semaphore, the process must call DosWaitMuxWaitSem again. | |||
A semaphore must be open for a process before the process can add that semaphore to a muxwait semaphore. If it is not open and a thread is waiting on the muxwait semaphore, DosAddMuxWaitSem returns ERROR_INVALID_HANDLE to the process adding the new semaphore, and the waiting thread continues waiting. | A semaphore must be open for a process before the process can add that semaphore to a muxwait semaphore. If it is not open and a thread is waiting on the muxwait semaphore, DosAddMuxWaitSem returns ERROR_INVALID_HANDLE to the process adding the new semaphore, and the waiting thread continues waiting. | ||
===Deleting a Semaphore from a Muxwait List=== | ===Deleting a Semaphore from a Muxwait List=== | ||
An application can delete semaphores from a muxwait semaphore by using DosDeleteMuxWaitSem. | An application can delete semaphores from a muxwait semaphore by using DosDeleteMuxWaitSem. | ||
Any thread in the process that created a muxwait semaphore can delete a mutex or event semaphore from the muxwait list by calling DosDeleteMuxWaitSem. Threads in other processes can also use this function, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem. | Any thread in the process that created a muxwait semaphore can delete a mutex or event semaphore from the muxwait list by calling DosDeleteMuxWaitSem. Threads in other processes can also use this function, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem. | ||
Semaphores can be deleted from the muxwait list even while threads are currently waiting for the semaphore. If the deleted semaphore is the only one in the muxwait list that has not yet been posted or released, then threads that are waiting for the muxwait semaphore are unblocked. Also, if the deleted semaphore happens to be the last one that a particular thread was waiting for, that thread is unblocked. Also, if the deleted semaphore is the last one in the muxwait list (that is, if the list is now empty), then all the threads that are waiting for the muxwait semaphore are unblocked. | Semaphores can be deleted from the muxwait list even while threads are currently waiting for the semaphore. If the deleted semaphore is the only one in the muxwait list that has not yet been posted or released, then threads that are waiting for the muxwait semaphore are unblocked. Also, if the deleted semaphore happens to be the last one that a particular thread was waiting for, that thread is unblocked. Also, if the deleted semaphore is the last one in the muxwait list (that is, if the list is now empty), then all the threads that are waiting for the muxwait semaphore are unblocked. | ||
===Querying a Muxwait Semaphore=== | ===Querying a Muxwait Semaphore=== | ||
Processes use DosQueryMuxWaitSem to obtain the semaph ore records for each of the semaphores included in the muxwait semaphore | Processes use DosQueryMuxWaitSem to obtain the semaph ore records for each of the semaphores included in the muxwait semaphore. | ||
Any thread in the process that created a muxwait semaphore can obtain information about the semaphores in the muxwait list by calling DosQueryMuxWaitSem. Threads in other processes can also use this function, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem. | |||
If the | An application must provide this function with an array in which to store the semaphore records. If the array is not large enough to hold all of the semaphore records that are in the muxwait list, then ERROR_PARAM_TOO_SMALL is returned, and the record-counting parameter of DosQueryMuxWaitSem will contain the number of semaphore records that are in the muxwait list. The calling thread can then allocate the correct amount of space and call DosQueryMuxWaitSem again with the correct amount of space for the list of records. | ||
If the owner of any mutex semaphore in the muxwait-semaphore list has ended without releasing the semaphore, the records of all the semaphores in the list are still returned, but DosQueryMuxWaitSem also returns ERROR_SEM_OWNER_DIED. The calling thread can call DosQueryMutexSem for each mutex semaphore in the muxwait-semaphore list so that it can determine which semaphores are in the Owner Died state. The process can then close the unowned mutex semaphores. | |||
Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed should be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem. | |||
If the specified muxwait semaphore does not exist, ERROR_INVALID_HANDLE is returned. | |||
[[Category:CPGuide]] | [[Category:CPGuide]] |
Latest revision as of 18:29, 23 April 2020
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Semaphores are one of three forms of interprocess communication (IPC), the other forms of IPC being pipes and queues.
This chapter describes how to create and use semaphores. Semaphores enable an application to signal the completion of tasks and control access to resources that are shared between more than one thread or process.
The following topics are related to the information in this chapter:
- Memory (shared memory)
- Program execution and control
- Pipes
- Queues
About Semaphores
Semaphores signal the beginning or ending of an operation and provide mutually exclusive ownership of resources. Typically, semaphores are used to prevent more than one process or thread within a process from accessing a resource, such as shared memory, at the same time.
Semaphores are defined by OS/2 and reside in an internal memory buffer. They are divided into three types, according to the functionality they provide:
- Event semaphores enable a thread to notify waiting threads that an event has occurred. The waiting threads then resume execution, performing operations that are dependent on the completion of the signaled event.
- Mutual exclusion (mutex) semaphores enable threads to serialize their access to shared resources. That is, ownership of a mutex semaphore is used by cooperating threads as a prerequisite for performing operations on a resource. (Threads cooperate by using the mutex semaphore functions to ensure that access to the resource is mutually exclusive.)
- Multiple wait (muxwait) semaphores enable threads to wait either for multiple events to occur, or for multiple resources to become available. Alternatively, a flag can be set so that a thread waits for any one of multiple events to occur, or for any one of multiple resources to become available.
Event Semaphores
An event semaphore provides a signaling mechanism among threads or processes, ensuring that events occur in the desired sequence. Event semaphores are used by one thread to signal other threads that an event has occurred. An application can use this type of semaphore to block a thread or process until the event has occurred.
An event semaphore has two states, reset and posted. When an event semaphore is in the reset state, OS/2 blocks any thread or process that is waiting on the semaphore. When an event semaphore is in the posted state, all threads or processes waiting on the semaphore resume execution.
For example, assume thread 1 is allocating a shared memory object and threads 2 and 3 must wait for the memory to be allocated before they attempt to examine its contents. Before thread 1 allocates the memory, it creates an event semaphore, specifying the initial state of the semaphore as reset. (If the event semaphore has already been created, thread 1 simply resets the semaphore.) Threads 2 and 3 use DosWaitEventSem to wait for the semaphore to signal that the event, in this case the allocation and preparation of the shared memory object, has been completed. Because the semaphore was reset by thread 1, threads 2 and 3 are blocked when they call DosWaitEventSem. After thread 1 has finished allocating and placing data in the shared memory object, it signals the completion of its task by posting the event semaphore. The posting of the event semaphore unblocks threads 2 and 3, enabling them to resume execution. They can then proceed to examine the contents of the allocated memory.
In the example above, one thread controls the resetting and posting of the event semaphore, while other threads merely wait on the semaphore. Another approach could be for an application or thread to reset an event semaphore, then block itself on that semaphore. At a later time, another application or thread would post the event semaphore, unblocking the first thread.
Mutual Exclusion (Mutex) Semaphores
A mutual exclusion (mutex) semaphore protects resources (such as files, data in memory, and peripheral devices) from simultaneous access by several processes. Mutex semaphores enable threads to serialize their access to resources. It does so by preventing the processes from concurrently executing the sections of code through which access is made. These sections of code are called critical sections. For example, a mutex semaphore could be used to prevent two or more threads from simultaneously writing to the same file on a disk.
Before a thread can execute a mutex-protected critical section, it must request and receive ownership of the mutex semaphore. Only the thread that has gained ownership of the mutex semaphore is permitted to perform operations on the protected resource. Only one thread at a time can own the mutex semaphore, and the owner thread retains ownership until it finishes executing its critical section. When finished, the owner thread releases the mutex semaphore, enabling another thread to become the owner.
When a thread requests ownership of a mutex semaphore that is already owned, OS/2 blocks the thread. When more than one thread requests ownership of the same semaphore, OS/2 queues the requests and grants subsequent ownership based on the thread's priority and the order in which the requests were received.
If more than one thread is blocked on a DosRequestMutexSem request, then ownership is given to the thread that has the highest priority level. If more than one of the waiting threads have the same priority level, then FIFO ordering is used to determine which thread is unblocked and given ownership of the semaphore.
For example, both thread 1 and thread 2 must write information to the same disk file. Thread 1 claims ownership of an agreed-upon mutex semaphore and starts writing its information to the file. If thread 2 also requests ownership of the semaphore, it will be blocked. When thread 1 has finished writing to the file, it releases the semaphore. OS/2 then unblocks thread 2 and designates it as the new owner of the semaphore so that it can write to the file.
During process termination, after delivery of process-termination exceptions and unwind exceptions, if any threads in the process aside from Thread 1 (the main thread) own a mutex semaphore, ownership of the semaphore (and therefore, the shared resource) passes to Thread 1. This gives Thread 1 a last chance to clean up the semaphore and the shared resource before the process ends. If Thread 1 ends without releasing the semaphore, all threads that are currently waiting on ownership of the semaphore will be unblocked with the SEM_OWNER_DIED return code. Any thread that attempts to open it or request ownership of the semaphore will receive a SEM_OWNER_DIED return code.
The recommended way to clean up semaphores, and other resources, is for each thread, especially Thread 1, to have an exception handler to handle clean up during process termination (the XCPT_PROCESS_TERMINATE or XCPT_ASYNC_PROCESS_TERMINATE exceptions). When it is not possible to register an exception handler for a thread, (a DLL, for example, must de-register its exception handlers when it returns control to the thread that called it), you should add a clean up routine to the exit list of the process.
Multiple Wait (Muxwait) Semaphores
A multiple wait (muxwait) semaphore enables a thread to wait on several event or mutex semaphores simultaneously. A muxwait semaphore is a compound semaphore that consists of a list of up to 64 event semaphores or mutex semaphores (the two types cannot be mixed).
A flag is set when the muxwait semaphore is created to enable threads to use the semaphore in either of two ways:
- Threads can wait for all of the mutex semaphores to be released, or for all of the event semaphores to be posted.
- Threads can wait for any one of the mutex semaphores in the list to be released, or for any one of the event semaphores in the list to be posted.
Depending on the value of the flag, a muxwait semaphore is said to have cleared when either any or all of the semaphores in the muxwait list have been posted or released.
For example, suppose a thread requires access to several regions of shared memory at the same time. OS/2 blocks the thread until the thread acquires ownership of all the mutex semaphores protecting the shared regions. The thread can then access all the memory regions. Meanwhile, OS/2 prevents access by other threads.
Named and Anonymous Semaphores
A semaphore can be either named or anonymous. A named semaphore is always shared; that is, it is always available to any process that knows the name. An anonymous semaphore can be either private to a process or shared among processes, depending on whether the application includes the DC_SEM_SHARED flag in the function that creates the semaphore. A semaphore intended for use solely among threads of the same process can be anonymous and private.
OS/2 creates a named semaphore when an application specifies a name in the function that creates the semaphore. The name must have the following form:
\SEM32\SemName
The "\SEM32\" is required, though it need not be uppercase. The semaphore name must conform to the rules for OS/2 file names, although no actual file is created for the semaphore. If the application does not specify a name in the function that creates the semaphore, OS/2 creates an anonymous semaphore.
OS/2 permits a system-wide maximum of 65536 (64K) shared semaphores. In addition, each process can use up to 65536 (64K) private semaphores.
A shared muxwait semaphore must contain either all shared event semaphores or all shared mutex semaphores. However, a private muxwait semaphore can contain a combination of shared and private event or mutex semaphores. OS/2 generates a unique handle when it creates a semaphore. Processes must obtain this handle before they can access the semaphore. A semaphore's handle is always available to the process that created the semaphore. A process can obtain the handle of a named semaphore created in another process by using the appropriate semaphore-opening function. A process that requires access to an anonymous shared semaphore that was created in another process must obtain the handle of the semaphore through some other form of interprocess communication, such as a pipe or a queue.
Semaphore Management
After one process creates a semaphore, threads in other processes must open the semaphore before they can access it. (Creating a semaphore automatically opens it for the creating process.) The open operation ensures that the process is a valid user of the semaphore. OS/2 keeps track of the number of open operations that each process performs on a semaphore. A process can have up to 65535 (64K - 1) open operations performed on a semaphore at any one time.
If a process finishes using a semaphore and will not use it again, the process should close the semaphore so that OS/2 can free the memory the semaphore is using. OS/2 returns the ERROR_SEM_BUSY error value if a thread tries to close a semaphore that has another thread in the same process still waiting for it.
If a process terminates with open semaphores, OS/2 automatically closes the semaphores for that process.
Semaphores reside in a memory buffer rather than a disk file. Therefore, when the last process that has a semaphore open exits or closes that semaphore, OS/2 frees the associated handle or name.
When an application calls a function that causes a thread to wait on a semaphore, the application can specify the amount of time for the thread to wait. When the interval elapses without the semaphore being posted or released, the function returns the ERROR_TIMEOUT error value and the thread continues running. The application can provide a specific time-out value in milliseconds, or it can specify either the SEM_INDEFINITE_WAIT or the SEM_IMMEDIATE_RETURN flag. If a thread is interrupted while it is waiting on a semaphore, the ERROR_INTERRUPT error value is returned to the caller.
Using Event Semaphores
An application can use an event semaphore to trigger execution of other processes. This is useful if, for example, one process provides data to many other processes. Using an event semaphore frees the other process from the trouble of polling to determine when new data is available.
- Note
- In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred.
Creating an Event Semaphore
Processes create an event semaphore by using DosCreateEventSem. The process that controls the event or resource is usually the one that creates the semaphore, but it does not have to be.
Threads in the process that creates the semaphore do not have to open the semaphore before using it. DosCreateEventSem obtains access to the semaphore for the calling process and its threads. Threads in other processes must call DosOpenEventSem to open the semaphore before they can use it.
Event semaphores can be defined as either private or shared:
- Private semaphores are always unnamed and are therefore always identified by their handles. They can be used only by threads within a single process.
- Shared semaphores can be either named or unnamed. If named, they can be opened using either the name or the handle. The handle returned by DosOpenEventSem is then used to identify the semaphore for all other functions. Semaphore names must include the prefix \SEM32\ and must conform to file system naming conventions. Shared semaphores can be used by threads in multiple processes.
In the following code fragment, the controlling process creates a named event semaphore and posts the semaphore after writing data to a shared file:
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> HEV hevWriteEvent; DosCreateEventSem("\\sem32\\wrtevent", /* Named-shared semaphore */ &hevWriteEvent, 0, FALSE); /* Initially reset */ . . /* Write data to shared file. */ . . DosPostEventSem(hevWriteEvent); /* Posts the event */ . . /* Continue execution. */ .
There is a system-wide limit of 65536 (64K) shared semaphores (including mutex, event, and muxwait semaphores); in addition, each process can have up to 65536 (64K) private semaphores.
When an event semaphore is created, a flag is used to specify the initial state of the event semaphore, either reset or posted. If the initial state is reset, a thread that calls DosWaitEventSem will be blocked until a process that has access to the semaphore uses DosPostEventSem to post the event semaphore. If the initial state is posted, then a thread that calls DosWaitEventSem will return immediately to continue its execution. If the thread calling DosWaitEventSem is not in the process that created the semaphore, the thread must open the semaphore with DosOpenEventSem before calling DosWaitEventSem.
OS/2 maintains a usage count for each semaphore. DosCreateEventSem initializes the usage count to 1. Thereafter, each call to DosOpenEventSem increments the count, and each call to DosCloseEventSem decrements it.
Opening an Event Semaphore
When a process creates an event semaphore, all of the threads that belong to the process have immediate access to the semaphore.
Threads in other processes must open the semaphore by calling DosOpenEventSem before they can use the semaphore in any other event semaphore function.
The following code fragment shows how processes can open an event semaphore that was created in a different process and then wait for the event to be posted:
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> HEV hevEventHandle = 0; /* Must be 0 because we are opening */ /* the semaphore by name */ DosOpenEventSem("\\sem32\\wrtevent", &hevEventHandle); DosWaitEventSem(hevEventHandle, SEM_INDEFINITE_WAIT); /* Waits until event is posted */ . . /* Read from file when event is posted. */ .
Applications can open an event semaphore by name or by handle. If the name is used to open the semaphore, as in the code fragment above, the handle parameter must be 0. If the handle is used to open the semaphore, the name parameter must be NULL.
Access to semaphores is on a per-process basis. Therefore, a semaphore that has been opened by one thread in a process is open to all other threads in that process as well.
DosOpenEventSem merely provides access to an event semaphore. In order to wait for an event semaphore to be posted, a thread must call DosWaitEventSem. In order to post or reset an open event semaphore, a thread uses DosPostEventSem or DosResetEventSem respectively.
When a process no longer requires access to an event semaphore, it closes the semaphore by calling DosCloseEventSem. If a process ends without closing an open semaphore, the semaphore is closed by OS/2.
Each call to DosOpenEventSem increments the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is decremented by each call to DosCloseEventSem. When the usage count reaches 0, the semaphore is deleted by OS/2.
Calls to DosOpenEventSem and DosCloseEventSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned.
Closing an Event Semaphore
When a process no longer requires access to an event semaphore, it closes the semaphore by calling DosCloseEventSem.
The following code fragment closes an event semaphore. Assume that the handle of the semaphore has been placed into HEV already.
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> #include <stdio.h> HEV hev; /* Event semaphore handle */ APIRET ulrc; /* Return code */ ulrc = DosCloseEventSem(hev); if (ulrc != 0) { printf("DosCloseEventSem error: return code = %ld", ulrc); return; }
Calls to DosOpenEventSem and DosCloseEventSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned.
If a process ends without closing an open semaphore, the semaphore is closed by OS/2.
Each call to DosCloseEventSem decrements the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is incremented by each call to DosOpenEventSem. When the usage count reaches 0, the semaphore is deleted from OS/2. The call to DosCloseEventSem that decrements the usage count to 0 and causes the semaphore to be deleted is referred to as the final close. If a thread attempts to perform the final close for a semaphore while another thread in the same process is still waiting for it, ERROR_SEM_BUSY is returned.
Resetting an Event Semaphore
DosResetEventSem resets an event semaphore if it is not already reset, and returns the number of times the semaphore was posted since it was last reset. All threads that subsequently call DosWaitEventSem for this semaphore will be blocked.
Any thread belonging to the process that created the event semaphore can change the state of the semaphore to reset by calling DosResetEventSem. Threads in other processes can also call DosResetEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem.
When an event semaphore is in the reset state, any thread that calls DosWaitEventSem to wait for the semaphore will be blocked. When the event semaphore is posted, all of the threads that are waiting for the semaphore are released to continue execution.
The following code fragment resets an event semaphore. Assume that the handle of the semaphore has been placed into HEV already.
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> #include <stdio.h> HEV hev; /* Event semaphore handle */ ULONG ulPostCt; /* Post count for the event semaphore (returned) */ APIRET ulrc; /* Return code */ ulrc = DosResetEventSem(hev, &ulPostCt); if (ulrc != 0) { printf("DosResetEventSem error: return code = %ld", ulrc); return; }
DosResetEventSem returns the post count of the event semaphore and resets the post count to 0. The post count is the number of times the semaphore has been posted (using DosPostEventSem) since the last time the semaphore was in the reset state. (An event semaphore can be reset when it is created, as well as by calling DosResetEventSem.) The post count can also be obtained by calling DosQueryEventSem.
If the event semaphore is already reset when DosResetEventSem is called, ERROR_ALREADY_RESET is returned, along with a post count of 0. The semaphore is not reset a second time.
Posting an Event Semaphore
DosPostEventSem posts the semaphore, if it is not already posted, and increments the post count. All threads that have called DosWaitEventSem for this semaphore are unblocked and resume execution. Threads that call DosWaitEventSem after the event semaphore has been posted and before the next time it is reset, will return immediately from a call to DosWaitEventSem and continue execution. If the semaphore is subsequently reset, threads that call DosWaitEventSem will again be blocked.
Any thread in the process that created an event semaphore can post the semaphore by calling DosPostEventSem. Threads in other processes can also call DosPostEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem.
The following code fragment posts a system event semaphore. Assume that the handle of the semaphore has been placed into HEV already.
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> #include <stdio.h> HEV hev; /* Event semaphore handle */ APIRET ulrc; /* Return code */ ulrc = DosPostEventSem(hev); if (ulrc != 0) { printf("DosPostEventSem error: return code = %ld", ulrc); return; }
OS/2 maintains a post count for each event semaphore. The post count is the number of times the semaphore has been posted (with DosPostEventSem) since the last time the semaphore was in the reset state.
If the event semaphore is reset when DosPostEventSem is called, the semaphore is posted and the post count is set to 1. If the event semaphore is already posted when DosPostEventSem is called, the post count is incremented, and ERROR_ALREADY_POSTED is returned to the calling thread.
The post count is returned as output by DosResetEventSem; it can also be obtained by calling DosQueryEventSem.
The maximum number of times an event semaphore can be posted is 65535. The value of the post count cannot exceed 65535. If an attempt is made to exceed this number, DosPostEventSem returns ERROR_TOO_MANY_POSTS.
Waiting for an Event Semaphore
Any thread in the process that created an event semaphore can wait for the semaphore to be posted by calling DosWaitEventSem. Threads in other processes can also call DosWaitEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem.
If the semaphore is already posted when DosWaitEventSem is called, the function returns immediately, and the thread continues to run. Otherwise, the thread is blocked until the semaphore is posted.
The following code fragment causes the calling thread to wait until the specified event semaphore is posted. Assume that the handle of the semaphore has been placed into HEV already. ulTimeout is the number of milliseconds that the calling thread will wait for the event semaphore to be posted. If the specified event semaphore is not posted during this time interval, the request times out.
#define INCL_DOSSEMAPHORES /* Semaphore values */ #define INCL_DOSERRORS /* error codes */ #include <os2.h> #include <stdio.h> HEV hev; /* Event semaphore handle */ ULONG ulTimeout; /* Number of milliseconds to wait */ APIRET ulrc; /* Return code */ ulTimeout = 60000; /* Wait for a maximum of 1 minute */ ulrc = DosWaitEventSem(hev, ulTimeout); if (ulrc == ERROR_TIMEOUT) { printf("DosWaitEventSem call timed out"); return; } if (ulrc == ERROR_INTERRUPT) { printf("DosWaitEventSem call was interrupted"); return; } if (ulrc != 0) { printf("DosWaitEventSem error: return code = %ld", ulrc); return; }
If the time limit specified in ulTimeout is reached before the semaphore is posted, ERROR_TIMEOUT is returned. If the waiting period is interrupted for some reason before the semaphore is posted, ERROR_INTERRUPT is returned. If SEM_IMMEDIATE_RETURN is specified for the time limit, DosWaitEventSem returns to the calling thread immediately. If SEM_INDEFINITE_WAIT is specified for the time limit, the thread waits indefinitely.
Unlike multiple event semaphores in a muxwait list, which are level-triggered, single event semaphores are edge-triggered. This means that if an event semaphore is posted and then reset before a waiting thread gets a chance to run, the semaphore is considered to be posted for the rest of that thread's waiting period; the thread does not have to wait for the semaphore to be posted again.
Querying an Event Semaphore
DosQueryEventSem returns the current post count of a semaphore. The post count is the number of times that the semaphore has been posted (with DosPostEventSem) since the last time the semaphore was reset. A count of 0 indicates that the semaphore is in the reset state; therefore, OS/2 will block any threads that call DosWaitEventSem to wait on the semaphore.
Any thread in the process that created an event semaphore can obtain the post count for the semaphore by calling DosQueryEventSem. Threads in other processes can also call DosQueryEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem.
The following code fragment retrieves the post count for an event semaphore. Assume that the handle of the semaphore has been placed into HEV already.
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> #include <stdio.h> HEV hev; /* Event semaphore handle */ ULONG ulPostCt; /* Current post count for the semaphore (returned) */ APIRET ulrc; /* Return code */ ulrc = DosQueryEventSem(hev, &ulPostCt); if (ulrc != 0) { printf("DosQueryEventSem error: return code = %ld", ulrc); return; }
If the specified event semaphore does not exist, ERROR_INVALID_HANDLE is returned.
Using Mutex Semaphores
An application can use a mutual exclusion (mutex) semaphore to protect a shared resource from simultaneous access by multiple threads or processes. For example, if several processes must write to the same disk file, the mutex semaphore ensures that only one process at a time writes to the file.
Creating a Mutex Semaphore
Mutex semaphores are created by calling DosCreateMutexSem. This function also opens the semaphore for the calling process and its threads.
When a mutex semaphore is created, a flag is set to specify the initial state of the semaphore, owned or unowned. If the semaphore is owned by a thread, other threads requesting the semaphore are blocked. If the semaphore is unowned-not owned by any thread- then any thread requesting ownership will be granted ownership immediately.
If the calling thread sets the initial state to owned, it owns the semaphore as soon as OS/2 creates the semaphore and can proceed to access the resource that the semaphore was created to protect.
If the semaphore is unowned, any thread in the creating process can subsequently request ownership of the semaphore by calling DosRequestMutexSem. Threads in other processes can gain ownership of the semaphore, but they must call DosOpenMutexSem to acquire access to the semaphore before they can call DosRequestMutexSem.
Mutex semaphores can be defined as either private or shared.
- Private semaphores are always unnamed and are therefore identified by their handles. They can be used only by threads within a single process.
- Shared semaphores can be either named or unnamed. If named, they can be opened using either the name or the handle. The handle returned by DosOpenMutexSem is then used to identify the semaphore for all other functions. Semaphore names must include the prefix \SEM32\ and must conform to file system naming conventions. Shared semaphores can be used by threads in multiple processes.
The following code fragment creates a mutex semaphore:
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> HMTX hmtxProtFile; DosCreateMutexSem("\\sem32\\ProtFile", /* Named-shared semaphore */ &hmtxProtFile, 0, FALSE); /* Initially unowned */ . . /* Get data to write to shared file. */ .
There is a system-wide limit of 65536 shared semaphores (including mutex, event, and muxwait semaphores); in addition, each process can have up to 65536 private semaphores.
OS/2 maintains a usage count for each semaphore. DosCreateMutexSem initializes the usage count to 1. Thereafter, each call to DosOpenMutexSem increments the count, and each call to DosCloseMutexSem decrements it.
Opening a Mutex Semaphore
All of the threads belonging to the process that creates a mutex semaphore have immediate access to the semaphore. Threads in other processes must request access to the semaphore by calling DosOpenMutexSem before they can use the semaphore in other mutex semaphore functions.
Access to system resources is granted on a per-process basis. Therefore, a semaphore that has been opened by one thread in a process is open to all other threads in that process as well.
DosOpenMutexSem merely provides access to a mutex semaphore. To request ownership of a mutex semaphore, a thread must call DosRequestMutexSem.
When a process no longer requires access to a mutex semaphore, it should close the semaphore by calling DosCloseMutexSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2.
Each call to DosOpenMutexSem. increments the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is decremented by each call to DosCloseMutexSem. When the usage count reaches 0, the semaphore is deleted by the system.
Calls to DosOpenMutexSem and DosCloseMutexSem. can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned.
If a process ends without releasing a mutex semaphore that it owns, any other thread that subsequently tries to open the semaphore will receive ERROR_SEM_OWNER_DIED. This return code indicates that the owning process ended abnormally, leaving the protected resource in an indeterminate state. However, the semaphore is still opened for the calling thread, enabling the thread to call DosQueryMutexSem to find out which process ended without releasing the semaphore. The thread can then take appropriate action concerning the semaphore and the protected resource.
Requesting a Mutex Semaphore
In order to access a shared resource, a process must own the mutex semaphore that is protecting the shared resource. Ownership is obtained by first opening the mutex semaphore with DosOpenMutexSem, then using DosRequestMutexSem to request ownership of the semaphore. If another process already owns the semaphore, the requesting process is blocked. If the semaphore is not owned, OS/2 grants ownership to the requesting process and the process can access the shared resource. When the process is finished using the shared resource, it uses DosReleaseMutexSem to relinquish its ownership of the semaphore, thereby enabling another process to gain ownership.
A process can gain ownership of a mutex semaphore in three ways:
- The thread that creates a mutex semaphore can designate itself as the owner by setting a flag when it calls DosCreateMutexSem.
- Any thread in the process that created the semaphore can request ownership by calling DosRequestMutexSem.
- A thread in another process must request access to the semaphore with DosOpenMutexSem before it can call DosRequestMutexSem.
Note that ownership of a mutex semaphore is given only to the requesting thread; it is not shared by other threads in the same process.
If a mutex semaphore is unowned, DosRequestMutexSem sets it as owned and returns immediately to the caller. If the semaphore is already owned, the calling thread is blocked until either the owning thread calls DosReleaseMutexSem to release the semaphore, or a specified time limit is reached.
The following code fragment shows how a process opens a mutex semaphore, requests it, and, after writing to the shared file, releases and closes the semaphore:
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> HMTX hmtxProtFile; DosOpenMutexSem("\\sem32\\ProtFile", &hmtxProtFile); /* Opens for this process */ DosRequestMutexSem(hmtxProtFile, 5000); /* Returns in 5 seconds if */ . /* Ownership not obtained */ . . /* Write data to shared file */ . . DosReleaseMutexSem(hmtxProtFile); /* Releases ownership */ . . /* Continue execution */ . . DosCloseMutexSem(hmtxProtFile); /* Finished with shared file */
If more than one thread is blocked on a DosRequestMutexSem request, the thread with the highest priority level is the first to be unblocked and given ownership of the semaphore. If more than 1 of the waiting threads have the same priority level, then FIFO ordering is used to determine which thread is unblocked and given ownership.
The time-out parameter (5000 milliseconds in the example above) places a limit on the amount of time a thread blocks on a DosRequestMutexSem request. If the time limit is reached before the thread gains ownership of the semaphore, ERROR_TIMEOUT is returned. If SEM_IMMEDIATE_RETURN is specified for the time limit, DosRequestMutexSem returns without blocking the thread. The thread can then perform other operations and call DosRequestMutexSem again later if it still requires access to the protected resource. If SEM_INDEFINITE_WAIT is specified for the time limit, the thread waits indefinitely. If the thread is unblocked by an external event while it is waiting for the mutex semaphore (as when a No Wait I/O request has just been completed), ERROR_INTERRUPT is returned to the caller.
In addition to the usage count that OS/2 maintains for all semaphores, OS/2 maintains a request count for each mutex semaphore. Each call to DosRequestMutexSem increments the count, and each call to DosReleaseMutexSem decrements it.
Calls to DosRequestMutexSem and DosReleaseMutexSem can be nested, but the request count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_SEM_REQUESTS is returned. When calls to DosRequestMutexSem and DosReleaseMutexSem are nested, a call to DosReleaseMutexSem merely decrements the request count for the semaphore; the semaphore is not actually released to another thread until its request count is 0. If a process ends while it owns a mutex semaphore, all of the currently blocked DosRequestMutexSem requests, as well as any future requests for the semaphore, return ERROR_SEM_OWNER_DIED. This return code indicates that the owning process ended abnormally, leaving the protected resource in an indeterminate state. An application that receives this error should close the mutex semaphore (so that it can be deleted from OS/2), because it is no longer valid. Appropriate action should also be taken concerning the protected resource.
Releasing a Mutex Semaphore
A thread can release ownership of a mutex semaphore by calling DosReleaseMutexSem. Each call to DosReleaseMutexSem decrements the request count that is maintained for the semaphore by OS/2. Each call to DosRequestMutexSem increments the count.
The following code fragment relinquishes ownership of a mutex semaphore. Assume that the handle of the semaphore has been placed into hmtx already.
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> #include <stdio.h> HMTX hmtx; /* Mutex semaphore handle */ APIRET ulrc; /* Return code */ ulrc = DosReleaseMutexSem(hmtx); if (ulrc != 0) { printf("DosReleaseMutexSem error: return code = %ld", ulrc); return; }
Calls to DosRequestMutexSem and DosReleaseMutexSem can be nested, but the request count cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_SEM_REQUESTS is returned. When calls to DosRequestMutexSem and DosReleaseMutexSem are nested, a call to DosReleaseMutexSem merely decrements the request count for the semaphore; the semaphore is not actually released to another thread until its request count is 0.
Closing a Mutex Semaphore
When a process no longer requires access to a mutex semaphore, it can close the semaphore by calling DosCloseMutexSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2.
The following code fragment closes a mutex semaphore. Assume that the handle of the semaphore has been placed into hmtx already.
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> #include <stdio.h> HMTX hmtx; /* Mutex semaphore handle */ APIRET ulrc; /* Return code */ ulrc = DosCloseMutexSem(hmtx); if (ulrc != 0) { printf("DosCloseMutexSem error: return code = %ld", ulrc); return; }
Each call to DosCloseMutexSem decrements the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is incremented by each call to DosOpenMutexSem. When the usage count reaches 0, the semaphore is deleted by OS/2.
The call to DosCloseMutexSem that decrements the usage count to 0 and causes the semaphore to be deleted is referred to as the final close. The final close will not succeed if either of the following conditions exists:
- The semaphore is owned by another thread in the same process.
- Another thread in the same process is still blocked on a DosRequestMutexSem request for the semaphore.
For both conditions, ERROR_SEM_BUSY is returned.
ERROR_SEM_BUSY is also returned if a thread tries to close a mutex semaphore that it still owns. The thread must first relinquish ownership of the semaphore by calling DosReleaseMutexSem.
Calls to DosOpenMutexSem and DosCloseMutexSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned.
Querying a Mutex Semaphore
An application can use DosQueryMutexSem to determine the current owner of a mutex semaphore and to obtain a count of the number of requests on it. If the mutex semaphore is not owned, the request count is 0.
Any thread in the process that created a mutex semaphore can obtain information about the semaphore by calling DosQueryMutexSem. Threads in other processes can also call DosQueryMutexSem, but they must first gain access to the semaphore by calling DosOpenMutexSem.
If the mutex semaphore exists and is owned, DosQueryMutexSem returns the process and thread identifications of the owner, as well as the request count for the semaphore. The request count is the number of DosRequestMutexSem calls minus the number of DosReleaseMutexSem calls that have been made for the semaphore by the owning thread.
If DosQueryMutexSem returns a request count of 0, the mutex semaphore is unowned.
If the owning process ended without calling DosCloseMutexSem, then ERROR_SEM_OWNER_DIED is returned, and the output parameters contain information about the ended owning process.
Using Muxwait Semaphores
A process that requires exclusive use of several shared resources at once can use a multiple wait (muxwait) semaphore to obtain ownership of all the mutex semaphores protecting the shared resources. A process can also use a muxwait semaphore to wait on a group of event semaphores so that the process continues running whenever events of interest occur.
A muxwait semaphore can refer to up to 64 event or mutex semaphores. An application cannot refer to event and mutex semaphores in a single muxwait semaphore, or include a muxwait semaphore in another muxwait semaphore.
Creating a Muxwait Semaphore
DosCreateMuxWaitSem is used to create muxwait semaphores. This function also opens (obtains access to) the semaphore for the calling process and its threads. Threads in other processes must call DosOpenMuxWaitSem to open the semaphore before they can use it in any other muxwait semaphore function.
All the semaphores in the muxwait list must be created and opened before the muxwait list can be created.
The following code fragment creates five event semaphores and a corresponding array of semaphore records. The array is used to specify the semaphores included in the muxwait semaphore in the subsequent call to DosCreateMuxWaitSem.
#define INCL_DOSSEMAPHORES /* DOS semaphore values */ #define INCL_DOSERRORS /* DOS error values */ #include <os2.h> #include <stdio.h> int main(VOID) { HMUX hmuxHandAny = NULLHANDLE; /* Muxwait handle */ HEV hev[5] = {0}; /* Event semaphores */ SEMRECORD apsr[5] = {{0}}; /* Semaphore records */ APIRET ulrc = NO_ERROR; /* Return code */ ULONG ulLoop = 0; /* Loop count */ ULONG ulSem = 0; for (ulLoop = 0; ulLoop < 5; ulLoop++) { ulrc = DosCreateEventSem((PSZ) NULL, &hev[ulLoop], 0, FALSE); if (ulrc != NO_ERROR) { printf("DosCreateEventSem error: return code = %u\n", ulrc); return 1; } apsr[ulLoop].hsemCur = (HSEM) hev[ulLoop], apsr[ulLoop].ulUser = 0; } /* end for */ ulrc = DosCreateMuxWaitSem((PSZ) NULL, &hmuxHandAny, 5L, /* Number of semaphores in list */ apsr, /* Semaphore list */ DCMW_WAIT_ANY); /* Wait for any semaphore */ if (ulrc != NO_ERROR) { printf("DosCreateMuxWaitSem error: return code = %u\n", ulrc); return 1; } ulrc = DosWaitMuxWaitSem(hmuxHandAny, SEM_IMMEDIATE_RETURN, &ulSem); /* No semaphores have been posted, so we should see a timeout below... */ if (ulrc != ERROR_TIMEOUT) { printf("DosWaitMuxWaitSem error: return code = %u\n", ulrc); return 1; } ulrc = DosDeleteMuxWaitSem(hmuxHandAny, apsr[0].hsemCur); if (ulrc != NO_ERROR) { printf("DosDeleteMuxWaitSem error: return code = %u\n", ulrc); return 1; } ulrc = DosCloseMuxWaitSem(hmuxHandAny); if (ulrc != NO_ERROR) { printf("DosCloseMuxWaitSem error: return code = %u\n", ulrc); return 1; } return NO_ERROR; }
Muxwait semaphores can be defined as either private or shared:
- Private semaphores are always unnamed and are therefore always identified by their handles. They can be used only by threads within a single process.
- Shared semaphores can be either named or unnamed. If named, they can be opened using either the name or the handle. The handle returned by DosOpenMuxWaitSem is then used to identify the semaphore for all other functions. Semaphore names must include the prefix \SEM32\ and must conform to file system naming conventions. Shared semaphores can be used by threads in multiple processes.
There is a system-wide limit of 65536 (64K) shared semaphores (including mutex, event, and muxwait semaphores); in addition, each process can have up to 65536 (64K) private semaphores.
The following conditions apply to the kinds of semaphores that can be included in a muxwait-semaphore list:
- The list must contain either mutex semaphores or event semaphores. It cannot contain both at the same time and it cannot contain other muxwait semaphores.
- If the muxwait semaphore is shared, then all the semaphores in the list must also be shared.
- If the muxwait semaphore is private, then the semaphores in its list can be either private or shared.
If any of these conditions is violated, ERROR_WRONG_TYPE is returned.
The muxwait list can contain a maximum of 64 event semaphores or mutex semaphores. If an attempt is made to exceed this maximum, ERROR_TOO_MANY_SEMAPHORES is returned.
If the owners of any of the mutex semaphores in the muxwait semaphore list have ended without releasing them, ERROR_SEM_OWNER_DIED is returned. The thread should call DosQueryMutexSem for each mutex semaphore in the muxwait-semaphore list so that it can determine which semaphores are in the Owner Died state.
Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed must be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem.
OS/2 maintains a usage count for each semaphore. DosCreateMuxWaitSem initializes the usage count to 1. Thereafter, each call to DosOpenMuxWaitSem increments the count, and each call to DosCloseMuxWaitSem decrements it.
One parameter of this function is a pointer to an array of SEMRECORD data structures. Each data structure contains one semaphore record for each of the semaphores to be included in the muxwait semaphore. A semaphore record contains the handle and a programmer-defined identifier for that semaphore.
Opening a Muxwait Semaphore
Processes other than the semaphore-creating process must use DosOpenMuxWaitSem to gain access to the muxwait semaphore before they can use the semaphore in any other muxwait semaphore function. All of the threads that belong to the process that creates the muxwait semaphore have immediate access to the semaphore.
The following code fragment opens a system muxwait semaphore.
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> #include <stdio.h> #include <string.h> UCHAR ucName[40]; /* Semaphore name */ HMUX hmux; /* Muxwait semaphore handle */ APIRET ulrc; /* Return code */ strcpy(ucName, "\\SEM32\\MUXWAIT1"); /* Name of the system muxwait semaphore */ ulrc = DosOpenMuxWaitSem(ucName, &hmux); if (ulrc != 0) { printf("DosOpenMuxWaitSem error: return code = %ld", ulrc); return; }
On successful return, hmux contains the handle of the system muxwait semaphore.
Opening a muxwait semaphore does not open the semaphores in its muxwait list. A process must open each of the semaphores included in a muxwait semaphore before it opens the muxwait semaphore. Otherwise, DosOpenMuxWaitSem returns the ERROR_INVALID_HANDLE error value to the calling function.
Access to semaphores is on a per-process basis. Therefore, a semaphore that has been opened by one thread in a process is open to all other threads in that process as well.
Note that DosOpenMuxWaitSem merely provides access to a muxwait semaphore. In order to wait for a muxwait semaphore to clear, a thread must call DosWaitMuxWaitSem.
When a process no longer requires access to a muxwait semaphore, it closes the semaphore by calling DosCloseMuxWaitSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2.
Each call to DosOpenMuxWaitSem increments the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is decremented by each call to DosCloseMuxWaitSem. When the usage count reaches 0, the semaphore is deleted by OS/2.
Calls to DosOpenMuxWaitSem and DosCloseMuxWaitSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned.
Even if the owner of a mutex semaphore in a muxwait-semaphore list has ended without releasing the semaphore, the muxwait semaphore is still opened. Subsequent calls to the muxwait semaphore will return ERROR_SEM_OWNER_DIED. But because the process has opened the semaphore, it can then call DosQueryMuxWaitSem to identify all the mutex semaphores in the muxwait list. Next, the process can call DosQueryMutexSem for each mutex semaphore in the list to find out which ones are in the Owner Died state. Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed should be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem.
Closing a Muxwait Semaphore
When a process no longer requires access to a muxwait semaphore, it closes the semaphore by calling DosCloseMuxWaitSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2.
Each call to DosCloseMuxWaitSem decrements the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is incremented by each call to DosOpenMuxWaitSem. When the usage count reaches 0, the semaphore is deleted by OS/2.
The call to DosCloseMuxWaitSem that decrements the usage count to 0 and causes the semaphore to be deleted is referred to as the final close. If a thread attempts to perform the final close for a semaphore while another thread in the same process is still waiting for it, ERROR_SEM_BUSY is returned.
Calls to DosOpenMuxWaitSem and DosCloseMuxWaitSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned.
Waiting for a Muxwait Semaphore
A thread can wait on a muxwait semaphore by using DosWaitMuxWaitSem.
Any thread in the process that created a muxwait semaphore can wait for the semaphore to clear by calling DosWaitMuxWaitSem. Threads in other processes can also call DosWaitMuxWaitSem, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem.
The following code fragment waits for a muxwait semaphore to clear. Assume that the handle of the semaphore has been placed into hmux already. ulTimeout is the number of milliseconds that the calling thread will wait for the muxwait semaphore to clear. If the specified muxwait semaphore is not cleared during this time interval, the request times out.
#define INCL_DOSSEMAPHORES /* Semaphore values */ #include <os2.h> #include <stdio.h> HMUX hmux; /* Muxwait semaphore handle */ ULONG ulTimeout; /* Number of milliseconds to wait */ ULONG ulUser; /* User field for the semaphore that was */ /* posted or released (returned) */ APIRET ulrc; /* Return code */ ulTimeout = 60000; /* Wait for a maximum of 1 minute */ ulrc = DosWaitMuxWaitSem(hmux, ulTimeout, &ulUser); if (ulrc == ERROR_TIMEOUT) { printf("DosWaitMuxWaitSem call timed out"); return; } if (ulrc == ERROR_INTERRUPT) { printf("DosWaitMuxWaitSem call was interrupted"); return; } if (ulrc != 0) { printf("DosWaitMuxWaitSem error: return code = %ld", ulrc); return; }
On successful return, the ulUser variable contains the user identifier of the semaphore that caused the wait to terminate. If the caller had to wait for all the semaphores within the muxwait semaphore to clear, then the value corresponds to the last semaphore within the muxwait semaphore to clear. If the caller had to wait for any semaphore with the muxwait semaphore to clear, then the value corresponds to that semaphore.
An application can use the DCMW_WAIT_ANY flag in DosCreateMuxWaitSem to block a thread until any one of the event or mutex semaphores included in the muxwait semaphore is posted or released. If the muxwait semaphore refers to mutex semaphores, the thread only gains ownership of the one mutex semaphore that was released.
An application can use the DCMW_WAIT_ALL flag in DosCreateMuxWaitSem to block a thread until all of the event or mutex semaphores included in the muxwait semaphore are posted or released. If the muxwait semaphore refers to mutex semaphores, the thread does not gain ownership of any of the mutex semaphores until they are all released. When all are released, the thread becomes owner of all the mutex semaphores included in the muxwait semaphore. If the muxwait semaphore refers to event semaphores, the thread will not run until all of the event semaphores are in the posted state at the same time. This is because event semaphores in a muxwait list are level-triggered, unlike individual event semaphores, which are edge-triggered.
For example, suppose that a thread is waiting for five event semaphores in a muxwait list to be posted. The first semaphore is posted and then reset. Next, the remaining semaphores are all posted, and they remain in the posted state. The thread that is waiting for the muxwait semaphore will not run until the first semaphore is posted again.
If an application specifies the DCMW_WAIT_ANY flag when the semaphore is created, DosWaitMuxWaitSem returns the programmer-defined identifier of the semaphore that is subsequently posted or released. If an application specifies the DCMW_WAIT_ALL flag, DosWaitMuxWaitSem returns the programmer-defined identifier of the last semaphore that was posted or released.
The ulTimeout parameter places a limit on the amount of time a thread blocks on a DosWaitMuxWaitSem request. If the time limit is reached before the semaphore has cleared, ERROR_TIMEOUT is returned. If SEM_IMMEDIATE_RETURN is specified as the time limit, DosWaitMuxWaitSem returns without blocking the thread. The thread can then go on to perform other operations and call DosWaitMuxWaitSem again later to wait for the event or mutex semaphores in the muxwait list to be posted or released. If a time limit of SEM_INDEFINITE_WAIT is specified, the thread waits (is blocked) indefinitely. If the thread is unblocked by an external event while it is waiting for the muxwait semaphore (as when a "no wait" I/O request has just been completed), DosWaitMuxWaitSem returns ERROR_INTERRUPT.
When a thread is waiting for any one of the semaphores in a muxwait list to be posted or released, the semaphores are checked in the order in which they are defined in the list.
Waiting for Multiple Event Semaphores
The following information pertains only to muxwait semaphores that consist of multiple event semaphores:
Unlike individual event semaphores, which are edge-triggered, event semaphores in a muxwait list are level-triggered. This means that if a thread is waiting for all of the event semaphores in the muxwait list, it will not run until all of the event semaphores are in the posted state at the same time.
For example, a thread is waiting for five event semaphores in a muxwait list to be posted. The first semaphore is posted and then reset. Next, the remaining semaphores are all posted, and they remain in the posted state. The thread that is waiting for the muxwait semaphore will not run until the first semaphore is posted again.
Waiting for Multiple Mutex Semaphores
The following information pertains only to muxwait semaphores that consist of multiple mutex semaphores:
- If a thread is waiting for all of the mutex semaphores in a muxwait list to be released, it does not receive ownership of any of the semaphores until all of the semaphores have been released.
- If a thread is waiting for any one of the mutex semaphores in a muxwait list, then the thread gains ownership only of the first mutex semaphore that is released. The ownership of all other mutex semaphores in the muxwait list remains unchanged.
- If two threads have the same priority, then a thread that is waiting for a mutex semaphore in a muxwait list takes precedence over a thread that has requested ownership of only the individual semaphore, provided all other mutex semaphores in the muxwait list have been released. For example, a mutex semaphore that is part of a muxwait semaphore is released. One thread has requested ownership of that single mutex semaphore, and another thread with the same priority is waiting for the muxwait semaphore that contains the same mutex semaphore. If all of the other mutex semaphores in the muxwait list are unowned and ready to be given to the muxwait semaphore, then the thread that is waiting for the muxwait semaphore will run first.
- If the owners of any of the mutex semaphores in the muxwait semaphore list have ended without releasing them, ERROR_SEM_OWNER_DIED is returned. The thread must then call DosQueryMuxWaitSem to obtain the records of all the semaphores in the muxwait list. Next, the thread must call DosQueryMutexSem for each mutex semaphore in the muxwait-semaphore list so that it can determine which semaphores are in the Owner Died state.
Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed should be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem.
- If any of the mutex semaphores in the muxwait list are owned by the calling thread, ERROR_MUTEX_OWNED is returned.
Adding a Semaphore to a Muxwait List
An application uses DosAddMuxWaitSem to add semaphores to a muxwait semaphore that has already been created, even while threads are waiting on the muxwait semaphore.
Any thread in the process that created a muxwait semaphore can add a mutex semaphore or an event semaphore to the muxwait list by calling DosAddMuxWaitSem. Threads in other processes can also use this function, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem.
A maximum of 64 semaphores can be included in a muxwait-semaphore list. If an attempt is made to exceed this maximum, ERROR_TOO_MANY_SEMAPHORES is returned.
All of the semaphores in a muxwait-semaphore list must be of the same type. That is, if a mutex semaphore is being added, then the other semaphores in the list must be mutex semaphores. If an event semaphore is being added, then the other semaphores in the list must be event semaphores. A shared muxwait semaphore can contain only shared semaphores in its list. A private muxwait semaphore can contain both private and shared semaphores. If any of these conditions is violated, ERROR_WRONG_TYPE is returned.
If the semaphore is successfully added to the muxwait list, DosAddMuxWaitSem checks to see whether each thread that is waiting for the muxwait semaphore has the newly added semaphore open in its process. The muxwait semaphore is invalid for any waiting threads that do not have the newly added semaphore open in their process; these threads are unblocked with a return code of ERROR_INVALID_HANDLE. Any processes that opened the muxwait semaphore before the add operation and that do not have the new semaphore open, will have to open the new semaphore before making any further use of the muxwait semaphore. Any future calls concerning the muxwait semaphore by processes that do not have the new semaphore open will have ERROR_INVALID_HANDLE returned until the new semaphore is opened.
A thread that receives a return code of ERROR_INVALID_HANDLE can take the following corrective action:
- First, the thread can obtain the records of all the semaphores in the muxwait list by calling DosQueryMuxWaitSem.
- Next, it can query each semaphore in the muxwait list, using DosQueryMutexSem or DosQueryEventSem, to find out which semaphore is not open to its process.
- Finally, it can open the semaphores that are not open by calling DosOpenMutexSem or DosOpenEventSem.
As soon as this semaphore is opened, the muxwait semaphore becomes valid again for the process, as long as no other changes have been made to the muxwait list to make it invalid. However, in order to successfully wait for the muxwait semaphore, the process must call DosWaitMuxWaitSem again.
A semaphore must be open for a process before the process can add that semaphore to a muxwait semaphore. If it is not open and a thread is waiting on the muxwait semaphore, DosAddMuxWaitSem returns ERROR_INVALID_HANDLE to the process adding the new semaphore, and the waiting thread continues waiting.
Deleting a Semaphore from a Muxwait List
An application can delete semaphores from a muxwait semaphore by using DosDeleteMuxWaitSem.
Any thread in the process that created a muxwait semaphore can delete a mutex or event semaphore from the muxwait list by calling DosDeleteMuxWaitSem. Threads in other processes can also use this function, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem.
Semaphores can be deleted from the muxwait list even while threads are currently waiting for the semaphore. If the deleted semaphore is the only one in the muxwait list that has not yet been posted or released, then threads that are waiting for the muxwait semaphore are unblocked. Also, if the deleted semaphore happens to be the last one that a particular thread was waiting for, that thread is unblocked. Also, if the deleted semaphore is the last one in the muxwait list (that is, if the list is now empty), then all the threads that are waiting for the muxwait semaphore are unblocked.
Querying a Muxwait Semaphore
Processes use DosQueryMuxWaitSem to obtain the semaph ore records for each of the semaphores included in the muxwait semaphore.
Any thread in the process that created a muxwait semaphore can obtain information about the semaphores in the muxwait list by calling DosQueryMuxWaitSem. Threads in other processes can also use this function, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem.
An application must provide this function with an array in which to store the semaphore records. If the array is not large enough to hold all of the semaphore records that are in the muxwait list, then ERROR_PARAM_TOO_SMALL is returned, and the record-counting parameter of DosQueryMuxWaitSem will contain the number of semaphore records that are in the muxwait list. The calling thread can then allocate the correct amount of space and call DosQueryMuxWaitSem again with the correct amount of space for the list of records.
If the owner of any mutex semaphore in the muxwait-semaphore list has ended without releasing the semaphore, the records of all the semaphores in the list are still returned, but DosQueryMuxWaitSem also returns ERROR_SEM_OWNER_DIED. The calling thread can call DosQueryMutexSem for each mutex semaphore in the muxwait-semaphore list so that it can determine which semaphores are in the Owner Died state. The process can then close the unowned mutex semaphores.
Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed should be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem.
If the specified muxwait semaphore does not exist, ERROR_INVALID_HANDLE is returned.