mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1256 lines
32 KiB
1256 lines
32 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
srvque.c
|
|
|
|
Abstract:
|
|
|
|
Queues API
|
|
|
|
Author:
|
|
|
|
Mark Lucovsky (markl) 10-Jul-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define INCL_OS2V20_QUEUES
|
|
#define INCL_OS2V20_ERRORS
|
|
#define INCL_OS2V20_TASKING
|
|
#include "os2srv.h"
|
|
|
|
NTSTATUS
|
|
Os2InitializeQueues( VOID )
|
|
{
|
|
// Os2Debug |= OS2_DEBUG_QUEUES;
|
|
Os2QueueTable =
|
|
Or2CreateQHandleTable( Os2Heap, sizeof( OS2_QUEUE ), 8 );
|
|
|
|
if (Os2QueueTable == NULL) {
|
|
return( STATUS_NO_MEMORY );
|
|
} else {
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosCreateQueue( IN POS2_THREAD t, IN POS2_API_MSG m )
|
|
{
|
|
POS2_DOSCREATEQUEUE_MSG a = &m->u.DosCreateQueue;
|
|
POS2_LOCAL_OBJECT_DIRENT Dirent;
|
|
STRING QueueName;
|
|
OS2_QUEUE LQueue;
|
|
POS2_QUEUE Queue;
|
|
|
|
AcquireLocalObjectLock(Os2QueueTable);
|
|
|
|
Dirent = Os2LookupLocalObjectByName(
|
|
&a->QueueName,
|
|
LocalObjectQueue
|
|
);
|
|
|
|
if (Dirent) {
|
|
m->ReturnedErrorValue = ERROR_QUE_DUPLICATE;
|
|
} else {
|
|
|
|
m->ReturnedErrorValue = ERROR_QUE_NO_MEMORY;
|
|
|
|
//
|
|
// allocate and create the queue
|
|
//
|
|
|
|
a->QueueHandle = (HQUEUE) -1;
|
|
|
|
if ( Or2CreateQHandle(
|
|
Os2QueueTable,
|
|
(PULONG)&a->QueueHandle,
|
|
(PVOID)&LQueue
|
|
) ) {
|
|
|
|
Queue = Or2MapQHandle(
|
|
Os2QueueTable,
|
|
(ULONG)a->QueueHandle,
|
|
TRUE
|
|
);
|
|
|
|
if (Queue != NULL) {
|
|
|
|
Queue->QueueType = a->QueueType;
|
|
Queue->OpenCount = 1;
|
|
Queue->EntryIdCounter = 1;
|
|
Queue->CreatorPid = t->Process->ProcessId;
|
|
QueueName = a->QueueName;
|
|
QueueName.Buffer = RtlAllocateHeap( Os2Heap, 0, QueueName.Length);
|
|
InitializeListHead(&Queue->Entries);
|
|
InitializeListHead(&Queue->Waiters);
|
|
InitializeListHead(&Queue->SemBlocks);
|
|
|
|
if ( QueueName.Buffer ) {
|
|
|
|
RtlMoveMemory(QueueName.Buffer,a->QueueName.Buffer,QueueName.Length);
|
|
|
|
Dirent = Os2InsertLocalObjectName(
|
|
&QueueName,
|
|
LocalObjectQueue,
|
|
(ULONG)a->QueueHandle
|
|
);
|
|
if ( !Dirent ) {
|
|
|
|
RtlFreeHeap( Os2Heap, 0, QueueName.Buffer);
|
|
|
|
//
|
|
// Destroy will unlock the queue table
|
|
//
|
|
|
|
Or2DestroyQHandle(Os2QueueTable,(ULONG)a->QueueHandle);
|
|
return (TRUE);
|
|
} else {
|
|
|
|
Queue->Dirent = Dirent;
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
}
|
|
}
|
|
#if DBG
|
|
else {
|
|
KdPrint(( "Os2Srv: no memory for QueueName.Buffer\n" ));
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
#if DBG
|
|
else {
|
|
KdPrint(( "Os2Srv: Or2MapQHandle returned NULL\n" ));
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
ReleaseLocalObjectLock(Os2QueueTable);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
POS2_QUEUE
|
|
Os2OpenQueueByHandle(
|
|
IN HQUEUE QueueHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to open a queue once the queues index is
|
|
known.
|
|
|
|
Arguments:
|
|
|
|
QueueHandle - Supplies the handle to the queue being opened.
|
|
|
|
Return Value:
|
|
|
|
NON-NULL - Returns the address of the opened queue.
|
|
NULL - The queue could not be opened.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
POS2_QUEUE Queue;
|
|
|
|
Queue = Or2MapQHandle(
|
|
Os2QueueTable,
|
|
(ULONG)QueueHandle,
|
|
TRUE
|
|
);
|
|
|
|
ASSERT(Queue);
|
|
|
|
Queue->OpenCount++;
|
|
|
|
return Queue;
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2DosOpenQueue( IN POS2_THREAD t, IN POS2_API_MSG m )
|
|
{
|
|
POS2_DOSOPENQUEUE_MSG a = &m->u.DosOpenQueue;
|
|
POS2_LOCAL_OBJECT_DIRENT Dirent;
|
|
POS2_QUEUE Queue;
|
|
|
|
UNREFERENCED_PARAMETER(t);
|
|
AcquireLocalObjectLock(Os2QueueTable);
|
|
|
|
Dirent = Os2LookupLocalObjectByName(
|
|
&a->QueueName,
|
|
LocalObjectQueue
|
|
);
|
|
|
|
if (!Dirent) {
|
|
m->ReturnedErrorValue = ERROR_QUE_NAME_NOT_EXIST;
|
|
} else {
|
|
|
|
Queue = Os2OpenQueueByHandle((HQUEUE)Dirent->ObjectHandle);
|
|
ASSERT(Queue);
|
|
a->QueueHandle = (HQUEUE) Dirent->ObjectHandle;
|
|
a->OwnerProcessId = Queue->CreatorPid;
|
|
}
|
|
|
|
ReleaseLocalObjectLock(Os2QueueTable);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
APIRET
|
|
Os2CloseQueueByHandle(
|
|
IN HQUEUE QueueHandle,
|
|
IN ULONG CloseCount,
|
|
IN PID OwnerPid,
|
|
IN POS2_PROCESS Process
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to close a queue. It is used by both
|
|
DosCloseQueue and session rundown.
|
|
|
|
Arguments:
|
|
|
|
QueueHandle - Supplies the handle to the queue being closed.
|
|
|
|
CloseCount - Supplies the close count for the queue.
|
|
|
|
Process - Supplies the address of the process doing the close
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
|
|
APIRET rc;
|
|
POS2_LOCAL_OBJECT_DIRENT Dirent;
|
|
POS2_QUEUE Queue;
|
|
|
|
Queue = (POS2_QUEUE)Or2MapQHandle( Os2QueueTable,
|
|
(ULONG)QueueHandle,
|
|
FALSE
|
|
);
|
|
if (!Queue) {
|
|
rc = ERROR_QUE_INVALID_HANDLE;
|
|
}
|
|
else if ((OwnerPid != (PID)(-1)) && (OwnerPid != Queue->CreatorPid)) {
|
|
rc = ERROR_QUE_INVALID_HANDLE;
|
|
}
|
|
else {
|
|
rc = NO_ERROR;
|
|
|
|
Queue->OpenCount -= CloseCount;
|
|
if (Queue->OpenCount < 0) {
|
|
ASSERT(Queue->OpenCount >= 0);
|
|
Queue->OpenCount = 0;
|
|
}
|
|
|
|
//
|
|
// Check to see if close is from owner of the queue.
|
|
// If so, purge the queue
|
|
//
|
|
|
|
if ( Queue->CreatorPid == Process->ProcessId ) {
|
|
|
|
//
|
|
// Destroy the queue and then free remove the dirent from the
|
|
// name table
|
|
// When destroying the queue, you must hold the lock and then
|
|
// nuke the dirent.
|
|
//
|
|
|
|
Dirent = Queue->Dirent;
|
|
Os2DeleteLocalObject(Dirent);
|
|
|
|
//
|
|
// Now no-one can reference the queue since it's dirent is gone
|
|
// and no-operations are going on on the queue since we hold the
|
|
// lock. Release the queue lock and free the queue.
|
|
//
|
|
|
|
//
|
|
// purge queue. free all asynch reads,
|
|
//
|
|
|
|
Os2PurgeQueueEntries(Queue);
|
|
|
|
Os2NotifyWait(WaitQueue,(PVOID)Queue,(PVOID)ERROR_SYS_INTERNAL);
|
|
Os2ProcessSemBlocks(Queue);
|
|
|
|
Or2DestroyQHandle(Os2QueueTable, (ULONG)QueueHandle);
|
|
}
|
|
else {
|
|
ReleaseHandleTableLock( Os2QueueTable );
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosCloseQueue( IN POS2_THREAD t, IN POS2_API_MSG m )
|
|
{
|
|
|
|
POS2_DOSCLOSEQUEUE_MSG a = &m->u.DosCloseQueue;
|
|
APIRET rc;
|
|
|
|
rc = Os2CloseQueueByHandle( a->QueueHandle,
|
|
a->CloseCount,
|
|
a->OwnerProcessId,
|
|
t->Process
|
|
);
|
|
|
|
m->ReturnedErrorValue = rc;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosPurgeQueue( IN POS2_THREAD t, IN POS2_API_MSG m )
|
|
{
|
|
POS2_DOSPURGEQUEUE_MSG a = &m->u.DosPurgeQueue;
|
|
POS2_QUEUE Queue;
|
|
APIRET rc;
|
|
|
|
Queue = (POS2_QUEUE)Or2MapQHandle( Os2QueueTable,
|
|
(ULONG)a->QueueHandle,
|
|
FALSE
|
|
);
|
|
if (!Queue) {
|
|
rc = ERROR_QUE_INVALID_HANDLE;
|
|
}
|
|
else {
|
|
|
|
if ( Queue->CreatorPid != t->Process->ProcessId ) {
|
|
rc = ERROR_QUE_PROC_NOT_OWNED;
|
|
}
|
|
else {
|
|
rc = NO_ERROR;
|
|
ReleaseHandleTableLock( Os2QueueTable );
|
|
Os2PurgeQueueEntries(Queue);
|
|
}
|
|
}
|
|
|
|
if ( rc != NO_ERROR ) {
|
|
ReleaseHandleTableLock( Os2QueueTable );
|
|
}
|
|
|
|
m->ReturnedErrorValue = rc;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2DosQueryQueue( IN POS2_THREAD t, IN POS2_API_MSG m )
|
|
{
|
|
|
|
POS2_DOSQUERYQUEUE_MSG a = &m->u.DosQueryQueue;
|
|
POS2_QUEUE Queue;
|
|
APIRET rc;
|
|
PLIST_ENTRY NextEntry;
|
|
ULONG ElementCount;
|
|
|
|
UNREFERENCED_PARAMETER(t);
|
|
Queue = (POS2_QUEUE)Or2MapQHandle( Os2QueueTable,
|
|
(ULONG)a->QueueHandle,
|
|
FALSE
|
|
);
|
|
if (!Queue) {
|
|
rc = ERROR_QUE_INVALID_HANDLE;
|
|
}
|
|
else if (Queue->CreatorPid != a->OwnerProcessId) {
|
|
rc = ERROR_QUE_INVALID_HANDLE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Lock the queue and release the queue table.
|
|
// Then simply walk the queue elements and count
|
|
// them.
|
|
//
|
|
|
|
ReleaseHandleTableLock( Os2QueueTable );
|
|
|
|
rc = NO_ERROR;
|
|
ElementCount = 0;
|
|
|
|
NextEntry = Queue->Entries.Flink;
|
|
while ( NextEntry != &Queue->Entries ) {
|
|
IF_OS2_DEBUG( QUEUES ) {
|
|
POS2_QUEUE_ENTRY QueueEntry;
|
|
QueueEntry = CONTAINING_RECORD(NextEntry,OS2_QUEUE_ENTRY,Links);
|
|
DumpQueueEntry("Query",QueueEntry);
|
|
}
|
|
ElementCount++;
|
|
NextEntry = NextEntry->Flink;
|
|
}
|
|
a->CountQueueElements = ElementCount;
|
|
}
|
|
|
|
m->ReturnedErrorValue = rc;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2DosPeekQueue( IN POS2_THREAD t, IN POS2_API_MSG m )
|
|
{
|
|
POS2_DOSPEEKQUEUE_MSG a = &m->u.DosPeekQueue;
|
|
POS2_QUEUE Queue;
|
|
APIRET rc;
|
|
POS2_QUEUE_ENTRY QueueEntry, QueueEntry2;
|
|
BOOL32 WaitFlag;
|
|
BOOLEAN ret;
|
|
ULONG ReadPosition;
|
|
|
|
//
|
|
// Check NoWait flag and map the queue handle
|
|
//
|
|
|
|
switch ((WaitFlag = a->NoWait)) {
|
|
case DCWW_WAIT :
|
|
case DCWW_NOWAIT :
|
|
rc = ERROR_QUE_INVALID_HANDLE;
|
|
Queue = (POS2_QUEUE)Or2MapQHandle( Os2QueueTable,
|
|
(ULONG)a->QueueHandle,
|
|
FALSE
|
|
);
|
|
break;
|
|
default:
|
|
Queue = NULL;
|
|
rc = ERROR_QUE_INVALID_WAIT;
|
|
}
|
|
|
|
if (Queue) {
|
|
|
|
//
|
|
// Lock the queue agains modifications/deletions by
|
|
// locking the queue and then dropping the queuetable/namespace
|
|
// lock.
|
|
//
|
|
|
|
ReleaseHandleTableLock( Os2QueueTable );
|
|
rc = NO_ERROR;
|
|
|
|
//
|
|
// If the queue is empty, then wait/register request
|
|
// based on wait flag or event handle
|
|
//
|
|
|
|
if ( IsListEmpty(&Queue->Entries) ) {
|
|
|
|
//
|
|
// If the queue is empty and DCWW_WAIT was specified, then
|
|
// setup a wait block and cause thread to wait. Otherwise,
|
|
// return ERROR_QUE_EMPTY and possibly register an event to
|
|
// set.
|
|
//
|
|
|
|
if (WaitFlag == DCWW_WAIT) {
|
|
ret = Os2CreateWait( WaitQueue,
|
|
Os2WaitQueueEntries,
|
|
t,
|
|
m,
|
|
Queue,
|
|
&Queue->Waiters
|
|
);
|
|
if ( ret ) {
|
|
|
|
//
|
|
// The wait is completely registered.
|
|
// Return false to signal that reply will be
|
|
// generated later.
|
|
//
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// If we fail to create a wait block and register the
|
|
// wait, then translate into no memory.
|
|
//
|
|
|
|
rc = ERROR_QUE_NO_MEMORY;
|
|
}
|
|
}
|
|
else {
|
|
|
|
if ( a->SemIndex ) {
|
|
|
|
//
|
|
// If the queue is empty during a read/peek, and the
|
|
// caller is not waiting and has specified an event
|
|
// semaphore, then locate the semaphore and create a
|
|
// sem block. Otherwise, just return ERROR_QUE_EMPTY
|
|
//
|
|
|
|
POS2_SEMAPHORE Semaphore;
|
|
POS2_QUEUE_SEM_BLOCK SemBlock;
|
|
|
|
Semaphore = (POS2_SEMAPHORE)Or2MapHandle( Os2SharedSemaphoreTable,
|
|
a->SemIndex & 0x7fffffff,
|
|
FALSE
|
|
);
|
|
if (Semaphore == NULL) {
|
|
rc = ERROR_QUE_INVALID_HANDLE;
|
|
}
|
|
else {
|
|
SemBlock = RtlAllocateHeap( Os2Heap, 0, sizeof(*SemBlock) );
|
|
if ( !SemBlock ) {
|
|
rc = ERROR_QUE_NO_MEMORY;
|
|
}
|
|
else {
|
|
InsertTailList(&Queue->SemBlocks,&SemBlock->Links);
|
|
SemBlock->NtEvent = Semaphore->u.EventHandle;
|
|
rc = ERROR_QUE_EMPTY;
|
|
}
|
|
ReleaseHandleTableLock( Os2SharedSemaphoreTable );
|
|
}
|
|
}
|
|
else {
|
|
rc = ERROR_QUE_EMPTY;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// If the queue has some entries, then either look for the
|
|
// next entry, or use list head. Remember, if the entry positon
|
|
// is specified we peek the NEXT item. Not the specified item as
|
|
// in the case of a call to DosReadQueue.
|
|
//
|
|
|
|
//
|
|
// Compute the next entry read position. If we are peeking
|
|
// the last entry, then read position is 0. Otherwise it is
|
|
// the entry id of the next entry.
|
|
//
|
|
|
|
QueueEntry = Os2LocateQueueEntry(Queue, a->ReadPosition);
|
|
|
|
if (a->ReadPosition != 0)
|
|
{
|
|
if ( QueueEntry->Links.Flink == &Queue->Entries )
|
|
{
|
|
ReadPosition = 0;
|
|
}
|
|
else {
|
|
QueueEntry2 = CONTAINING_RECORD(QueueEntry->Links.Flink,OS2_QUEUE_ENTRY,Links);
|
|
ReadPosition = QueueEntry2->EntryId;
|
|
}
|
|
|
|
QueueEntry = Os2LocateQueueEntry(Queue, ReadPosition);
|
|
}
|
|
|
|
if ( !QueueEntry ) {
|
|
rc = ERROR_QUE_ELEMENT_NOT_EXIST;
|
|
}
|
|
else {
|
|
Os2PeekQueueEntry(Queue,QueueEntry,a);
|
|
}
|
|
}
|
|
}
|
|
m->ReturnedErrorValue = rc;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
Os2DosReadQueue( IN POS2_THREAD t, IN POS2_API_MSG m )
|
|
{
|
|
POS2_DOSREADQUEUE_MSG a = &m->u.DosReadQueue;
|
|
POS2_QUEUE Queue;
|
|
APIRET rc;
|
|
POS2_QUEUE_ENTRY QueueEntry;
|
|
BOOL32 WaitFlag;
|
|
BOOLEAN ret;
|
|
|
|
//
|
|
// Check NoWait flag and map the queue handle
|
|
//
|
|
|
|
switch ((WaitFlag = a->NoWait)) {
|
|
case DCWW_WAIT :
|
|
case DCWW_NOWAIT :
|
|
rc = ERROR_QUE_INVALID_HANDLE;
|
|
Queue = (POS2_QUEUE)Or2MapQHandle( Os2QueueTable,
|
|
(ULONG)a->QueueHandle,
|
|
FALSE
|
|
);
|
|
break;
|
|
default:
|
|
Queue = NULL;
|
|
rc = ERROR_QUE_INVALID_WAIT;
|
|
}
|
|
|
|
if (Queue) {
|
|
|
|
//
|
|
// Lock the queue agains modifications/deletions by
|
|
// locking the queue and then dropping the queuetable/namespace
|
|
// lock.
|
|
//
|
|
|
|
ReleaseHandleTableLock( Os2QueueTable );
|
|
rc = NO_ERROR;
|
|
|
|
//
|
|
// If the queue is empty, then wait/register request
|
|
// based on wait flag or event handle
|
|
//
|
|
|
|
if ( IsListEmpty(&Queue->Entries) ) {
|
|
|
|
//
|
|
// If the queue is empty and DCWW_WAIT was specified, then
|
|
// setup a wait block and cause thread to wait. Otherwise,
|
|
// return ERROR_QUE_EMPTY and possibly register an event to
|
|
// set.
|
|
//
|
|
|
|
if (WaitFlag == DCWW_WAIT) {
|
|
ret = Os2CreateWait( WaitQueue,
|
|
Os2WaitQueueEntries,
|
|
t,
|
|
m,
|
|
Queue,
|
|
&Queue->Waiters
|
|
);
|
|
if ( ret ) {
|
|
|
|
//
|
|
// The wait is completely registered.
|
|
// Return false to signal that reply will be
|
|
// generated later.
|
|
//
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// If we fail to create a wait block and register the
|
|
// wait, then translate into no memory.
|
|
//
|
|
|
|
rc = ERROR_QUE_NO_MEMORY;
|
|
}
|
|
}
|
|
else {
|
|
|
|
if ( a->SemIndex ) {
|
|
//
|
|
// If the queue is empty during a read/peek, and the
|
|
// caller is not waiting and has specified an event
|
|
// semaphore, then locate the semaphore and create a
|
|
// sem block. Otherwise, just return ERROR_QUE_EMPTY
|
|
//
|
|
|
|
POS2_SEMAPHORE Semaphore;
|
|
POS2_QUEUE_SEM_BLOCK SemBlock;
|
|
|
|
Semaphore = (POS2_SEMAPHORE)Or2MapHandle( Os2SharedSemaphoreTable,
|
|
a->SemIndex & 0x7fffffff,
|
|
FALSE
|
|
);
|
|
if (Semaphore == NULL) {
|
|
rc = ERROR_QUE_INVALID_HANDLE;
|
|
}
|
|
else {
|
|
SemBlock = RtlAllocateHeap( Os2Heap, 0, sizeof(*SemBlock) );
|
|
if ( !SemBlock ) {
|
|
rc = ERROR_QUE_NO_MEMORY;
|
|
}
|
|
else {
|
|
InsertTailList(&Queue->SemBlocks,&SemBlock->Links);
|
|
SemBlock->NtEvent = Semaphore->u.EventHandle;
|
|
rc = ERROR_QUE_EMPTY;
|
|
}
|
|
ReleaseHandleTableLock( Os2SharedSemaphoreTable );
|
|
}
|
|
}
|
|
else {
|
|
rc = ERROR_QUE_EMPTY;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// If the queue has some entries, then either look for the
|
|
// specified entry, or use list head.
|
|
//
|
|
|
|
QueueEntry = Os2LocateQueueEntry(Queue, a->ReadPosition);
|
|
|
|
if ( !QueueEntry ) {
|
|
rc = ERROR_QUE_ELEMENT_NOT_EXIST;
|
|
}
|
|
else {
|
|
Os2ReadQueueEntry(QueueEntry,a);
|
|
}
|
|
}
|
|
}
|
|
m->ReturnedErrorValue = rc;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
APIRET
|
|
Os2WriteQueueByHandle(
|
|
POS2_DOSWRITEQUEUE_MSG a,
|
|
PID ProcessId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function writes the specified message to the
|
|
specified queue. It is normally called by DosWriteQueue, but
|
|
is also used to send a session termination message.
|
|
|
|
Arguments:
|
|
|
|
a - Supplies a formatted write queue message
|
|
|
|
ProcessId - Supplies the process id of the writer.
|
|
|
|
Return Value:
|
|
|
|
Returns the OS/2 error value associated with the write.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
POS2_QUEUE Queue;
|
|
APIRET rc;
|
|
POS2_QUEUE_ENTRY QueueEntry;
|
|
POS2_QUEUE_ENTRY NextQueueEntry;
|
|
PLIST_ENTRY Pred;
|
|
PLIST_ENTRY NextEntry;
|
|
|
|
Queue = (POS2_QUEUE)Or2MapQHandle( Os2QueueTable,
|
|
(ULONG)a->QueueHandle,
|
|
FALSE
|
|
);
|
|
if (!Queue) {
|
|
rc = ERROR_QUE_INVALID_HANDLE;
|
|
}
|
|
//
|
|
// If not called by the server (ProcessId == 0), check against
|
|
// the creator of the queue
|
|
//
|
|
else if (ProcessId != 0 && Queue->CreatorPid != a->OwnerProcessId) {
|
|
ReleaseHandleTableLock( Os2QueueTable );
|
|
rc = ERROR_QUE_INVALID_HANDLE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Now allocate space for a queue entry. Then lock the
|
|
// queue and release queue table lock. Then insert entry
|
|
// in queue and release queue lock.
|
|
//
|
|
|
|
QueueEntry = RtlAllocateHeap( Os2Heap, 0, sizeof(*QueueEntry) );
|
|
|
|
if ( !QueueEntry ) {
|
|
ReleaseHandleTableLock( Os2QueueTable );
|
|
rc = ERROR_QUE_NO_MEMORY;
|
|
}
|
|
else {
|
|
|
|
ReleaseHandleTableLock( Os2QueueTable );
|
|
rc = NO_ERROR;
|
|
|
|
QueueEntry->RequestData.SenderProcessId = ProcessId;
|
|
QueueEntry->RequestData.SenderData = a->SenderData;
|
|
QueueEntry->ElementAddress = (PVOID) a->Data;
|
|
QueueEntry->ElementLength = a->DataLength;
|
|
|
|
if (a->ElementPriority>15) a->ElementPriority = 15;
|
|
|
|
QueueEntry->Priority = (Queue->QueueType == QUE_PRIORITY ? a->ElementPriority : 0);
|
|
QueueEntry->EntryId = Queue->EntryIdCounter++;
|
|
|
|
if ( Queue->EntryIdCounter == 0 ) {
|
|
Queue->EntryIdCounter++;
|
|
}
|
|
|
|
//
|
|
// Insert Entry in queue
|
|
//
|
|
|
|
switch ( Queue->QueueType ) {
|
|
case QUE_FIFO :
|
|
Pred = Queue->Entries.Blink;
|
|
break;
|
|
|
|
case QUE_LIFO :
|
|
Pred = &Queue->Entries;
|
|
break;
|
|
|
|
case QUE_PRIORITY :
|
|
Pred = &Queue->Entries;
|
|
while (Pred->Flink != &Queue->Entries) {
|
|
NextEntry = Pred->Flink;
|
|
NextQueueEntry = CONTAINING_RECORD(NextEntry, OS2_QUEUE_ENTRY, Links);
|
|
if (QueueEntry->Priority > NextQueueEntry->Priority) {
|
|
break;
|
|
}
|
|
Pred = NextEntry;
|
|
}
|
|
break;
|
|
}
|
|
|
|
IF_OS2_DEBUG( QUEUES ) DumpQueueEntry("Write",QueueEntry);
|
|
|
|
InsertHeadList(Pred,&QueueEntry->Links);
|
|
Os2ProcessSemBlocks(Queue);
|
|
Os2QueueWaitCheck(Queue);
|
|
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2DosWriteQueue( IN POS2_THREAD t, IN POS2_API_MSG m )
|
|
{
|
|
|
|
POS2_DOSWRITEQUEUE_MSG a = &m->u.DosWriteQueue;
|
|
|
|
m->ReturnedErrorValue = Os2WriteQueueByHandle(a,t->Process->ProcessId);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
Os2PurgeQueueEntries(
|
|
IN POS2_QUEUE Queue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called during DosCloseQueue and DosPurgeQueue
|
|
to empty the queue.
|
|
|
|
This function must be called with the queue locked !
|
|
|
|
Arguments:
|
|
|
|
Queue - Supplies the address of the queue to purge.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY NextEntry;
|
|
POS2_QUEUE_ENTRY QueueEntry;
|
|
|
|
while ( !IsListEmpty(&Queue->Entries) ) {
|
|
NextEntry = RemoveHeadList(&Queue->Entries);
|
|
QueueEntry = CONTAINING_RECORD(NextEntry,OS2_QUEUE_ENTRY,Links);
|
|
RtlFreeHeap( Os2Heap, 0, QueueEntry);
|
|
}
|
|
}
|
|
|
|
POS2_QUEUE_ENTRY
|
|
Os2LocateQueueEntry(
|
|
IN POS2_QUEUE Queue,
|
|
IN ULONG ReadPosition
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called during DosReadQueue and DosPeekQueue
|
|
to determine if the specified queue contains an entry whose
|
|
entry id matches ReadPosition. Note that a read position of
|
|
0 matches the entry at the head of the queue.
|
|
|
|
This function must be called with the queue locked !
|
|
|
|
Arguments:
|
|
|
|
Queue - Supplies the address of the queue to search.
|
|
|
|
ReadPosition - Supplies the key of the queue entry to
|
|
locate.
|
|
|
|
Return Value:
|
|
|
|
NON-NULL - Returns the address of the queue entry that matches
|
|
ReadPosition.
|
|
|
|
NULL - The queue does not contain the specified entry.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY NextEntry;
|
|
POS2_QUEUE_ENTRY QueueEntry;
|
|
|
|
if ( IsListEmpty(&Queue->Entries) ) {
|
|
return NULL;
|
|
}
|
|
|
|
if ( ReadPosition == 0 ) {
|
|
QueueEntry = CONTAINING_RECORD(Queue->Entries.Flink,OS2_QUEUE_ENTRY,Links);
|
|
IF_OS2_DEBUG( QUEUES ) DumpQueueEntry("LocateReturn",QueueEntry);
|
|
return QueueEntry;
|
|
}
|
|
|
|
NextEntry = Queue->Entries.Flink;
|
|
|
|
while ( NextEntry != &Queue->Entries ) {
|
|
QueueEntry = CONTAINING_RECORD(NextEntry,OS2_QUEUE_ENTRY,Links);
|
|
if ( QueueEntry->EntryId == ReadPosition ) {
|
|
return QueueEntry;
|
|
}
|
|
NextEntry = NextEntry->Flink;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
Os2ProcessSemBlocks(
|
|
IN POS2_QUEUE Queue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function processes the semaphore blocks for the specified
|
|
queue. This function is called whenever an entry is placed in the
|
|
queue. Its function is to signal all waiting semaphores and
|
|
to deallocate the semaphore block.
|
|
|
|
Arguments:
|
|
|
|
Queue - Supplies the queue whose semaphore blocks need processing.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLIST_ENTRY NextEntry;
|
|
POS2_QUEUE_SEM_BLOCK SemBlock;
|
|
|
|
while ( !IsListEmpty(&Queue->SemBlocks) ) {
|
|
NextEntry = RemoveHeadList(&Queue->SemBlocks);
|
|
SemBlock = CONTAINING_RECORD(NextEntry,OS2_QUEUE_SEM_BLOCK,Links);
|
|
NtSetEvent(SemBlock->NtEvent,NULL);
|
|
RtlFreeHeap( Os2Heap, 0, SemBlock);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Os2WaitQueueEntries(
|
|
IN OS2_WAIT_REASON WaitReason,
|
|
IN POS2_THREAD WaitingThread,
|
|
IN POS2_API_MSG WaitReplyMessage,
|
|
IN PVOID WaitParameter,
|
|
IN PVOID SatisfyParameter1,
|
|
IN PVOID SatisfyParameter2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by the Os2NotifyWait function whenever
|
|
a queue waiter is awakened. This is typically due to a queue write
|
|
where there are waiters present and a write occurs. Os2 Exception
|
|
logic may also cause this routine to be entered.
|
|
|
|
Arguments:
|
|
|
|
WaitReason - Supplies the reason this wait is being satisfied.
|
|
|
|
WaitingThread - Supplies the address of the thread waiting being
|
|
unwaited.
|
|
|
|
WaitReplyMessage - Supplies the API message that originally caused
|
|
the thread to wait.
|
|
|
|
WaitParameter - Supplies the address of the queue that the waiter is
|
|
waiting on.
|
|
|
|
SatisfyParameter1 - Supplies the address of the queue that has an
|
|
entry to satisfy the wait. The queue is locked by the wait
|
|
notifier.
|
|
|
|
SatisfyParameter2 - If not null, then supplies the error code
|
|
for the wait. Used when a queue is being closed.
|
|
|
|
Return Value:
|
|
|
|
TRUE - A reply should be generated
|
|
|
|
FALSE - A reply should not be generated
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
POS2_DOSREADQUEUE_MSG read = &WaitReplyMessage->u.DosReadQueue;
|
|
POS2_DOSPEEKQUEUE_MSG peek = &WaitReplyMessage->u.DosPeekQueue;
|
|
POS2_QUEUE Queue;
|
|
APIRET rc;
|
|
POS2_QUEUE_ENTRY QueueEntry;
|
|
ULONG ReadPosition;
|
|
BOOLEAN Peek;
|
|
|
|
UNREFERENCED_PARAMETER(WaitingThread);
|
|
if (WaitReason == WaitInterrupt) {
|
|
return TRUE;
|
|
}
|
|
else {
|
|
ASSERT(WaitReason == WaitQueue);
|
|
}
|
|
|
|
if ( WaitParameter != SatisfyParameter1 ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If we are being waked due to queue closure, then return
|
|
// with error.
|
|
//
|
|
|
|
if ( SatisfyParameter2 ) {
|
|
rc = (APIRET)SatisfyParameter2;
|
|
}
|
|
else {
|
|
|
|
Queue = (POS2_QUEUE)SatisfyParameter1;
|
|
|
|
if ( WaitReplyMessage->ApiNumber == Os2PeekQueue ) {
|
|
Peek = TRUE;
|
|
ReadPosition = peek->ReadPosition;
|
|
}
|
|
else {
|
|
Peek = FALSE;
|
|
ReadPosition = read->ReadPosition;
|
|
}
|
|
|
|
QueueEntry = Os2LocateQueueEntry(Queue, ReadPosition);
|
|
|
|
if ( !QueueEntry ) {
|
|
if ( ReadPosition ) {
|
|
rc = ERROR_QUE_ELEMENT_NOT_EXIST;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
|
|
rc = NO_ERROR;
|
|
if ( Peek ) {
|
|
Os2PeekQueueEntry(Queue,QueueEntry,peek);
|
|
}
|
|
else {
|
|
Os2ReadQueueEntry(QueueEntry,read);
|
|
}
|
|
IF_OS2_DEBUG( QUEUES ) DumpQueueEntry("WaitReturn",QueueEntry);
|
|
}
|
|
}
|
|
|
|
WaitReplyMessage->ReturnedErrorValue = rc;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
Os2QueueWaitCheck(
|
|
POS2_QUEUE Queue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to check to see if there are queue waiters
|
|
whose waits might be satisfied if entries exist on the queue.
|
|
|
|
Arguments:
|
|
|
|
Queue - Supplies the address of the queue to check.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
POS2_WAIT_BLOCK WaitBlock;
|
|
|
|
//
|
|
// If there are no entries on the list, then there is nothing to
|
|
// do.
|
|
//
|
|
|
|
if ( IsListEmpty(&Queue->Entries) ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If there are entries in the queue, but no waiters, then return
|
|
// since there is no-one to wake.
|
|
//
|
|
|
|
if ( IsListEmpty(&Queue->Waiters) ) {
|
|
|
|
//
|
|
// actually, we need to deal w/ semaphores...
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// There are waiters, so pop the first waiter
|
|
//
|
|
|
|
WaitBlock = CONTAINING_RECORD(Queue->Waiters.Flink,OS2_WAIT_BLOCK,UserLink);
|
|
|
|
Os2NotifyWait(WaitQueue,(PVOID)Queue,NULL);
|
|
}
|
|
|
|
VOID
|
|
Os2ReadQueueEntry(
|
|
IN POS2_QUEUE_ENTRY QueueEntry,
|
|
OUT POS2_DOSREADQUEUE_MSG ReadMsg
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function reads the specified queue entry from its queue
|
|
|
|
Arguments:
|
|
|
|
QueueEntry - Supplies the address of the queue entry to read from the
|
|
queue.
|
|
|
|
ReadMsg - Supplies the read queue api message to fill in.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Remove the queue entry, capture all the data,
|
|
// and free the entry;
|
|
//
|
|
|
|
RemoveEntryList(&QueueEntry->Links);
|
|
|
|
ReadMsg->RequestInfo = QueueEntry->RequestData;
|
|
ReadMsg->DataLength = QueueEntry->ElementLength;
|
|
ReadMsg->Data = QueueEntry->ElementAddress;
|
|
ReadMsg->ElementPriority = (BYTE)QueueEntry->Priority;
|
|
RtlFreeHeap( Os2Heap, 0, QueueEntry);
|
|
}
|
|
|
|
VOID
|
|
Os2PeekQueueEntry(
|
|
IN POS2_QUEUE Queue,
|
|
IN POS2_QUEUE_ENTRY QueueEntry,
|
|
OUT POS2_DOSPEEKQUEUE_MSG PeekMsg
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function peeks the specified queue entry from its queue. It
|
|
also computes the next queue entry.
|
|
|
|
Arguments:
|
|
|
|
Queue - Supplies the queue to peek from.
|
|
|
|
QueueEntry - Supplies the address of the queue entry to read from the
|
|
queue.
|
|
|
|
PeekMsg - Supplies the read queue api message to fill in.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PeekMsg->RequestInfo = QueueEntry->RequestData;
|
|
PeekMsg->DataLength = QueueEntry->ElementLength;
|
|
PeekMsg->Data = QueueEntry->ElementAddress;
|
|
PeekMsg->ElementPriority = (BYTE)QueueEntry->Priority;
|
|
|
|
PeekMsg->ReadPosition=QueueEntry->EntryId;
|
|
|
|
}
|
|
|
|
VOID
|
|
DumpQueueEntry(
|
|
IN PSZ Str,
|
|
IN POS2_QUEUE_ENTRY QueueEntry
|
|
)
|
|
{
|
|
#if DBG
|
|
KdPrint(("\n*** %s QUEUE ENTRY st 0x%lx***\n",Str,QueueEntry));
|
|
KdPrint(("RequestData.SenderProcessId 0x%lx\n",QueueEntry->RequestData.SenderProcessId));
|
|
KdPrint(("RequestData.SenderData 0x%lx\n",QueueEntry->RequestData.SenderData ));
|
|
KdPrint(("EntryId; 0x%lx\n",QueueEntry->EntryId ));
|
|
KdPrint(("ElementAddress 0x%lx\n",QueueEntry->ElementAddress ));
|
|
KdPrint(("ElementLength 0x%lx\n",QueueEntry->ElementLength ));
|
|
KdPrint(("Priority 0x%lx\n",QueueEntry->Priority ));
|
|
KdPrint(("*******************\n"));
|
|
#endif
|
|
}
|