CPGuide - Queues: Difference between revisions
No edit summary |
|||
(One intermediate revision by one other user not shown) | |||
Line 2: | Line 2: | ||
{{CPGuide}} | {{CPGuide}} | ||
Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Queues are one of three forms of interprocess communication (IPC), the other forms of IPC being semaphores and pipes. | Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Queues are one of three forms of interprocess communication (IPC), the other forms of IPC being semaphores and pipes. | ||
This chapter describes how to create and use queues. Queues enable one or more processes to transfer data to a specific target process. | This chapter describes how to create and use queues. Queues enable one or more processes to transfer data to a specific target process. | ||
Note: The queues used for interprocess communication should not to be confused with the message queues used for communication between Presentation Manager (PM) and PM applications, nor with the printer queues used by the print spooler in managing print jobs. | Note: The queues used for interprocess communication should not to be confused with the message queues used for communication between Presentation Manager (PM) and PM applications, nor with the printer queues used by the print spooler in managing print jobs. | ||
The following topics are related to the information in this chapter: | |||
*Memory (shared memory) | |||
*Program execution and control | |||
*Semaphores | |||
*Pipes | |||
==About Queues== | ==About Queues== | ||
A queue is a named, ordered list of elements that is used to pass information between threads of the same (related) process or between different (unrelated) processes. | A queue is a named, ordered list of elements that is used to pass information between threads of the same (related) process or between different (unrelated) processes. | ||
Processes pass information to a queue in the form of elements. An element is a 32-bit unit of information. Queue elements can be values, flags, pointers to shared memory regions, anything that can fit into 32 bits. The format of a queue element depends entirely on the process that creates the queue (the queue owner). Only the queue owner can read elements from the queue; other processes can only write to the queue. Reading an element automatically removes it from the queue. | Processes pass information to a queue in the form of elements. An element is a 32-bit unit of information. Queue elements can be values, flags, pointers to shared memory regions, anything that can fit into 32 bits. The format of a queue element depends entirely on the process that creates the queue (the queue owner). Only the queue owner can read elements from the queue; other processes can only write to the queue. Reading an element automatically removes it from the queue. | ||
The process that creates the queue is known as the server process of the queue. The other processes that access the queue are known as client processes. | The process that creates the queue is known as the server process of the queue. The other processes that access the queue are known as client processes. | ||
The owner of the queue (the server process) can choose the order in which to read incoming information and can examine queue elements without removing them from the queue. Queue elements can be added and accessed in First-In-First-Out (FIFO), Last-In-First-Out (LIFO), or priority-based order. | The owner of the queue (the server process) can choose the order in which to read incoming information and can examine queue elements without removing them from the queue. Queue elements can be added and accessed in First-In-First-Out (FIFO), Last-In-First-Out (LIFO), or priority-based order. | ||
Any process that has the name of a queue can open the queue and write to it. The processes writing elements to the queue must use the format determined by the queue owner. | Any process that has the name of a queue can open the queue and write to it. The processes writing elements to the queue must use the format determined by the queue owner. | ||
Queues are very efficient. They pass only 32-bit sized elements, rather than large data structures. However, queues can be used only for one-way communication, because a client process can write to a queue but cannot read from one. | Queues are very efficient. They pass only 32-bit sized elements, rather than large data structures. However, queues can be used only for one-way communication, because a client process can write to a queue but cannot read from one. | ||
Typically, processes use queues to transfer information about the contents of a shared memory. The elements in the queue could contain the address and length of data areas in shared memory objects. The sending process allocates a shared memory object and gives access to the shared memory to the queue owner. The sending process can free the shared memory after writing the elements to the queue because the shared memory will not be deallocated until the queue owner frees it. | Typically, processes use queues to transfer information about the contents of a shared memory. The elements in the queue could contain the address and length of data areas in shared memory objects. The sending process allocates a shared memory object and gives access to the shared memory to the queue owner. The sending process can free the shared memory after writing the elements to the queue because the shared memory will not be deallocated until the queue owner frees it. | ||
Any thread in the process that owns the queue can examine queue elements without removing them. This is called peeking at the queue. | Any thread in the process that owns the queue can examine queue elements without removing them. This is called peeking at the queue. | ||
OS/2 supplies the process identifier of the process that writes an element to the queue, so that a process reading from or peeking at the queue can determine the origin of the element. The process identifier is returned as part of a REQUESTDATA data structure. Threads can use the ulData field of the REQUESTDATA data structure to pass additional information about the queue element. | OS/2 supplies the process identifier of the process that writes an element to the queue, so that a process reading from or peeking at the queue can determine the origin of the element. The process identifier is returned as part of a [[REQUESTDATA]] data structure. Threads can use the ulData field of the [[REQUESTDATA]] data structure to pass additional information about the queue element. | ||
If the queue is empty when a process attempts to read from it, the process can either wait for an element to become available or continue executing without reading from the queue. Semaphores can be used to indicate when an element is in the queue. | If the queue is empty when a process attempts to read from it, the process can either wait for an element to become available or continue executing without reading from the queue. Semaphores can be used to indicate when an element is in the queue. | ||
===Queues and Semaphores=== | ===Queues and Semaphores=== | ||
If a process manages only one queue, it typically waits for an element to become available. However, if a process manages several queues, waiting for one queue means that other queues cannot be read. To avoid wasting time while waiting, a process can supply an event semaphore when it calls DosReadQueue or DosPeekQueue. The process can then continue to execute without actually reading an element from the queue, because DosWriteQueue will post the semaphore only when an element is ready. The semaphore remains posted until someone resets it; usually the queue owner process resets the semaphore after it reads all the available information from the queue. | If a process manages only one queue, it typically waits for an element to become available. However, if a process manages several queues, waiting for one queue means that other queues cannot be read. To avoid wasting time while waiting, a process can supply an event semaphore when it calls [[DosReadQueue]] or [[DosPeekQueue]]. The process can then continue to execute without actually reading an element from the queue, because [[DosWriteQueue]] will post the semaphore only when an element is ready. The semaphore remains posted until someone resets it; usually the queue owner process resets the semaphore after it reads all the available information from the queue. | ||
If a process uses a unique semaphore for each queue, it can use [[DosWaitMuxWaitSem]] to wait for the first queue to receive an element. | |||
Only one semaphore is permitted per queue. | |||
===Queue Servers and Clients=== | ===Queue Servers and Clients=== | ||
The server process and its threads have certain queue-managing privileges. Only the server process and its threads can: | The server process and its threads have certain queue-managing privileges. Only the server process and its threads can: | ||
*Examine queue elements without removing them ([[DosPeekQueue]]) | |||
*Remove elements from the queue ([[DosReadQueue]]) | |||
*Purge all the elements in a queue ([[DosPurgeQueue]]) | |||
*Write to the queue without opening it first ([[DosWriteQueue]]) | |||
*Delete the queue ([[DosCloseQueue]]). | |||
Both server and client processes can query the number of elements in the queue using [[DosQueryQueue]]. | |||
Client processes can query the queue ([[DosQueryQueue]]) and add elements to it ([[DosWriteQueue]]), but they must first gain access to the queue by calling [[DosOpenQueue]]. When a client process is finished with a queue, it ends its access to the queue by calling [[DosCloseQueue]]. (Note that, unlike the server process and its threads, a client process cannot use [[DosCloseQueue]] to delete a queue.) | |||
When a queue is opened by a client process, an access count is set to 1. Each client process has its own access count. The access count is incremented whenever a thread in a process opens the queue and decremented whenever a thread in the process closes the queue. Access to the queue by the client process ends when the access count for the process reaches 0. When the server process closes the queue, the queue is terminated and removed from the system. | |||
===Queue Element Order=== | ===Queue Element Order=== | ||
DosReadQueue reads either a specified element or the first element in the queue. The first element in the queue depends on the queue type, which is specified when the queue is created. A queue can have FIFO, LIFO, or priority ordering. | DosReadQueue reads either a specified element or the first element in the queue. The first element in the queue depends on the queue type, which is specified when the queue is created. A queue can have FIFO, LIFO, or priority ordering. | ||
Priority values range from 0 (lowest priority) through 15 (highest priority). The writing process assigns a priority to a queue element when the element is written to the queue. DosReadQueue reads elements from the queue in descending order of priority, regardless of the order in which DosWriteQueue placed the elements in the queue. Elements with equal priority are read in FIFO order. | |||
===Obtaining Information about Queues and Queue Elements=== | ===Obtaining Information about Queues and Queue Elements=== | ||
Any thread in the process that owns the queue can use DosPeekQueue to examine the elements in the queue to determine which one to actually read. Each call to DosPeekQueue returns the identifier of the next element in the queue, so the function can be called repeatedly to move through the queue. The identifier of the desired element can then be supplied to DosReadQueue. | Any thread in the process that owns the queue can use DosPeekQueue to examine the elements in the queue to determine which one to actually read. Each call to DosPeekQueue returns the identifier of the next element in the queue, so the function can be called repeatedly to move through the queue. The identifier of the desired element can then be supplied to DosReadQueue. | ||
Any process that has opened a queue can use DosQueryQueue to determine the number of elements in the queue. This function also returns an error value if the queue owner has closed the queue. | Any process that has opened a queue can use DosQueryQueue to determine the number of elements in the queue. This function also returns an error value if the queue owner has closed the queue. | ||
==Using Queues== | ==Using Queues== | ||
Queues are useful for a process to manage input from other processes. The examples in the following sections show how to create and use queues. | Queues are useful for a process to manage input from other processes. The examples in the following sections show how to create and use queues. | ||
;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. | ;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 a Queue=== | ===Creating a Queue=== | ||
A thread creates a queue by using DosCreateQueue and specifying a queue name and the queue type as arguments. The queue name must be unique and have the following form: | A thread creates a queue by using DosCreateQueue and specifying a queue name and the queue type as arguments. The queue name must be unique and have the following form: | ||
\QUEUES\QueName | |||
The "\QUEUES\" is required, though it need not be uppercase. It is not a subdirectory. | |||
The "\QUEUES\" is required, though it need not be uppercase. It is not a subdirectory | |||
The QueName parameter must conform to the rules for OS/2 file names, although no actual file is created for the queue. | |||
The | The process that creates the queue is known as the server process of the queue. The other processes that access the queue are known as client processes. | ||
The following code fragment creates a FIFO queue named \queues\sample.que: | |||
<pre> | <pre> | ||
#define INCL_DOSQUEUES /* Queue values */ | |||
#include <os2.h> | |||
HQUEUE hq; | |||
DosCreateQueue(&hq, QUE_FIFO | | |||
QUE_CONVERT_ADDRESS, | |||
"\\queues\\sample.que"); | |||
</pre> | |||
When the server process creates the queue, it determines whether the ordering of queue elements is based on arrival (FIFO or LIFO) or priority. If the ordering is based on priority, then priority values must be assigned whenever data is added to the queue. | |||
The server must also specify whether OS/2 is to convert 16-bit addresses of elements placed in the queue by 16-bit processes to 32-bit addresses. | |||
After a process has created a queue, any thread in that process can access the queue with equal authority. | |||
===Allocating Memory for Queue Data=== | ===Allocating Memory for Queue Data=== | ||
When queues are used only to pass the addresses to data rather than the data itself, processes must allocate shared memory objects for storing queue data. The two most common methods of storing queue data are: | When queues are used only to pass the addresses to data rather than the data itself, processes must allocate shared memory objects for storing queue data. The two most common methods of storing queue data are: | ||
*Using a named shared memory object - for related processes | *Using a named shared memory object - for related processes | ||
*Using unnamed shared memory objects - for unrelated processes | *Using unnamed shared memory objects - for unrelated processes | ||
====Named Shared Memory Objects==== | ====Named Shared Memory Objects==== | ||
Related processes generally use a single named shared memory object for storing queue data. The server process allocates the memory object by calling DosAllocSharedMem. Care must be taken to ensure that the memory object is large enough to meet application requirements. | Related processes generally use a single named shared memory object for storing queue data. The server process allocates the memory object by calling DosAllocSharedMem. Care must be taken to ensure that the memory object is large enough to meet application requirements. | ||
The name of the shared memory object is established by agreement among the server and the client processes. For simplicity, the name can be the same as the queue name, except that the prefix \SHAREMEM\ must be used instead of \QUEUES\. | The name of the shared memory object is established by agreement among the server and the client processes. For simplicity, the name can be the same as the queue name, except that the prefix \SHAREMEM\ must be used instead of \QUEUES\. | ||
A client process accesses the named shared memory object by calling DosGetNamedSharedMem. It must then call DosOpenQueue to gain access to the queue of the server. | A client process accesses the named shared memory object by calling DosGetNamedSharedMem. It must then call DosOpenQueue to gain access to the queue of the server. | ||
Before the server process ends, it releases the memory object by calling DosFreeMem. | Before the server process ends, it releases the memory object by calling DosFreeMem. | ||
====Unnamed Shared Memory Objects==== | ====Unnamed Shared Memory Objects==== | ||
Unrelated processes generally use unnamed shared memory objects for storing queue data. This makes it possible for a client process to store data in a shared memory object without knowing its name. To use unnamed shared memory objects for storing queue data, the server process must take the following steps whenever it is called by a client: | Unrelated processes generally use unnamed shared memory objects for storing queue data. This makes it possible for a client process to store data in a shared memory object without knowing its name. To use unnamed shared memory objects for storing queue data, the server process must take the following steps whenever it is called by a client: | ||
#Save the process identification (PID) of the client process. | |||
#Allocate an unnamed shared memory object for the data of the client process by calling DosAllocSharedMem. | |||
#Give the client process the capability of accessing the memory object by calling DosGiveSharedMem with the client's PID. | |||
The client process must then call DosOpenQueue to gain access to the server's queue. | |||
After each client completes its queue requests or ends, the server calls DosFreeMem to release the client's memory object. | |||
After each client completes its queue requests or ends, the server calls DosFreeMem to release the client's memory object. | |||
===Opening a Queue=== | ===Opening a Queue=== | ||
Once the queue is created, the server process of the queue and the threads of the server process have immediate access to the queue and can proceed to access the queue. A client process must request access to a queue by calling DosOpenQueue. Once the queue is open, the client process can add an element to the queue with DosWriteQueue, and it can query the number of elements in the queue with DosQueryQueue | Once the queue is created, the server process of the queue and the threads of the server process have immediate access to the queue and can proceed to access the queue. A client process must request access to a queue by calling DosOpenQueue. Once the queue is open, the client process can add an element to the queue with DosWriteQueue, and it can query the number of elements in the queue with DosQueryQueue. | ||
The | DosOpenQueue retrieves the queue handle and the process identifier of the queue owner. The function also increments the queue's access count. | ||
The following code fragment shows how another process would open the queue created with DosCreateQueue. | |||
<pre> | <pre> | ||
#define INCL_DOSQUEUES | |||
#include<os2.h> | |||
#define HF_STDOUT 1 /* Standard output handle */ | |||
HQUEUE hq; | |||
PID pidOwner; | |||
ULONG ulWritten; | |||
APIRET ulrc; | |||
ulrc = DosOpenQueue(&pidOwner, | |||
&hq, | |||
"\\queues\\sample.que"); | |||
if (ulrc) { | |||
DosWrite(HF_STDOUT, | |||
"\r\n Queue open failed. \r\n", | |||
24, | |||
&ulWritten); | |||
DosExit(EXIT_PROCESS, | |||
1); | |||
} | |||
else { | |||
DosWrite(HF_STDOUT, | |||
"\r\n Queue opened. \r\n", | |||
19, | |||
&ulWritten); | |||
} | |||
</pre> | </pre> | ||
When it is finished with the queue, a thread in the client process ends its access by calling DosCloseQueue. DosCloseQueue decrements the access count for the process each time it is called. When the access count reaches 0, the connection between the client process and the queue is terminated. | |||
After a process has opened a queue, any thread in that process can access the queue with equal authority. | |||
After a process has opened a queue, any thread in that process can access the queue with equal authority | |||
;Note: If a queue was created by a call to the 16-bit DosCreateQueue, then it is not accessible to 32-bit DosOpenQueue requests, and ERROR_QUE_PROC_NO_ACCESS will be returned. | |||
===Writing to a Queue=== | ===Writing to a Queue=== | ||
The server process and any of its threads can add an element to a queue simply by calling DosWriteQueue. A client process, however, must first request access to the queue by calling DosOpenQueue. | The server process and any of its threads can add an element to a queue simply by calling DosWriteQueue. A client process, however, must first request access to the queue by calling DosOpenQueue. | ||
Processes that communicate by passing the addresses of shared memory objects through the queue must have a shared memory object that they each have access to. Once a process opens the queue, it can allocate shared memory by using DosAllocMem with the OBJ_GIVEABLE attribute and then give the shared memory to the queue owner with DosGiveSharedMem. | Processes that communicate by passing the addresses of shared memory objects through the queue must have a shared memory object that they each have access to. Once a process opens the queue, it can allocate shared memory by using DosAllocMem with the OBJ_GIVEABLE attribute and then give the shared memory to the queue owner with DosGiveSharedMem. | ||
A process that has opened a queue can write to the queue by using DosWriteQueue. The writing process must create elements in a form that the queue owner can read. | A process that has opened a queue can write to the queue by using DosWriteQueue. The writing process must create elements in a form that the queue owner can read. | ||
The following code fragment adds an element to a queue. Assume that the caller has placed the handle of the queue into QueueHandle already. Assume also that DataBuffer has been set to point to a data element in shared memory, and that DataLength has been set to contain the length of the data element in shared memory. | The following code fragment adds an element to a queue. Assume that the caller has placed the handle of the queue into QueueHandle already. Assume also that DataBuffer has been set to point to a data element in shared memory, and that DataLength has been set to contain the length of the data element in shared memory. | ||
<pre> | <pre> | ||
#define INCL_DOSQUEUES /* Queue values */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HQUEUE hqQueueHandle; /* Queue handle */ | |||
ULONG ulRequest; /* Request-identification data */ | |||
ULONG ulDataLength; /* Length of element being added */ | |||
PVOID pDataBuffer; /* Element being added */ | |||
ULONG ulElemPriority; /* Priority of element being added */ | |||
APIRET ulrc; /* Return code */ | |||
ulRequest = 0; /* Assume that no special data is being */ | |||
/* sent along with this write request */ | |||
ulElemPriority = 0; /* For priority-based queues: add the */ | |||
/* new queue element at the logical end */ | |||
/* of the queue */ | |||
ulrc = DosWriteQueue(hqQueueHandle, | |||
ulRequest, | |||
ulDataLength, | |||
pDataBuffer, | |||
ulElemPriority); | |||
if (ulrc != 0) { | |||
printf("DosWriteQueue error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
Once the process has written to the queue, it frees the shared memory. However, the memory will not be freed until the queue owner also frees it. | |||
If the queue was created as a priority-based queue (as specified in the QueueFlags parameter of DosCreateQueue), then the priority of the element that is being added must be specified. | |||
If the queue | If the server process has ended, or if it has closed the queue before DosWriteQueue is called, then ERROR_QUE_INVALID_HANDLE is returned. | ||
===Reading from a Queue=== | ===Reading from a Queue=== | ||
The queue owner (server process) and its threads can read an element from the queue by using DosReadQueue. The owner can read the first element in the queue by specifying 0 as the element number. Alternatively, the owner can read a particular element in the queue by specifying an element code returned from DosPeekQueue. This function is not available to client processes | The queue owner (server process) and its threads can read an element from the queue by using DosReadQueue. The owner can read the first element in the queue by specifying 0 as the element number. Alternatively, the owner can read a particular element in the queue by specifying an element code returned from DosPeekQueue. This function is not available to client processes. | ||
DosReadQueue can either remove queue elements in the order that was specified when the queue was created (FIFO, LIFO, or priority), or it can use an element identifier from DosPeekQueue as input to remove a previously examined element. | |||
The following code fragment reads an element from the queue. Assume that the caller has placed the handle of the queue into QueueHandle already and that the identifier of the process that owns the queue has been placed into OwningPID already. | |||
<pre> | <pre> | ||
#define INCL_DOSQUEUES /* Queue values */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HQUEUE hqQueueHandle; /* Queue handle */ | |||
REQUESTDATA rqRequest; /* Request-identification data */ | |||
ULONG ulDataLength; /* Length of element received */ | |||
PULONG pulDataAddress; /* Address of element received */ | |||
ULONG ulElementCode; /* Request a particular element */ | |||
BOOL32 bNoWait; /* No wait if queue is empty */ | |||
BYTE bElemPriority; /* Priority of element received */ | |||
HEV hevSemHandle; /* Semaphore handle */ | |||
PID pidOwningPID; /* PID of queue owner */ | |||
APIRET ulrc; /* Return code */ | |||
rqRequest.pid = pidOwningPID; /* Set request data block to indicate */ | |||
/* queue owner */ | |||
ulElementCode = 0; /* Indicate that the read should start */ | |||
/* at the front of the queue */ | |||
bNoWait = 0; /* Indicate that the read should wait */ | |||
/* if the queue is currently empty */ | |||
hevSemHandle = 0; /* Unused since this is a call that */ | |||
/* waits synchronously */ | |||
ulrc = DosReadQueue(hqQueueHandle, | |||
&rqRequest, | |||
&ulDataLength, | |||
(PVOID *) &pulDataAddress, | |||
ulElementCode, | |||
bNoWait, | |||
&bElemPriority, | |||
hevSemHandle); | |||
if (ulrc != 0) { | |||
printf("DosReadQueue error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
On successful return, DataLength contains the size of the element on the queue that is pointed to by the pointer within DataAddress, ElemPriority has been updated to contain the priority of the queue element pointed to by DataAddress, and Request.ulData contains any special data that the DosWriteQueue caller placed into the queue. | |||
If the queue is empty and NoWait is set to DCWW_WAIT (0), the calling thread waits until an element is placed in the queue. If the queue is empty and NoWait is set to DCWW_NOWAIT (1), DosReadQueue returns immediately with ERROR_QUE_EMPTY. | |||
If NoWait is set to DCWW_NOWAIT, an event semaphore must be provided so that the calling thread can determine when an element has been placed in the queue. The semaphore is created by calling DosCreateEventSem, and its handle is supplied as a DosReadQueue parameter. The first time an event semaphore handle is supplied in a DosReadQueue or DosPeekQueue request for which DCWW_NOWAIT has been specified for a particular queue, the handle is saved by the system. The same handle must be supplied in all subsequent DosReadQueue and DosPeekQueue requests that are called for that queue; if a different handle is supplied, ERROR_INVALID_PARAMETER is returned. | |||
If | |||
When a client process adds an element to the queue, the system automatically opens the semaphore (if necessary) and posts it. The server can either call DosQueryEventSem periodically to determine whether the semaphore has been posted, or it can call DosWaitEventSem. DosWaitEventSem causes the calling thread to block until the semaphore is posted. | |||
After the event semaphore has been posted, the calling thread must call DosReadQueue again to remove the newly added queue element. | |||
If QUE_CONVERT_ADDRESS is specified in the call to DosCreateQueue, OS/2 will automatically convert 16-bit addresses to 32-bit addresses. | |||
===Peeking at a Queue=== | ===Peeking at a Queue=== | ||
The server process and its threads can examine a queue element by calling DosPeekQueue. This function is not available to client processes. | The server process and its threads can examine a queue element by calling DosPeekQueue. This function is not available to client processes. | ||
Unlike DosReadQueue, DosPeekQueue does not remove the element from the queue. | Unlike DosReadQueue, DosPeekQueue does not remove the element from the queue. | ||
DosPeekQueue can either examine elements in the order that was specified when the queue was created (FIFO, LIFO, or priority), or it can examine the next element in the queue after a previous DosPeekQueue request has been called. By making multiple DosPeekQueue requests, the server process can search through a queue, examining each element in turn. When it locates the element it is searching for, the server process can remove the element from the queue by calling DosReadQueue. | DosPeekQueue can either examine elements in the order that was specified when the queue was created (FIFO, LIFO, or priority), or it can examine the next element in the queue after a previous DosPeekQueue request has been called. By making multiple DosPeekQueue requests, the server process can search through a queue, examining each element in turn. When it locates the element it is searching for, the server process can remove the element from the queue by calling DosReadQueue. | ||
If several threads are using the same queue, the process writing to the queue can use the ulData field of the REQUESTDATA data structure to indicate that an element is directed to a particular thread. The thread can peek at the queue whenever data is available and read any elements containing the appropriate value in the ulData field | If several threads are using the same queue, the process writing to the queue can use the ulData field of the REQUESTDATA data structure to indicate that an element is directed to a particular thread. The thread can peek at the queue whenever data is available and read any elements containing the appropriate value in the ulData field. | ||
The following code fragment shows how a thread can use DosPeekQueue to examine the elements in a queue. Assume that a previous call to DosOpenQueue provided the queue handle that is contained in QueueHandle. Assume that the identifier of the process that owns the queue has been placed into OwningPID already. | |||
<pre> | <pre> | ||
#define INCL_DOSQUEUES /* Queue values */ | |||
#define INCL_DOSPROCESS /* needed for DCWW_WAIT */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HQUEUE hqQueueHandle; /* Queue handle */ | |||
REQUESTDATA rqRequest; /* Request-identification data */ | |||
ULONG ulDataLength; /* Length of examined element */ | |||
PVOID pDataAddress; /* Address of examined element */ | |||
ULONG ulElementCode; /* Indicator of examined element */ | |||
BOOL32 bNoWait; /* No wait if queue is empty */ | |||
BYTE bElemPriority; /* Priority of examined element */ | |||
HEV hevSemHandle; /* Semaphore handle */ | |||
PID pidOwningPID; /* PID of queue owner */ | |||
APIRET ulrc; /* Return code */ | |||
rqRequest.pid = pidOwningPID; /* Set request data block to indicate */ | |||
/* queue owner */ | |||
ulElementCode = 0; /* Indicate that the peek should start */ | |||
/* at the front of the queue */ | |||
bNoWait = DCWW_WAIT; /* Indicate that the peek call should */ | |||
/* wait if the queue is currently empty */ | |||
hevSemHandle = 0; /* Unused since this is a call that */ | |||
/* synchronously waits */ | |||
ulrc = DosPeekQueue(hqQueueHandle, | |||
&rqRequest, | |||
&ulDataLength, | |||
&pDataAddress, | |||
&ulElementCode, | |||
bNoWait, | |||
&bElemPriority, | |||
hevSemHandle); | |||
if (ulrc != 0) { | |||
printf("DosPeekQueue error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
On successful return, DataLength contains the size of the element on the queue that is pointed to by the pointer within DataAddress, ElementCode has been updated to indicate the next queue element, ElemPriority has been updated to contain the priority of the queue element pointed to by DataAddress, and Request.ulData contains any special data that the DosWriteQueue caller placed into the queue. | |||
If the queue is empty and NoWait is set to DCWW_WAIT (0), the calling thread waits until an element is placed in the queue. If the queue is empty and NoWait is set to DCWW_NOWAIT (1), DosPeekQueue returns immediately with ERROR_QUE_EMPTY. | |||
If NoWait is set to DCWW_NOWAIT, an event semaphore must be provided so that the calling thread can determine when an element has been placed in the queue. The semaphore is created by calling DosCreateEventSem, and its handle is supplied as a DosPeekQueue parameter. The first time an event semaphore handle is supplied in a DosPeekQueue or DosReadQueue request for which DCWW_NOWAIT has been specified for a particular queue, the handle is saved by the system. The same handle must be supplied in all subsequent DosPeekQueue and DosReadQueue requests that are called for that queue; if a different handle is supplied, ERROR_INVALID_PARAMETER is returned. | |||
When a client process adds an element to the queue, the system automatically opens the semaphore (if necessary) and posts it. The server can either call DosQueryEventSem periodically to determine whether the semaphore has been posted, or it can call DosWaitEventSem. DosWaitEventSem causes the calling thread to block until the semaphore is posted. | |||
After the event semaphore has been posted, the calling thread must call DosPeekQueue again to examine the newly added queue element. | |||
If QUE_CONVERT_ADDRESS is specified in the call to DosCreateQueue, OS/2 will automatically convert 16-bit addresses to 32-bit addresses. | |||
===Purging a Queue=== | ===Purging a Queue=== | ||
The server process or any of its threads can empty a queue of all its elements by calling DosPurgeQueue. This function is not available to client processes. | The server process or any of its threads can empty a queue of all its elements by calling DosPurgeQueue. This function is not available to client processes. | ||
;Warning: This is an unconditional purge of all elements in the queue | ;Warning: This is an unconditional purge of all elements in the queue. | ||
The following code fragment shows how the owner of a queue can empty the queue of all data elements. Assume that the owner of the queue has saved the queue's handle (obtained in a previous call to DosCreateQueue) in QueueHandle. | |||
<pre> | <pre> | ||
#define INCL_DOSQUEUES /* Queue values */ | |||
#include <os2.h> | |||
#include <stdio.h> | |||
HQUEUE hqQueueHandle; /* Queue handle */ | |||
APIRET ulrc; /* Return code */ | |||
ulrc = DosPurgeQueue(hqQueueHandle); | |||
if (ulrc != 0) { | |||
printf("DosPurgeQueue error: return code = %ld", | |||
ulrc); | |||
return; | |||
} | |||
</pre> | </pre> | ||
===Closing a Queue=== | ===Closing a Queue=== | ||
DosCloseQueue can be used both to end access to a queue and to delete a queue. The action taken as a result of a DosCloseQueue request depends on: | DosCloseQueue can be used both to end access to a queue and to delete a queue. The action taken as a result of a DosCloseQueue request depends on: | ||
*Whether the requesting process is the server process or a client process | |||
*Whether the requesting process is the server process or a client process | *The value of the access count, which is maintained for each client process by OS/2. The access count for a client process is incremented whenever DosOpenQueue is called, and decremented whenever DosCloseQueue is called. | ||
*The value of the access count, which is maintained for each client process by OS/2. The access count for a client process is incremented whenever DosOpenQueue is called, and decremented whenever DosCloseQueue is called. | If the requesting process is a client, and the access count equals 0, DosCloseQueue ends the client's access to the queue, but the queue itself is not affected. If the access count does not equal 0, the count is decremented, but the process retains access to the queue. | ||
If the requesting process is a client, and the access count equals 0, DosCloseQueue ends the client's access to the queue, but the queue itself is not affected. If the access count does not equal 0, the count is decremented, but the process retains access to the queue. | |||
If the requesting process is the server, DosCloseQueue purges any outstanding elements from the queue and deletes the queue regardless of the access count; client processes that still have the queue open receive ERROR_QUE_INVALID_HANDLE on their next request. | If the requesting process is the server, DosCloseQueue purges any outstanding elements from the queue and deletes the queue regardless of the access count; client processes that still have the queue open receive ERROR_QUE_INVALID_HANDLE on their next request. | ||
[[Category:CPGuide]] | [[Category:CPGuide]] |
Latest revision as of 18:44, 19 May 2025
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. Queues are one of three forms of interprocess communication (IPC), the other forms of IPC being semaphores and pipes.
This chapter describes how to create and use queues. Queues enable one or more processes to transfer data to a specific target process.
Note: The queues used for interprocess communication should not to be confused with the message queues used for communication between Presentation Manager (PM) and PM applications, nor with the printer queues used by the print spooler in managing print jobs.
The following topics are related to the information in this chapter:
- Memory (shared memory)
- Program execution and control
- Semaphores
- Pipes
About Queues
A queue is a named, ordered list of elements that is used to pass information between threads of the same (related) process or between different (unrelated) processes.
Processes pass information to a queue in the form of elements. An element is a 32-bit unit of information. Queue elements can be values, flags, pointers to shared memory regions, anything that can fit into 32 bits. The format of a queue element depends entirely on the process that creates the queue (the queue owner). Only the queue owner can read elements from the queue; other processes can only write to the queue. Reading an element automatically removes it from the queue.
The process that creates the queue is known as the server process of the queue. The other processes that access the queue are known as client processes.
The owner of the queue (the server process) can choose the order in which to read incoming information and can examine queue elements without removing them from the queue. Queue elements can be added and accessed in First-In-First-Out (FIFO), Last-In-First-Out (LIFO), or priority-based order.
Any process that has the name of a queue can open the queue and write to it. The processes writing elements to the queue must use the format determined by the queue owner.
Queues are very efficient. They pass only 32-bit sized elements, rather than large data structures. However, queues can be used only for one-way communication, because a client process can write to a queue but cannot read from one.
Typically, processes use queues to transfer information about the contents of a shared memory. The elements in the queue could contain the address and length of data areas in shared memory objects. The sending process allocates a shared memory object and gives access to the shared memory to the queue owner. The sending process can free the shared memory after writing the elements to the queue because the shared memory will not be deallocated until the queue owner frees it.
Any thread in the process that owns the queue can examine queue elements without removing them. This is called peeking at the queue.
OS/2 supplies the process identifier of the process that writes an element to the queue, so that a process reading from or peeking at the queue can determine the origin of the element. The process identifier is returned as part of a REQUESTDATA data structure. Threads can use the ulData field of the REQUESTDATA data structure to pass additional information about the queue element.
If the queue is empty when a process attempts to read from it, the process can either wait for an element to become available or continue executing without reading from the queue. Semaphores can be used to indicate when an element is in the queue.
Queues and Semaphores
If a process manages only one queue, it typically waits for an element to become available. However, if a process manages several queues, waiting for one queue means that other queues cannot be read. To avoid wasting time while waiting, a process can supply an event semaphore when it calls DosReadQueue or DosPeekQueue. The process can then continue to execute without actually reading an element from the queue, because DosWriteQueue will post the semaphore only when an element is ready. The semaphore remains posted until someone resets it; usually the queue owner process resets the semaphore after it reads all the available information from the queue.
If a process uses a unique semaphore for each queue, it can use DosWaitMuxWaitSem to wait for the first queue to receive an element.
Only one semaphore is permitted per queue.
Queue Servers and Clients
The server process and its threads have certain queue-managing privileges. Only the server process and its threads can:
- Examine queue elements without removing them (DosPeekQueue)
- Remove elements from the queue (DosReadQueue)
- Purge all the elements in a queue (DosPurgeQueue)
- Write to the queue without opening it first (DosWriteQueue)
- Delete the queue (DosCloseQueue).
Both server and client processes can query the number of elements in the queue using DosQueryQueue.
Client processes can query the queue (DosQueryQueue) and add elements to it (DosWriteQueue), but they must first gain access to the queue by calling DosOpenQueue. When a client process is finished with a queue, it ends its access to the queue by calling DosCloseQueue. (Note that, unlike the server process and its threads, a client process cannot use DosCloseQueue to delete a queue.)
When a queue is opened by a client process, an access count is set to 1. Each client process has its own access count. The access count is incremented whenever a thread in a process opens the queue and decremented whenever a thread in the process closes the queue. Access to the queue by the client process ends when the access count for the process reaches 0. When the server process closes the queue, the queue is terminated and removed from the system.
Queue Element Order
DosReadQueue reads either a specified element or the first element in the queue. The first element in the queue depends on the queue type, which is specified when the queue is created. A queue can have FIFO, LIFO, or priority ordering.
Priority values range from 0 (lowest priority) through 15 (highest priority). The writing process assigns a priority to a queue element when the element is written to the queue. DosReadQueue reads elements from the queue in descending order of priority, regardless of the order in which DosWriteQueue placed the elements in the queue. Elements with equal priority are read in FIFO order.
Obtaining Information about Queues and Queue Elements
Any thread in the process that owns the queue can use DosPeekQueue to examine the elements in the queue to determine which one to actually read. Each call to DosPeekQueue returns the identifier of the next element in the queue, so the function can be called repeatedly to move through the queue. The identifier of the desired element can then be supplied to DosReadQueue.
Any process that has opened a queue can use DosQueryQueue to determine the number of elements in the queue. This function also returns an error value if the queue owner has closed the queue.
Using Queues
Queues are useful for a process to manage input from other processes. The examples in the following sections show how to create and use queues.
- 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 a Queue
A thread creates a queue by using DosCreateQueue and specifying a queue name and the queue type as arguments. The queue name must be unique and have the following form:
\QUEUES\QueName
The "\QUEUES\" is required, though it need not be uppercase. It is not a subdirectory.
The QueName parameter must conform to the rules for OS/2 file names, although no actual file is created for the queue.
The process that creates the queue is known as the server process of the queue. The other processes that access the queue are known as client processes.
The following code fragment creates a FIFO queue named \queues\sample.que:
#define INCL_DOSQUEUES /* Queue values */ #include <os2.h> HQUEUE hq; DosCreateQueue(&hq, QUE_FIFO | QUE_CONVERT_ADDRESS, "\\queues\\sample.que");
When the server process creates the queue, it determines whether the ordering of queue elements is based on arrival (FIFO or LIFO) or priority. If the ordering is based on priority, then priority values must be assigned whenever data is added to the queue.
The server must also specify whether OS/2 is to convert 16-bit addresses of elements placed in the queue by 16-bit processes to 32-bit addresses.
After a process has created a queue, any thread in that process can access the queue with equal authority.
Allocating Memory for Queue Data
When queues are used only to pass the addresses to data rather than the data itself, processes must allocate shared memory objects for storing queue data. The two most common methods of storing queue data are:
- Using a named shared memory object - for related processes
- Using unnamed shared memory objects - for unrelated processes
Related processes generally use a single named shared memory object for storing queue data. The server process allocates the memory object by calling DosAllocSharedMem. Care must be taken to ensure that the memory object is large enough to meet application requirements.
The name of the shared memory object is established by agreement among the server and the client processes. For simplicity, the name can be the same as the queue name, except that the prefix \SHAREMEM\ must be used instead of \QUEUES\.
A client process accesses the named shared memory object by calling DosGetNamedSharedMem. It must then call DosOpenQueue to gain access to the queue of the server.
Before the server process ends, it releases the memory object by calling DosFreeMem.
Unrelated processes generally use unnamed shared memory objects for storing queue data. This makes it possible for a client process to store data in a shared memory object without knowing its name. To use unnamed shared memory objects for storing queue data, the server process must take the following steps whenever it is called by a client:
- Save the process identification (PID) of the client process.
- Allocate an unnamed shared memory object for the data of the client process by calling DosAllocSharedMem.
- Give the client process the capability of accessing the memory object by calling DosGiveSharedMem with the client's PID.
The client process must then call DosOpenQueue to gain access to the server's queue.
After each client completes its queue requests or ends, the server calls DosFreeMem to release the client's memory object.
Opening a Queue
Once the queue is created, the server process of the queue and the threads of the server process have immediate access to the queue and can proceed to access the queue. A client process must request access to a queue by calling DosOpenQueue. Once the queue is open, the client process can add an element to the queue with DosWriteQueue, and it can query the number of elements in the queue with DosQueryQueue.
DosOpenQueue retrieves the queue handle and the process identifier of the queue owner. The function also increments the queue's access count.
The following code fragment shows how another process would open the queue created with DosCreateQueue.
#define INCL_DOSQUEUES #include<os2.h> #define HF_STDOUT 1 /* Standard output handle */ HQUEUE hq; PID pidOwner; ULONG ulWritten; APIRET ulrc; ulrc = DosOpenQueue(&pidOwner, &hq, "\\queues\\sample.que"); if (ulrc) { DosWrite(HF_STDOUT, "\r\n Queue open failed. \r\n", 24, &ulWritten); DosExit(EXIT_PROCESS, 1); } else { DosWrite(HF_STDOUT, "\r\n Queue opened. \r\n", 19, &ulWritten); }
When it is finished with the queue, a thread in the client process ends its access by calling DosCloseQueue. DosCloseQueue decrements the access count for the process each time it is called. When the access count reaches 0, the connection between the client process and the queue is terminated.
After a process has opened a queue, any thread in that process can access the queue with equal authority.
- Note
- If a queue was created by a call to the 16-bit DosCreateQueue, then it is not accessible to 32-bit DosOpenQueue requests, and ERROR_QUE_PROC_NO_ACCESS will be returned.
Writing to a Queue
The server process and any of its threads can add an element to a queue simply by calling DosWriteQueue. A client process, however, must first request access to the queue by calling DosOpenQueue.
Processes that communicate by passing the addresses of shared memory objects through the queue must have a shared memory object that they each have access to. Once a process opens the queue, it can allocate shared memory by using DosAllocMem with the OBJ_GIVEABLE attribute and then give the shared memory to the queue owner with DosGiveSharedMem.
A process that has opened a queue can write to the queue by using DosWriteQueue. The writing process must create elements in a form that the queue owner can read.
The following code fragment adds an element to a queue. Assume that the caller has placed the handle of the queue into QueueHandle already. Assume also that DataBuffer has been set to point to a data element in shared memory, and that DataLength has been set to contain the length of the data element in shared memory.
#define INCL_DOSQUEUES /* Queue values */ #include <os2.h> #include <stdio.h> HQUEUE hqQueueHandle; /* Queue handle */ ULONG ulRequest; /* Request-identification data */ ULONG ulDataLength; /* Length of element being added */ PVOID pDataBuffer; /* Element being added */ ULONG ulElemPriority; /* Priority of element being added */ APIRET ulrc; /* Return code */ ulRequest = 0; /* Assume that no special data is being */ /* sent along with this write request */ ulElemPriority = 0; /* For priority-based queues: add the */ /* new queue element at the logical end */ /* of the queue */ ulrc = DosWriteQueue(hqQueueHandle, ulRequest, ulDataLength, pDataBuffer, ulElemPriority); if (ulrc != 0) { printf("DosWriteQueue error: return code = %ld", ulrc); return; }
Once the process has written to the queue, it frees the shared memory. However, the memory will not be freed until the queue owner also frees it.
If the queue was created as a priority-based queue (as specified in the QueueFlags parameter of DosCreateQueue), then the priority of the element that is being added must be specified.
If the server process has ended, or if it has closed the queue before DosWriteQueue is called, then ERROR_QUE_INVALID_HANDLE is returned.
Reading from a Queue
The queue owner (server process) and its threads can read an element from the queue by using DosReadQueue. The owner can read the first element in the queue by specifying 0 as the element number. Alternatively, the owner can read a particular element in the queue by specifying an element code returned from DosPeekQueue. This function is not available to client processes.
DosReadQueue can either remove queue elements in the order that was specified when the queue was created (FIFO, LIFO, or priority), or it can use an element identifier from DosPeekQueue as input to remove a previously examined element.
The following code fragment reads an element from the queue. Assume that the caller has placed the handle of the queue into QueueHandle already and that the identifier of the process that owns the queue has been placed into OwningPID already.
#define INCL_DOSQUEUES /* Queue values */ #include <os2.h> #include <stdio.h> HQUEUE hqQueueHandle; /* Queue handle */ REQUESTDATA rqRequest; /* Request-identification data */ ULONG ulDataLength; /* Length of element received */ PULONG pulDataAddress; /* Address of element received */ ULONG ulElementCode; /* Request a particular element */ BOOL32 bNoWait; /* No wait if queue is empty */ BYTE bElemPriority; /* Priority of element received */ HEV hevSemHandle; /* Semaphore handle */ PID pidOwningPID; /* PID of queue owner */ APIRET ulrc; /* Return code */ rqRequest.pid = pidOwningPID; /* Set request data block to indicate */ /* queue owner */ ulElementCode = 0; /* Indicate that the read should start */ /* at the front of the queue */ bNoWait = 0; /* Indicate that the read should wait */ /* if the queue is currently empty */ hevSemHandle = 0; /* Unused since this is a call that */ /* waits synchronously */ ulrc = DosReadQueue(hqQueueHandle, &rqRequest, &ulDataLength, (PVOID *) &pulDataAddress, ulElementCode, bNoWait, &bElemPriority, hevSemHandle); if (ulrc != 0) { printf("DosReadQueue error: return code = %ld", ulrc); return; }
On successful return, DataLength contains the size of the element on the queue that is pointed to by the pointer within DataAddress, ElemPriority has been updated to contain the priority of the queue element pointed to by DataAddress, and Request.ulData contains any special data that the DosWriteQueue caller placed into the queue.
If the queue is empty and NoWait is set to DCWW_WAIT (0), the calling thread waits until an element is placed in the queue. If the queue is empty and NoWait is set to DCWW_NOWAIT (1), DosReadQueue returns immediately with ERROR_QUE_EMPTY.
If NoWait is set to DCWW_NOWAIT, an event semaphore must be provided so that the calling thread can determine when an element has been placed in the queue. The semaphore is created by calling DosCreateEventSem, and its handle is supplied as a DosReadQueue parameter. The first time an event semaphore handle is supplied in a DosReadQueue or DosPeekQueue request for which DCWW_NOWAIT has been specified for a particular queue, the handle is saved by the system. The same handle must be supplied in all subsequent DosReadQueue and DosPeekQueue requests that are called for that queue; if a different handle is supplied, ERROR_INVALID_PARAMETER is returned.
When a client process adds an element to the queue, the system automatically opens the semaphore (if necessary) and posts it. The server can either call DosQueryEventSem periodically to determine whether the semaphore has been posted, or it can call DosWaitEventSem. DosWaitEventSem causes the calling thread to block until the semaphore is posted.
After the event semaphore has been posted, the calling thread must call DosReadQueue again to remove the newly added queue element.
If QUE_CONVERT_ADDRESS is specified in the call to DosCreateQueue, OS/2 will automatically convert 16-bit addresses to 32-bit addresses.
Peeking at a Queue
The server process and its threads can examine a queue element by calling DosPeekQueue. This function is not available to client processes.
Unlike DosReadQueue, DosPeekQueue does not remove the element from the queue.
DosPeekQueue can either examine elements in the order that was specified when the queue was created (FIFO, LIFO, or priority), or it can examine the next element in the queue after a previous DosPeekQueue request has been called. By making multiple DosPeekQueue requests, the server process can search through a queue, examining each element in turn. When it locates the element it is searching for, the server process can remove the element from the queue by calling DosReadQueue.
If several threads are using the same queue, the process writing to the queue can use the ulData field of the REQUESTDATA data structure to indicate that an element is directed to a particular thread. The thread can peek at the queue whenever data is available and read any elements containing the appropriate value in the ulData field.
The following code fragment shows how a thread can use DosPeekQueue to examine the elements in a queue. Assume that a previous call to DosOpenQueue provided the queue handle that is contained in QueueHandle. Assume that the identifier of the process that owns the queue has been placed into OwningPID already.
#define INCL_DOSQUEUES /* Queue values */ #define INCL_DOSPROCESS /* needed for DCWW_WAIT */ #include <os2.h> #include <stdio.h> HQUEUE hqQueueHandle; /* Queue handle */ REQUESTDATA rqRequest; /* Request-identification data */ ULONG ulDataLength; /* Length of examined element */ PVOID pDataAddress; /* Address of examined element */ ULONG ulElementCode; /* Indicator of examined element */ BOOL32 bNoWait; /* No wait if queue is empty */ BYTE bElemPriority; /* Priority of examined element */ HEV hevSemHandle; /* Semaphore handle */ PID pidOwningPID; /* PID of queue owner */ APIRET ulrc; /* Return code */ rqRequest.pid = pidOwningPID; /* Set request data block to indicate */ /* queue owner */ ulElementCode = 0; /* Indicate that the peek should start */ /* at the front of the queue */ bNoWait = DCWW_WAIT; /* Indicate that the peek call should */ /* wait if the queue is currently empty */ hevSemHandle = 0; /* Unused since this is a call that */ /* synchronously waits */ ulrc = DosPeekQueue(hqQueueHandle, &rqRequest, &ulDataLength, &pDataAddress, &ulElementCode, bNoWait, &bElemPriority, hevSemHandle); if (ulrc != 0) { printf("DosPeekQueue error: return code = %ld", ulrc); return; }
On successful return, DataLength contains the size of the element on the queue that is pointed to by the pointer within DataAddress, ElementCode has been updated to indicate the next queue element, ElemPriority has been updated to contain the priority of the queue element pointed to by DataAddress, and Request.ulData contains any special data that the DosWriteQueue caller placed into the queue.
If the queue is empty and NoWait is set to DCWW_WAIT (0), the calling thread waits until an element is placed in the queue. If the queue is empty and NoWait is set to DCWW_NOWAIT (1), DosPeekQueue returns immediately with ERROR_QUE_EMPTY.
If NoWait is set to DCWW_NOWAIT, an event semaphore must be provided so that the calling thread can determine when an element has been placed in the queue. The semaphore is created by calling DosCreateEventSem, and its handle is supplied as a DosPeekQueue parameter. The first time an event semaphore handle is supplied in a DosPeekQueue or DosReadQueue request for which DCWW_NOWAIT has been specified for a particular queue, the handle is saved by the system. The same handle must be supplied in all subsequent DosPeekQueue and DosReadQueue requests that are called for that queue; if a different handle is supplied, ERROR_INVALID_PARAMETER is returned.
When a client process adds an element to the queue, the system automatically opens the semaphore (if necessary) and posts it. The server can either call DosQueryEventSem periodically to determine whether the semaphore has been posted, or it can call DosWaitEventSem. DosWaitEventSem causes the calling thread to block until the semaphore is posted.
After the event semaphore has been posted, the calling thread must call DosPeekQueue again to examine the newly added queue element.
If QUE_CONVERT_ADDRESS is specified in the call to DosCreateQueue, OS/2 will automatically convert 16-bit addresses to 32-bit addresses.
Purging a Queue
The server process or any of its threads can empty a queue of all its elements by calling DosPurgeQueue. This function is not available to client processes.
- Warning
- This is an unconditional purge of all elements in the queue.
The following code fragment shows how the owner of a queue can empty the queue of all data elements. Assume that the owner of the queue has saved the queue's handle (obtained in a previous call to DosCreateQueue) in QueueHandle.
#define INCL_DOSQUEUES /* Queue values */ #include <os2.h> #include <stdio.h> HQUEUE hqQueueHandle; /* Queue handle */ APIRET ulrc; /* Return code */ ulrc = DosPurgeQueue(hqQueueHandle); if (ulrc != 0) { printf("DosPurgeQueue error: return code = %ld", ulrc); return; }
Closing a Queue
DosCloseQueue can be used both to end access to a queue and to delete a queue. The action taken as a result of a DosCloseQueue request depends on:
- Whether the requesting process is the server process or a client process
- The value of the access count, which is maintained for each client process by OS/2. The access count for a client process is incremented whenever DosOpenQueue is called, and decremented whenever DosCloseQueue is called.
If the requesting process is a client, and the access count equals 0, DosCloseQueue ends the client's access to the queue, but the queue itself is not affected. If the access count does not equal 0, the count is decremented, but the process retains access to the queue.
If the requesting process is the server, DosCloseQueue purges any outstanding elements from the queue and deletes the queue regardless of the access count; client processes that still have the queue open receive ERROR_QUE_INVALID_HANDLE on their next request.