wine/dlls/netapi32/nbcmdqueue.c
2006-05-23 14:11:13 +02:00

200 lines
5.4 KiB
C

/* Copyright (c) 2003 Juan Lang
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include "wine/debug.h"
#include "nbcmdqueue.h"
WINE_DEFAULT_DEBUG_CHANNEL(netbios);
struct NBCmdQueue
{
HANDLE heap;
CRITICAL_SECTION cs;
PNCB head;
};
#define CANCEL_EVENT_PTR(ncb) (PHANDLE)((ncb)->ncb_reserve)
#define NEXT_PTR(ncb) (PNCB *)((ncb)->ncb_reserve + sizeof(HANDLE))
/* The reserved area of an ncb will be used for the following data:
* - a cancelled flag (BOOL, 4 bytes??)
* - a handle to an event that's set by a cancelled command on completion
* (HANDLE, 4 bytes)
* These members are used in the following way
* - on cancel, set the event member of the reserved field (with create event)
* - NBCmdComplete will delete the ncb from the queue of there's no event;
* otherwise it will set the event and not delete the ncb
* - cancel must lock the queue before finding the ncb in it, and can unlock it
* once it's set the event (and the cancelled flag)
* - NBCmdComplete must lock the queue before attempting to remove the ncb or
* check the event
* - NBCmdQueueCancelAll will lock the queue, and cancel all ncb's in the queue.
* It'll then unlock the queue, and wait on the event in the head of the queue
* until there's no more ncb's in the queue.
* Space optimization: use the handle as a boolean. NULL == 0 => not cancelled.
* Non-NULL == valid handle => cancelled. This allows storing a next pointer
* in the ncb's reserved field as well, avoiding a memory alloc for a new
* command (cool).
*/
struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap)
{
struct NBCmdQueue *queue;
if (heap == NULL)
heap = GetProcessHeap();
queue = HeapAlloc(heap, 0, sizeof(struct NBCmdQueue));
if (queue)
{
queue->heap = heap;
InitializeCriticalSection(&queue->cs);
queue->head = NULL;
}
return queue;
}
UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb)
{
UCHAR ret;
TRACE(": queue %p, ncb %p\n", queue, ncb);
if (!queue)
return NRC_BADDR;
if (!ncb)
return NRC_INVADDRESS;
*CANCEL_EVENT_PTR(ncb) = NULL;
EnterCriticalSection(&queue->cs);
*NEXT_PTR(ncb) = queue->head;
queue->head = ncb;
ret = NRC_GOODRET;
LeaveCriticalSection(&queue->cs);
TRACE("returning 0x%02x\n", ret);
return ret;
}
static PNCB *NBCmdQueueFindNBC(struct NBCmdQueue *queue, PNCB ncb)
{
PNCB *ret;
if (!queue || !ncb)
ret = NULL;
else
{
ret = &queue->head;
while (ret && *ret != ncb)
ret = NEXT_PTR(*ret);
}
return ret;
}
UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb)
{
UCHAR ret;
PNCB *spot;
TRACE(": queue %p, ncb %p\n", queue, ncb);
if (!queue)
return NRC_BADDR;
if (!ncb)
return NRC_INVADDRESS;
EnterCriticalSection(&queue->cs);
spot = NBCmdQueueFindNBC(queue, ncb);
if (spot)
{
*CANCEL_EVENT_PTR(*spot) = CreateEventW(NULL, FALSE, FALSE, NULL);
WaitForSingleObject(*CANCEL_EVENT_PTR(*spot), INFINITE);
CloseHandle(*CANCEL_EVENT_PTR(*spot));
*spot = *NEXT_PTR(*spot);
if (ncb->ncb_retcode == NRC_CMDCAN)
ret = NRC_CMDCAN;
else
ret = NRC_CANOCCR;
}
else
ret = NRC_INVADDRESS;
LeaveCriticalSection(&queue->cs);
TRACE("returning 0x%02x\n", ret);
return ret;
}
UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode)
{
UCHAR ret;
PNCB *spot;
TRACE(": queue %p, ncb %p\n", queue, ncb);
if (!queue)
return NRC_BADDR;
if (!ncb)
return NRC_INVADDRESS;
EnterCriticalSection(&queue->cs);
spot = NBCmdQueueFindNBC(queue, ncb);
if (spot)
{
if (*CANCEL_EVENT_PTR(*spot))
SetEvent(*CANCEL_EVENT_PTR(*spot));
else
*spot = *NEXT_PTR(*spot);
ret = NRC_GOODRET;
}
else
ret = NRC_INVADDRESS;
LeaveCriticalSection(&queue->cs);
TRACE("returning 0x%02x\n", ret);
return ret;
}
UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue)
{
UCHAR ret;
TRACE(": queue %p\n", queue);
if (!queue)
return NRC_BADDR;
EnterCriticalSection(&queue->cs);
while (queue->head)
{
TRACE(": waiting for ncb %p (command 0x%02x)\n", queue->head,
queue->head->ncb_command);
NBCmdQueueCancel(queue, queue->head);
}
LeaveCriticalSection(&queue->cs);
ret = NRC_GOODRET;
TRACE("returning 0x%02x\n", ret);
return ret;
}
void NBCmdQueueDestroy(struct NBCmdQueue *queue)
{
TRACE(": queue %p\n", queue);
if (queue)
{
NBCmdQueueCancelAll(queue);
DeleteCriticalSection(&queue->cs);
HeapFree(queue->heap, 0, queue);
}
}