/*
* Copyright (c) Likewise Software. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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 General Public License
* for more details. You should have received a copy of the GNU General
* Public License along with this program. If not, see
* .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewise.com
*/
/*
* Copyright (C) Likewise Software. All rights reserved.
*
* Module Name:
*
* irpcontext.c
*
* Abstract:
*
* IO Test Driver
*
* Authors: Danilo Almeida (dalmeida@likewise.com)
*/
#include "iodriver.h"
#include "workqueue.h"
#include "lwthreads.h"
#include "lwlist.h"
#include
#include "lwioutils.h"
#include "ntlogmacros.h"
typedef ULONG IOTEST_WORK_ITEM_FLAGS;
#define IOTEST_WORK_ITEM_FLAG_IN_WORK_QUEUE 0x00000001
typedef struct _IOTEST_WORK_ITEM {
IOTEST_WORK_ITEM_FLAGS Flags;
LW_RTL_WINDOWS_TIME EventTime;
PVOID pContext;
PIOTEST_WORK_CALLBACK Callback;
LW_LIST_LINKS QueueLinks;
LW_LIST_LINKS RunQueueLinks;
} IOTEST_WORK_ITEM;
typedef struct _IOTEST_WORK_QUEUE {
// List of PIOTEST_WORK_ITEM
LW_LIST_LINKS Head;
PLW_RTL_THREAD pThread;
BOOLEAN IsShutdown;
LW_RTL_MUTEX Mutex;
LW_RTL_CONDITION_VARIABLE Condition;
} IOTEST_WORK_QUEUE;
static
BOOLEAN
ItpRemoveLockedWorkQueue(
IN PIOTEST_WORK_ITEM pWorkItem
);
NTSTATUS
ItCreateWorkItem(
OUT PIOTEST_WORK_ITEM* ppWorkItem
)
{
NTSTATUS status = STATUS_SUCCESS;
int EE = 0;
PIOTEST_WORK_ITEM pWorkItem = NULL;
status = RTL_ALLOCATE(&pWorkItem, IOTEST_WORK_ITEM, sizeof(*pWorkItem));
GOTO_CLEANUP_ON_STATUS_EE(status, EE);
cleanup:
*ppWorkItem = pWorkItem;
LOG_LEAVE_IF_STATUS_EE(status, EE);
return status;
}
VOID
ItDestroyWorkItem(
IN OUT PIOTEST_WORK_ITEM* ppWorkItem
)
{
RTL_FREE(ppWorkItem);
}
static
PVOID
ItpRunWorkQueue(
IN PVOID pContext
)
{
PIOTEST_WORK_QUEUE pWorkQueue = (PIOTEST_WORK_QUEUE) pContext;
LW_LIST_LINKS runListHead = { 0 };
BOOLEAN isAcquired = FALSE;
LwListInit(&runListHead);
for (;;)
{
PLW_RTL_WINDOWS_TIME pTimeout = NULL;
PIOTEST_WORK_ITEM pWorkItem = NULL;
BOOLEAN isSignalled = FALSE;
NTSTATUS status = STATUS_SUCCESS;
LW_RTL_WINDOWS_TIME now = 0;
PLW_LIST_LINKS pLinks = NULL;
PLW_LIST_LINKS pNextLinks = NULL;
if (!isAcquired)
{
LwRtlLockMutex(&pWorkQueue->Mutex);
isAcquired = TRUE;
}
// Find the timeout for the earliest event, if any.
if (!LwListIsEmpty(&pWorkQueue->Head))
{
pLinks = pWorkQueue->Head.Next;
pWorkItem = LW_STRUCT_FROM_FIELD(pLinks, IOTEST_WORK_ITEM, QueueLinks);
pTimeout = &pWorkItem->EventTime;
pWorkItem = NULL;
}
isSignalled = LwRtlWaitConditionVariable(&pWorkQueue->Condition, &pWorkQueue->Mutex, pTimeout);
if (pWorkQueue->IsShutdown)
{
break;
}
// Find any events that need to be run and put
// them in a separate local queue.
LWIO_ASSERT(isSignalled || pTimeout);
status = LwRtlGetCurrentWindowsTime(&now);
LWIO_ASSERT(!status);
for (pLinks = pWorkQueue->Head.Next;
pLinks != &pWorkQueue->Head;
pLinks = pNextLinks)
{
pNextLinks = pLinks->Next;
pWorkItem = LW_STRUCT_FROM_FIELD(pLinks, IOTEST_WORK_ITEM, QueueLinks);
if (pWorkItem->EventTime > now)
{
break;
}
ItpRemoveLockedWorkQueue(pWorkItem);
LwListInsertTail(&runListHead, &pWorkItem->QueueLinks);
}
// Run events w/o holding the queue lock.
LwRtlUnlockMutex(&pWorkQueue->Mutex);
isAcquired = FALSE;
while (!LwListIsEmpty(&runListHead))
{
pLinks = LwListRemoveHead(&runListHead);
pWorkItem = LW_STRUCT_FROM_FIELD(pLinks, IOTEST_WORK_ITEM, QueueLinks);
pWorkItem->Callback(pWorkItem, pWorkItem->pContext);
}
}
return NULL;
}
NTSTATUS
ItCreateWorkQueue(
OUT PIOTEST_WORK_QUEUE* ppWorkQueue
)
{
NTSTATUS status = STATUS_SUCCESS;
int EE = 0;
PIOTEST_WORK_QUEUE pWorkQueue = NULL;
status = LW_RTL_ALLOCATE(&pWorkQueue, IOTEST_WORK_QUEUE, sizeof(*pWorkQueue));
GOTO_CLEANUP_ON_STATUS(status);
LwListInit(&pWorkQueue->Head);
status = LwRtlInitializeConditionVariable(&pWorkQueue->Condition);
GOTO_CLEANUP_ON_STATUS(status);
status = LwRtlInitializeMutex(&pWorkQueue->Mutex, FALSE);
GOTO_CLEANUP_ON_STATUS(status);
status = LwRtlCreateThread(&pWorkQueue->pThread, ItpRunWorkQueue, pWorkQueue);
GOTO_CLEANUP_ON_STATUS(status);
cleanup:
if (status)
{
ItDestroyWorkQueue(&pWorkQueue);
}
*ppWorkQueue = pWorkQueue;
LOG_LEAVE_IF_STATUS_EE(status, EE);
return status;
}
VOID
ItDestroyWorkQueue(
IN OUT PIOTEST_WORK_QUEUE* ppWorkQueue
)
{
PIOTEST_WORK_QUEUE pWorkQueue = *ppWorkQueue;
if (pWorkQueue)
{
if (pWorkQueue->pThread)
{
pWorkQueue->IsShutdown = TRUE;
LwRtlSignalConditionVariable(&pWorkQueue->Condition);
LwRtlJoinThread(pWorkQueue->pThread);
}
LwRtlCleanupConditionVariable(&pWorkQueue->Condition);
LwRtlCleanupMutex(&pWorkQueue->Mutex);
LW_RTL_FREE(&pWorkQueue);
*ppWorkQueue = NULL;
}
}
NTSTATUS
ItAddWorkQueue(
IN PIOTEST_WORK_QUEUE pWorkQueue,
IN PIOTEST_WORK_ITEM pWorkItem,
IN PVOID pContext,
IN ULONG WaitSeconds,
IN PIOTEST_WORK_CALLBACK Callback
)
{
NTSTATUS status = STATUS_SUCCESS;
int EE = 0;
PLW_LIST_LINKS pLinks = NULL;
LW_RTL_WINDOWS_TIME now = 0;
status = LwRtlGetCurrentWindowsTime(&now);
GOTO_CLEANUP_ON_STATUS_EE(status, EE);
pWorkItem->pContext = pContext;
pWorkItem->EventTime = now + LW_RTL_WINDOWS_TIMESPAN_SECOND * WaitSeconds;
pWorkItem->Callback = Callback;
if (pWorkItem->EventTime < now)
{
status = STATUS_INTEGER_OVERFLOW;
GOTO_CLEANUP_ON_STATUS_EE(status, EE);
}
LwRtlLockMutex(&pWorkQueue->Mutex);
for (pLinks = pWorkQueue->Head.Next;
pLinks != &pWorkQueue->Head;
pLinks = pLinks->Next)
{
PIOTEST_WORK_ITEM pCurrentItem = LW_STRUCT_FROM_FIELD(pLinks, IOTEST_WORK_ITEM, QueueLinks);
if (pCurrentItem->EventTime > pWorkItem->EventTime)
{
break;
}
}
SetFlag(pWorkItem->Flags, IOTEST_WORK_ITEM_FLAG_IN_WORK_QUEUE);
LwListInsertBefore(pLinks, &pWorkItem->QueueLinks);
LwRtlSignalConditionVariable(&pWorkQueue->Condition);
LwRtlUnlockMutex(&pWorkQueue->Mutex);
cleanup:
LWIO_ASSERT(!status);
LOG_LEAVE_IF_STATUS_EE(status, EE);
return status;
}
static
BOOLEAN
ItpRemoveLockedWorkQueue(
IN PIOTEST_WORK_ITEM pWorkItem
)
{
BOOLEAN isFound = FALSE;
if (IsSetFlag(pWorkItem->Flags, IOTEST_WORK_ITEM_FLAG_IN_WORK_QUEUE))
{
ClearFlag(pWorkItem->Flags, IOTEST_WORK_ITEM_FLAG_IN_WORK_QUEUE);
LwListRemove(&pWorkItem->QueueLinks);
isFound = TRUE;
}
return isFound;
}
BOOLEAN
ItRemoveWorkQueue(
IN PIOTEST_WORK_QUEUE pWorkQueue,
IN PIOTEST_WORK_ITEM pWorkItem
)
{
BOOLEAN isFound = FALSE;
LwRtlLockMutex(&pWorkQueue->Mutex);
isFound = ItpRemoveLockedWorkQueue(pWorkItem);
LwRtlUnlockMutex(&pWorkQueue->Mutex);
return isFound;
}