mirror of
https://github.com/reactos/reactos.git
synced 2024-09-19 09:09:53 +00:00
284 lines
6.5 KiB
C
284 lines
6.5 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Kernel Streaming
|
|
* FILE: drivers/ksfilter/ks/worker.c
|
|
* PURPOSE: KS Allocator functions
|
|
* PROGRAMMER: Johannes Anderwald
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* ===============================================================
|
|
Worker Management Functions
|
|
*/
|
|
|
|
typedef struct
|
|
{
|
|
WORK_QUEUE_ITEM WorkItem;
|
|
|
|
KEVENT Event;
|
|
KSPIN_LOCK Lock;
|
|
WORK_QUEUE_TYPE Type;
|
|
LONG Counter;
|
|
LONG QueuedWorkItemCount;
|
|
LIST_ENTRY QueuedWorkItems;
|
|
|
|
PWORK_QUEUE_ITEM CountedWorkItem;
|
|
}KSIWORKER, *PKSIWORKER;
|
|
|
|
VOID
|
|
NTAPI
|
|
WorkItemRoutine(
|
|
IN PVOID Context)
|
|
{
|
|
PKSIWORKER KsWorker;
|
|
KIRQL OldLevel;
|
|
PWORK_QUEUE_ITEM WorkItem;
|
|
PLIST_ENTRY Entry;
|
|
|
|
|
|
/* get ks worker implementation */
|
|
KsWorker = (PKSIWORKER)Context;
|
|
|
|
/* acquire back the lock */
|
|
KeAcquireSpinLock(&KsWorker->Lock, &OldLevel);
|
|
|
|
do
|
|
{
|
|
/* sanity check */
|
|
ASSERT(!IsListEmpty(&KsWorker->QueuedWorkItems));
|
|
|
|
/* remove first entry */
|
|
Entry = RemoveHeadList(&KsWorker->QueuedWorkItems);
|
|
/* get offset to work item */
|
|
WorkItem = (PWORK_QUEUE_ITEM)CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List);
|
|
|
|
/* release lock as the callback might call one KsWorker functions */
|
|
KeReleaseSpinLock(&KsWorker->Lock, OldLevel);
|
|
|
|
/* now dispatch the work */
|
|
WorkItem->WorkerRoutine(WorkItem->Parameter);
|
|
|
|
/* acquire back the lock */
|
|
KeAcquireSpinLock(&KsWorker->Lock, &OldLevel);
|
|
|
|
/* decrement queued work item count */
|
|
InterlockedDecrement(&KsWorker->QueuedWorkItemCount);
|
|
|
|
}while(KsWorker->QueuedWorkItemCount);
|
|
|
|
/* release the lock */
|
|
KeReleaseSpinLock(&KsWorker->Lock, OldLevel);
|
|
|
|
/* signal completion event */
|
|
KeSetEvent(&KsWorker->Event, IO_NO_INCREMENT, FALSE);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
@implemented
|
|
*/
|
|
KSDDKAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
KsRegisterWorker(
|
|
IN WORK_QUEUE_TYPE WorkQueueType,
|
|
OUT PKSWORKER* Worker)
|
|
{
|
|
PKSIWORKER KsWorker;
|
|
|
|
|
|
if (WorkQueueType != CriticalWorkQueue &&
|
|
WorkQueueType != DelayedWorkQueue &&
|
|
WorkQueueType != HyperCriticalWorkQueue)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* allocate worker context */
|
|
KsWorker = AllocateItem(NonPagedPool, sizeof(KSIWORKER));
|
|
if (!KsWorker)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* initialize the work ctx */
|
|
ExInitializeWorkItem(&KsWorker->WorkItem, WorkItemRoutine, (PVOID)KsWorker);
|
|
/* setup type */
|
|
KsWorker->Type = WorkQueueType;
|
|
/* Initialize work item queue */
|
|
InitializeListHead(&KsWorker->QueuedWorkItems);
|
|
/* initialize work item lock */
|
|
KeInitializeSpinLock(&KsWorker->Lock);
|
|
/* initialize event */
|
|
KeInitializeEvent(&KsWorker->Event, NotificationEvent, FALSE);
|
|
|
|
*Worker = KsWorker;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
@implemented
|
|
*/
|
|
KSDDKAPI
|
|
VOID
|
|
NTAPI
|
|
KsUnregisterWorker(
|
|
IN PKSWORKER Worker)
|
|
{
|
|
PKSIWORKER KsWorker;
|
|
KIRQL OldIrql;
|
|
|
|
if (!Worker)
|
|
return;
|
|
|
|
/* get ks worker implementation */
|
|
KsWorker = (PKSIWORKER)Worker;
|
|
/* acquire spinlock */
|
|
KeAcquireSpinLock(&KsWorker->Lock, &OldIrql);
|
|
/* fake status running to avoid work items to be queued by the counted worker */
|
|
KsWorker->Counter = 1;
|
|
/* is there currently a work item active */
|
|
if (KsWorker->QueuedWorkItemCount)
|
|
{
|
|
/* release the lock */
|
|
KeReleaseSpinLock(&KsWorker->Lock, OldIrql);
|
|
/* wait for the worker routine to finish */
|
|
KeWaitForSingleObject(&KsWorker->Event, Executive, KernelMode, FALSE, NULL);
|
|
}
|
|
else
|
|
{
|
|
/* no work item active, just release the lock */
|
|
KeReleaseSpinLock(&KsWorker->Lock, OldIrql);
|
|
}
|
|
/* free worker context */
|
|
FreeItem(KsWorker);
|
|
}
|
|
|
|
/*
|
|
@implemented
|
|
*/
|
|
KSDDKAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
KsRegisterCountedWorker(
|
|
IN WORK_QUEUE_TYPE WorkQueueType,
|
|
IN PWORK_QUEUE_ITEM CountedWorkItem,
|
|
OUT PKSWORKER* Worker)
|
|
{
|
|
NTSTATUS Status;
|
|
PKSIWORKER KsWorker;
|
|
|
|
/* check for counted work item parameter */
|
|
if (!CountedWorkItem)
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
|
|
/* create the work ctx */
|
|
Status = KsRegisterWorker(WorkQueueType, Worker);
|
|
/* check for success */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* get ks worker implementation */
|
|
KsWorker = *(PKSIWORKER*)Worker;
|
|
/* store counted work item */
|
|
KsWorker->CountedWorkItem = CountedWorkItem;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
@implemented
|
|
*/
|
|
KSDDKAPI
|
|
ULONG
|
|
NTAPI
|
|
KsDecrementCountedWorker(
|
|
IN PKSWORKER Worker)
|
|
{
|
|
PKSIWORKER KsWorker;
|
|
LONG Counter;
|
|
|
|
/* did the caller pass a work ctx */
|
|
if (!Worker)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* get ks worker implementation */
|
|
KsWorker = (PKSIWORKER)Worker;
|
|
/* decrement counter */
|
|
Counter = InterlockedDecrement(&KsWorker->Counter);
|
|
/* return result */
|
|
return Counter;
|
|
}
|
|
|
|
/*
|
|
@implemented
|
|
*/
|
|
KSDDKAPI
|
|
ULONG
|
|
NTAPI
|
|
KsIncrementCountedWorker(
|
|
IN PKSWORKER Worker)
|
|
{
|
|
PKSIWORKER KsWorker;
|
|
LONG Counter;
|
|
|
|
/* did the caller pass a work ctx */
|
|
if (!Worker)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* get ks worker implementation */
|
|
KsWorker = (PKSIWORKER)Worker;
|
|
/* increment counter */
|
|
Counter = InterlockedIncrement(&KsWorker->Counter);
|
|
if (Counter == 1)
|
|
{
|
|
/* this is the first work item in list, so queue a real work item */
|
|
KsQueueWorkItem(Worker, KsWorker->CountedWorkItem);
|
|
}
|
|
|
|
/* return current counter */
|
|
return Counter;
|
|
}
|
|
|
|
/*
|
|
@implemented
|
|
*/
|
|
KSDDKAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
KsQueueWorkItem(
|
|
IN PKSWORKER Worker,
|
|
IN PWORK_QUEUE_ITEM WorkItem)
|
|
{
|
|
PKSIWORKER KsWorker;
|
|
KIRQL OldIrql;
|
|
|
|
/* check for all parameters */
|
|
if (!Worker || !WorkItem)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* get ks worker implementation */
|
|
KsWorker = (PKSIWORKER)Worker;
|
|
/* lock the work queue */
|
|
KeAcquireSpinLock(&KsWorker->Lock, &OldIrql);
|
|
/* insert work item to list */
|
|
InsertTailList(&KsWorker->QueuedWorkItems, &WorkItem->List);
|
|
/* increment active count */
|
|
InterlockedIncrement(&KsWorker->QueuedWorkItemCount);
|
|
/* is this the first work item */
|
|
if (KsWorker->QueuedWorkItemCount == 1)
|
|
{
|
|
/* clear event */
|
|
KeClearEvent(&KsWorker->Event);
|
|
/* it is, queue it */
|
|
ExQueueWorkItem(&KsWorker->WorkItem, KsWorker->Type);
|
|
}
|
|
/* release lock */
|
|
KeReleaseSpinLock(&KsWorker->Lock, OldIrql);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|