1
0
mirror of https://github.com/UzixLS/KernelEx.git synced 2025-07-19 07:21:20 +03:00

import KernelEx-4.5-Beta2

This commit is contained in:
UzixLS
2018-11-03 16:21:44 +03:00
parent 09929b2b7d
commit 2e7f4ba60c
47 changed files with 9203 additions and 8829 deletions

View File

@ -1,7 +1,6 @@
/*
* KernelEx
* Copyright (C) 2009, Tihiy
* Copyright (c) 2006 Robert Shearman (WINE Project)
* Copyright (C) 2009-2010, Tihiy
*
* This file is part of KernelEx source code.
*
@ -21,6 +20,7 @@
*/
#include <windows.h>
#include <malloc.h>
#include "shlord.h"
/*
@ -30,13 +30,21 @@
* RegisterWaitForSingleObjectEx
* UnregisterWait
* UnregisterWaitEx
* CreateTimerQueue
* CreateTimerQueueTimer
* DeleteTimerQueue
* DeleteTimerQueueEx
* DeleteTimerQueueTimer
* all functions could be implemented with shlwapi,
* but they don't support async de-registration and most flags
* Also, Wine functions can cause problems in cases like
* when second unregister is called, or handle is reused...
* But Windows XP also fails hard in such cases.
* but they don't support async de-registration and most flags.
* Wine code was completely purged outta here - it's no good.
*/
/* RegisterWait/TimerQueues are implemented using waitable timers,
* unlike wine/shlwapi/whatever implementations exist.
*/
#define TPS_EXECUTEIO 0x00000001
#define TPS_LONGEXECTIME 0x00000008
@ -64,107 +72,379 @@ BOOL WINAPI QueueUserWorkItem_new( LPTHREAD_START_ROUTINE Function, PVOID Contex
return SHQueueUserWorkItem( Function, Context, 0, NULL, NULL, NULL, dwFlags );
}
//registerwait routines
//256h should be enough for everyone
#define MAX_WAIT_ITEMS (MAXIMUM_WAIT_OBJECTS / 2)
#define MAX_WAIT_THREADS 8
#define WAIT_STATE_ACTIVE 0 //item is in wait thread
#define WAIT_STATE_UNREGISTERED 1 //item is unregistered and queued to be removed from wait thread
#define WAIT_STATE_REMOVED 2 //item is removed from wait thread
#define WAIT_STATE_DELETESYNC 3 //item will be destroyed with removal
#define WAIT_STATE_GAMEOVER 4 //item is destroyed
#define WAIT_STATE_ABANDONED 5 //item expired and removed from wait thread but not yet unregistered
#define NONSENSE 10000
// RegisterWaitForSingleObject routines.
typedef struct
{
HANDLE Object;
HANDLE CancelEvent;
HANDLE TimerObject;
WAITORTIMERCALLBACK Callback;
PVOID Context;
ULONG Milliseconds;
ULONG Flags;
HANDLE CompletionEvent;
LONG DeleteCount;
BOOLEAN CallbackInProgress;
} wait_work_item_struct, *wait_work_item_ptr;
PVOID waitThread;
LONG State;
LONG CallbacksPending;
HANDLE CompletionEvent;
HANDLE CallbackEvent;
} WAIT_WORK_ITEM, *PWAIT_WORK_ITEM;
static void delete_wait_work_item(wait_work_item_ptr wait_work_item)
typedef struct
{
CloseHandle( wait_work_item->CancelEvent );
wait_work_item->CancelEvent = 0; //in case someone tries to work on deleted handle
HeapFree( GetProcessHeap(), 0, wait_work_item );
HANDLE hThread;
HANDLE WaitEvent;
BOOL InSyncCallback;
LONG nItems;
PWAIT_WORK_ITEM waitItems[MAX_WAIT_ITEMS];
} WAIT_THREAD, *PWAIT_THREAD;
typedef struct
{
PWAIT_WORK_ITEM item;
BOOLEAN TimerOrWaitFired;
} ASYNC_CALL, *PASYNC_CALL;
static PWAIT_THREAD waitthreads[MAX_WAIT_THREADS] = {0};
static CRITICAL_SECTION wait_cs;
BOOL init_threadpool()
{
InitializeCriticalSection(&wait_cs);
return TRUE;
}
static DWORD CALLBACK wait_thread_proc(LPVOID Arg)
static VOID ActivateTimer(PWAIT_WORK_ITEM workItem)
{
wait_work_item_ptr wait_work_item = (wait_work_item_ptr)Arg;
BOOL alertable = (wait_work_item->Flags & WT_EXECUTEINIOTHREAD) ? TRUE : FALSE;
HANDLE handles[2] = { wait_work_item->Object, wait_work_item->CancelEvent };
HANDLE completion_event;
DWORD status;
if (workItem->Milliseconds != INFINITE)
{
LARGE_INTEGER timeout;
timeout.QuadPart = workItem->Milliseconds*(LONGLONG)-10000;
SetWaitableTimer(workItem->TimerObject,&timeout,0,NULL,NULL,FALSE);
}
}
static VOID DestroyWaitItem(PWAIT_WORK_ITEM workItem)
{
CloseHandle(workItem->CallbackEvent);
CloseHandle(workItem->TimerObject);
HeapFree(GetProcessHeap(),0,workItem);
}
while (TRUE)
{
status = WaitForMultipleObjectsEx( 2, handles, FALSE, wait_work_item->Milliseconds, alertable );
if (status == STATUS_WAIT_0 || status == STATUS_TIMEOUT)
{
BOOL TimerOrWaitFired = (status == STATUS_WAIT_0) ? FALSE : TRUE;
static VOID CALLBACK AddWaitItem(ULONG_PTR Arg)
{
PWAIT_WORK_ITEM workItem = (PWAIT_WORK_ITEM)Arg;
PWAIT_THREAD waitThread = (PWAIT_THREAD)workItem->waitThread;
int i;
for (i=0;i<waitThread->nItems;i++)
{
//we can't have one handle several times
if (waitThread->waitItems[i]->Object == workItem->Object)
{
workItem->waitThread = NULL;
SetEvent(workItem->CompletionEvent);
return;
}
}
waitThread->waitItems[waitThread->nItems] = workItem;
waitThread->nItems++;
SetEvent(workItem->CompletionEvent);
//what if callback will fire before RegisterWait... returns?
ActivateTimer(workItem);
}
wait_work_item->CallbackInProgress = TRUE;
wait_work_item->Callback( wait_work_item->Context, TimerOrWaitFired );
wait_work_item->CallbackInProgress = FALSE;
static VOID CALLBACK RemoveWaitItem(ULONG_PTR Arg)
{
PWAIT_WORK_ITEM workItem = (PWAIT_WORK_ITEM)Arg;
PWAIT_THREAD waitThread = (PWAIT_THREAD)workItem->waitThread;
int i, j;
for (i=0;i<MAX_WAIT_ITEMS;i++)
if (waitThread->waitItems[i] == workItem)
break;
if (i<MAX_WAIT_ITEMS)
{
for (j=i;j<waitThread->nItems-1;j++)
{
waitThread->waitItems[j]=waitThread->waitItems[j+1];
waitThread->waitItems[j+1]=NULL;
}
waitThread->nItems--;
}
//if unregister marked it for delete, kill it here
if (InterlockedCompareExchange(&workItem->State,WAIT_STATE_GAMEOVER,WAIT_STATE_DELETESYNC) == WAIT_STATE_DELETESYNC)
{
if (workItem->CompletionEvent) SetEvent(workItem->CompletionEvent);
DestroyWaitItem(workItem);
}
else
{
InterlockedCompareExchange(&workItem->State,WAIT_STATE_ABANDONED,WAIT_STATE_ACTIVE);
InterlockedCompareExchange(&workItem->State,WAIT_STATE_REMOVED,WAIT_STATE_UNREGISTERED); //mark it removed
}
}
if (wait_work_item->Flags & WT_EXECUTEONLYONCE)
break;
}
else
break; //CancelEvent signalled
}
static DWORD CALLBACK AsyncWaiterCall(LPVOID Arg)
{
PASYNC_CALL waiter = (PASYNC_CALL)Arg;
PWAIT_WORK_ITEM workItem = waiter->item;
if (workItem->State == WAIT_STATE_ACTIVE || workItem->State == WAIT_STATE_ABANDONED)
workItem->Callback(workItem->Context,waiter->TimerOrWaitFired);
LONG CallbacksPending = InterlockedDecrement(&workItem->CallbacksPending);
SetEvent(workItem->CallbackEvent);
if (CallbacksPending == 0 && workItem->State != WAIT_STATE_ACTIVE)
{
//if it's removed, all fine, destroy it here
if (InterlockedCompareExchange(&workItem->State,WAIT_STATE_GAMEOVER,WAIT_STATE_REMOVED) == WAIT_STATE_REMOVED)
{
if (workItem->CompletionEvent) SetEvent(workItem->CompletionEvent);
DestroyWaitItem(workItem);
}
else //destroy it with removal
InterlockedCompareExchange(&workItem->State,WAIT_STATE_DELETESYNC,WAIT_STATE_UNREGISTERED);
}
completion_event = wait_work_item->CompletionEvent;
if (completion_event)
SetEvent( completion_event );
HeapFree(GetProcessHeap(),0,Arg);
return 0;
}
if ( InterlockedIncrement( &wait_work_item->DeleteCount ) == 2 )
delete_wait_work_item( wait_work_item );
static VOID TerminateWaitThread(PWAIT_THREAD threadinfo)
{
int i;
EnterCriticalSection(&wait_cs);
for (i=0;i<MAX_WAIT_THREADS;i++)
{
if (waitthreads[i] == threadinfo)
{
waitthreads[i] = NULL;
break;
}
}
LeaveCriticalSection(&wait_cs);
//if thread died of failure, mark all wait items abandoned
for (i=0;i<threadinfo->nItems;i++)
InterlockedCompareExchange(&threadinfo->waitItems[i]->State,WAIT_STATE_ABANDONED,WAIT_STATE_ACTIVE);
//make sure there aren't any outstanding APCs
SleepEx(0,TRUE);
CloseHandle(threadinfo->hThread);
CloseHandle(threadinfo->WaitEvent);
HeapFree(GetProcessHeap(),0,threadinfo);
}
return 0;
static DWORD WaitThreadWait(PWAIT_THREAD threadinfo)
{
int i;
int nCount = threadinfo->nItems * 2;
HANDLE* handles = (HANDLE*)alloca(sizeof(HANDLE)*nCount);
for (i=0;i<threadinfo->nItems;i++)
{
handles[i*2] = threadinfo->waitItems[i]->Object;
handles[(i*2)+1] = threadinfo->waitItems[i]->TimerObject;
}
return WaitForMultipleObjectsEx(nCount,handles,FALSE,INFINITE,TRUE);
}
static DWORD CALLBACK WaitThreadProc(LPVOID Arg)
{
PWAIT_THREAD threadinfo = (PWAIT_THREAD)Arg;
//wait for first APC to come
SleepEx(INFINITE,TRUE);
while (TRUE)
{
SetEvent(threadinfo->WaitEvent);
if (!threadinfo->nItems) //no things to do, wait 3 seconds and leave
{
if (SleepEx(3000,TRUE)!=WAIT_IO_COMPLETION)
break;
else
continue;
}
DWORD status = WaitThreadWait(threadinfo);
ResetEvent(threadinfo->WaitEvent);
if (status == WAIT_FAILED) break; //FFFFFUUUU
if (status >= WAIT_OBJECT_0 && status <= WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS )
{
status -= WAIT_OBJECT_0;
PWAIT_WORK_ITEM workItem = threadinfo->waitItems[status/2];
BOOL TimerFired = (status & 1);
if (workItem->State != WAIT_STATE_ACTIVE) continue; //don't run if removed
if (workItem->CallbackEvent == NULL) //sync callback
{
SetEvent(threadinfo->WaitEvent);
threadinfo->InSyncCallback = TRUE;
workItem->Callback(workItem->Context,TimerFired);
threadinfo->InSyncCallback = FALSE;
}
else
{
PASYNC_CALL waiter = (PASYNC_CALL)HeapAlloc(GetProcessHeap(),0,sizeof(*waiter));
if (waiter)
{
waiter->item = workItem;
waiter->TimerOrWaitFired = TimerFired;
InterlockedIncrement(&workItem->CallbacksPending);
QueueUserWorkItem_new(AsyncWaiterCall,waiter,workItem->Flags);
}
}
if (workItem->Flags & WT_EXECUTEONLYONCE || workItem->Milliseconds == 0)
RemoveWaitItem((ULONG_PTR)workItem);
else
ActivateTimer(workItem);
}
}
TerminateWaitThread(threadinfo);
return 0;
}
static BOOL RegisterWaitItem(PWAIT_WORK_ITEM workItem)
{
EnterCriticalSection(&wait_cs);
BOOL success = FALSE;
int i;
for (i=0;i<MAX_WAIT_THREADS;i++)
{
if (waitthreads[i] == NULL)
{
DWORD dummy;
waitthreads[i] = (PWAIT_THREAD)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*waitthreads[i]));
if (waitthreads[i] != NULL)
waitthreads[i]->hThread = CreateThread(NULL, 0, WaitThreadProc, waitthreads[i], 0, &dummy);
if (waitthreads[i]->hThread == NULL)
{
HeapFree(GetProcessHeap(),0,waitthreads[i]);
waitthreads[i]=NULL;
break;
}
waitthreads[i]->WaitEvent = CreateEvent(NULL,TRUE,TRUE,NULL);
}
if (waitthreads[i]->nItems == MAX_WAIT_ITEMS || waitthreads[i]->InSyncCallback) //full or busy, gtfo
continue;
else
{
//try adding it
workItem->waitThread = waitthreads[i];
workItem->CompletionEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
if ( QueueUserAPC(AddWaitItem,waitthreads[i]->hThread,(ULONG_PTR)workItem) )
{
//wait until item is added
WaitForSingleObject(workItem->CompletionEvent,INFINITE);
success = (workItem->waitThread != NULL);
}
CloseHandle(workItem->CompletionEvent);
workItem->CompletionEvent = NULL;
if (success) break;
}
}
LeaveCriticalSection(&wait_cs);
return success;
}
static BOOL IsValidHandle( HANDLE hObject )
{
HANDLE newobject = NULL;
BOOL success = DuplicateHandle(GetCurrentProcess(),hObject,GetCurrentProcess(),&newobject,0,0,DUPLICATE_SAME_ACCESS);
if (success) CloseHandle(newobject);
return success;
}
/* MAKE_EXPORT RegisterWaitForSingleObject_new=RegisterWaitForSingleObject */
BOOL WINAPI RegisterWaitForSingleObject_new(PHANDLE phNewWaitObject, HANDLE hObject, WAITORTIMERCALLBACK Callback,
PVOID Context, ULONG dwMilliseconds, ULONG dwFlags)
BOOL WINAPI RegisterWaitForSingleObject_new(
PHANDLE phNewWaitObject, HANDLE hObject, WAITORTIMERCALLBACK Callback,
PVOID Context, ULONG dwMilliseconds, ULONG dwFlags)
{
//validate stuff first. we aren't Wine.
if (!phNewWaitObject || IsBadCodePtr((FARPROC)Callback) || WaitForSingleObject(hObject,0) == WAIT_FAILED)
if (!phNewWaitObject || IsBadCodePtr((FARPROC)Callback) || !IsValidHandle(hObject))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
wait_work_item_ptr wait_work_item;
wait_work_item = (wait_work_item_ptr)HeapAlloc(GetProcessHeap(), 0, sizeof(*wait_work_item));
PWAIT_WORK_ITEM newWorkItem = (PWAIT_WORK_ITEM)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*newWorkItem));
if (!newWorkItem) return FALSE;
newWorkItem->Object = hObject;
newWorkItem->Callback = Callback;
newWorkItem->Context = Context;
newWorkItem->Milliseconds = dwMilliseconds;
newWorkItem->Flags = dwFlags;
if (!(dwFlags & WT_EXECUTEINWAITTHREAD))
newWorkItem->CallbackEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
newWorkItem->State = WAIT_STATE_ACTIVE;
if (dwMilliseconds != INFINITE)
newWorkItem->TimerObject = CreateWaitableTimer(NULL,FALSE,NULL);
else
newWorkItem->TimerObject = CreateEvent(NULL,FALSE,FALSE,NULL); //dummy event which never fires
if (!wait_work_item)
if (!RegisterWaitItem(newWorkItem))
{
DestroyWaitItem(newWorkItem);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
return FALSE;
}
if ( dwMilliseconds == 0 ) dwFlags |= WT_EXECUTEONLYONCE;
wait_work_item->Object = hObject;
wait_work_item->Callback = Callback;
wait_work_item->Context = Context;
wait_work_item->Milliseconds = dwMilliseconds;
wait_work_item->Flags = dwFlags;
wait_work_item->CallbackInProgress = FALSE;
wait_work_item->DeleteCount = 0;
wait_work_item->CompletionEvent = NULL;
wait_work_item->CancelEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
if (!wait_work_item->CancelEvent)
{
HeapFree( GetProcessHeap(), 0, wait_work_item );
return FALSE;
}
*phNewWaitObject = (HANDLE)newWorkItem;
return TRUE;
}
if ( !QueueUserWorkItem_new(wait_thread_proc, wait_work_item, dwFlags) )
{
delete_wait_work_item( wait_work_item );
return FALSE;
}
*phNewWaitObject = (HANDLE)wait_work_item;
return TRUE;
/* MAKE_EXPORT UnregisterWaitEx_new=UnregisterWaitEx */
BOOL WINAPI UnregisterWaitEx_new( HANDLE WaitHandle, HANDLE CompletionEvent)
{
PWAIT_WORK_ITEM workItem = (PWAIT_WORK_ITEM)WaitHandle;
if (!workItem)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//9x dies if we queue an apc into dead thread so avoid it at all costs
if ( InterlockedCompareExchange(&workItem->State,WAIT_STATE_UNREGISTERED,WAIT_STATE_ACTIVE)==WAIT_STATE_ACTIVE )
{
PWAIT_THREAD waitthread = (PWAIT_THREAD)workItem->waitThread;
if (!QueueUserAPC(RemoveWaitItem,waitthread->hThread,(ULONG_PTR)workItem))
return FALSE;
//make sure thread is in wait state and isn't doing sth with item we delete
WaitForSingleObject(waitthread->WaitEvent,NONSENSE);
}
InterlockedCompareExchange(&workItem->State,WAIT_STATE_REMOVED,WAIT_STATE_ABANDONED);
BOOL pending = FALSE;
if (workItem->CallbacksPending)
{
pending = TRUE;
if (CompletionEvent != NULL)
{
if (CompletionEvent == INVALID_HANDLE_VALUE)
{
//wait for callbacks to finish
while (workItem->CallbacksPending)
WaitForSingleObject(workItem->CallbackEvent,INFINITE);
pending = FALSE;
}
else
InterlockedExchangePointer(&workItem->CompletionEvent, CompletionEvent);
}
}
//all async callbacks are finished for sure?
if (!pending)
{
//yes? and the item is removed from thread?
if (InterlockedCompareExchange(&workItem->State,WAIT_STATE_GAMEOVER,WAIT_STATE_REMOVED) == WAIT_STATE_REMOVED)
DestroyWaitItem(workItem);
else //not removed? okay, it will be destroyed with removal
InterlockedCompareExchange(&workItem->State,WAIT_STATE_DELETESYNC,WAIT_STATE_UNREGISTERED);
}
//otherwise, there are still pending callbacks and item will be destroyed as soon as all callbacks end
if (pending)
{
SetLastError(ERROR_IO_PENDING);
return FALSE;
}
return TRUE;
}
/* MAKE_EXPORT RegisterWaitForSingleObjectEx_new=RegisterWaitForSingleObjectEx */
@ -179,60 +459,366 @@ HANDLE WINAPI RegisterWaitForSingleObjectEx_new(HANDLE hObject,
return NULL;
}
/* MAKE_EXPORT UnregisterWaitEx_new=UnregisterWaitEx */
BOOL WINAPI UnregisterWaitEx_new(HANDLE WaitHandle, HANDLE CompletionEvent)
{
if (!WaitHandle)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
BOOL pending = FALSE;
wait_work_item_ptr wait_work_item = (wait_work_item_ptr)WaitHandle;
if ( !SetEvent( wait_work_item->CancelEvent ) ) //signal cancel
return FALSE;
if (wait_work_item->CallbackInProgress)
{
if (CompletionEvent != NULL)
{
if (CompletionEvent == INVALID_HANDLE_VALUE)
{
CompletionEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
if (!CompletionEvent)
return FALSE;
InterlockedExchangePointer( &wait_work_item->CompletionEvent, CompletionEvent );
if (wait_work_item->CallbackInProgress)
WaitForSingleObject( CompletionEvent, INFINITE );
CloseHandle( CompletionEvent );
}
else
{
InterlockedExchangePointer( &wait_work_item->CompletionEvent, CompletionEvent );
if (wait_work_item->CallbackInProgress)
pending = TRUE;
}
}
else
pending = TRUE;
}
if ( InterlockedIncrement(&wait_work_item->DeleteCount) == 2 )
{
pending = FALSE; //somehow callback is complete already
delete_wait_work_item( wait_work_item );
}
if ( pending )
{
SetLastError(ERROR_IO_PENDING);
return FALSE;
}
return TRUE;
}
/* MAKE_EXPORT UnregisterWait_new=UnregisterWait */
BOOL WINAPI UnregisterWait_new(HANDLE WaitHandle)
{
return UnregisterWaitEx_new(WaitHandle,NULL);
}
// Timer Queue routines
#define MAX_TIMERS MAXIMUM_WAIT_OBJECTS
typedef struct
{
HANDLE TimerObject;
WAITORTIMERCALLBACK Callback;
PVOID Parameter;
ULONG DueTime;
DWORD Period;
ULONG Flags;
PVOID TimerQueue;
LONG State;
LONG CallbacksPending;
HANDLE CompletionEvent;
HANDLE CallbackEvent;
} TIMER_ITEM, *PTIMER_ITEM;
typedef struct
{
HANDLE hThread;
HANDLE WaitEvent;
HANDLE CompletionEvent;
LONG nItems;
PTIMER_ITEM timers[MAX_TIMERS];
} TIMER_QUEUE, *PTIMER_QUEUE;
static PTIMER_QUEUE defaultTimerQueue = NULL;
static VOID DestroyTimerItem(PTIMER_ITEM timer)
{
CloseHandle(timer->CallbackEvent);
CloseHandle(timer->TimerObject);
HeapFree(GetProcessHeap(),0,timer);
}
static VOID CALLBACK ChangeTimerItem(PTIMER_ITEM timer)
{
//(re)activate waitable timer
LARGE_INTEGER timeout;
timeout.QuadPart = timer->DueTime * (LONGLONG)-10000;
SetWaitableTimer(timer->TimerObject,&timeout,(timer->Flags & WT_EXECUTEONLYONCE) ? 0 : timer->Period,NULL,NULL,FALSE);
}
static VOID CALLBACK AddTimerItem(ULONG_PTR Arg)
{
PTIMER_ITEM timer = (PTIMER_ITEM)Arg;
PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)timer->TimerQueue;
if (timerqueue->nItems == MAX_TIMERS) //too much timers
{
timer->TimerQueue = NULL;
SetEvent(timer->CompletionEvent);
return;
}
timerqueue->timers[timerqueue->nItems] = timer;
timerqueue->nItems++;
SetEvent(timer->CompletionEvent);
ChangeTimerItem(timer);
}
static VOID CALLBACK RemoveTimerItem(ULONG_PTR Arg)
{
PTIMER_ITEM timer = (PTIMER_ITEM)Arg;
PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)timer->TimerQueue;
int i, j;
for (i=0;i<MAX_TIMERS;i++)
if (timerqueue->timers[i] == timer)
break;
if (i<MAX_TIMERS)
{
for (j=i;j<timerqueue->nItems-1;j++)
{
timerqueue->timers[j]=timerqueue->timers[j+1];
timerqueue->timers[j+1]=NULL;
}
timerqueue->nItems--;
}
//if unregister marked it for delete, kill it here
if (InterlockedCompareExchange(&timer->State,WAIT_STATE_GAMEOVER,WAIT_STATE_DELETESYNC) == WAIT_STATE_DELETESYNC)
{
if (timer->CompletionEvent) SetEvent(timer->CompletionEvent);
DestroyTimerItem(timer);
}
else
InterlockedCompareExchange(&timer->State,WAIT_STATE_REMOVED,WAIT_STATE_UNREGISTERED); //mark it removed
}
static DWORD CALLBACK AsyncTimerCall(LPVOID Arg)
{
PTIMER_ITEM timer = PTIMER_ITEM(Arg);
if (timer->State == WAIT_STATE_ACTIVE)
timer->Callback(timer->Parameter,TRUE);
LONG CallbacksPending = InterlockedDecrement(&timer->CallbacksPending);
SetEvent(timer->CallbackEvent);
if (CallbacksPending == 0 && timer->State != WAIT_STATE_ACTIVE)
{
//if it's removed, all fine, destroy it here
if (InterlockedCompareExchange(&timer->State,WAIT_STATE_GAMEOVER,WAIT_STATE_REMOVED) == WAIT_STATE_REMOVED)
{
if (timer->CompletionEvent) SetEvent(timer->CompletionEvent);
DestroyTimerItem(timer);
}
else //destroy it with removal
InterlockedCompareExchange(&timer->State,WAIT_STATE_DELETESYNC,WAIT_STATE_UNREGISTERED);
}
return 0;
}
/* MAKE_EXPORT DeleteTimerQueueTimer_new=DeleteTimerQueueTimer */
BOOL WINAPI DeleteTimerQueueTimer_new( HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent)
{
//absolutely equal to UnregisterWaitEx
if (!Timer)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
PTIMER_ITEM timer = (PTIMER_ITEM)Timer;
PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)timer->TimerQueue;
if ( InterlockedCompareExchange(&timer->State,WAIT_STATE_UNREGISTERED,WAIT_STATE_ACTIVE)==WAIT_STATE_ACTIVE )
{
if (!QueueUserAPC(RemoveTimerItem,timerqueue->hThread,(ULONG_PTR)timer))
return FALSE;
//make sure thread is in wait state and isn't doing sth with item we delete
WaitForSingleObject(timerqueue->WaitEvent,NONSENSE);
}
BOOL pending = FALSE;
if (timer->CallbacksPending)
{
pending = TRUE;
if (CompletionEvent != NULL)
{
if (CompletionEvent == INVALID_HANDLE_VALUE)
{
while (timer->CallbacksPending)
WaitForSingleObject(timer->CallbackEvent,INFINITE);
pending = FALSE;
}
else
InterlockedExchangePointer(&timer->CompletionEvent, CompletionEvent);
}
}
if (!pending)
{
if (InterlockedCompareExchange(&timer->State,WAIT_STATE_GAMEOVER,WAIT_STATE_REMOVED) == WAIT_STATE_REMOVED)
DestroyTimerItem(timer);
else
InterlockedCompareExchange(&timer->State,WAIT_STATE_DELETESYNC,WAIT_STATE_UNREGISTERED);
}
if (pending)
{
SetLastError(ERROR_IO_PENDING);
return FALSE;
}
return TRUE;
}
static VOID CALLBACK TerminateTimerQueue(ULONG_PTR Arg)
{
PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)Arg;
CloseHandle(timerqueue->hThread);
CloseHandle(timerqueue->WaitEvent);
HeapFree(GetProcessHeap(),0,timerqueue);
ExitThread(0);
}
static VOID CALLBACK FinishTimerQueue(ULONG_PTR Arg)
{
int i;
PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)Arg;
EnterCriticalSection(&wait_cs);
if (defaultTimerQueue == timerqueue)
defaultTimerQueue = NULL;
LeaveCriticalSection(&wait_cs);
//delete all timers, waiting
for (i=timerqueue->nItems-1;i>=0;i--)
DeleteTimerQueueTimer_new( timerqueue, timerqueue->timers[i], INVALID_HANDLE_VALUE );
if (timerqueue->CompletionEvent)
SetEvent(timerqueue->CompletionEvent);
//queue object termination
QueueUserAPC(TerminateTimerQueue,GetCurrentThread(),Arg);
}
static DWORD TimerQueueWait(PTIMER_QUEUE timerqueue)
{
int i;
HANDLE* handles = (HANDLE*)alloca(sizeof(HANDLE)*timerqueue->nItems);
for (i=0;i<timerqueue->nItems;i++)
handles[i] = timerqueue->timers[i]->TimerObject;
return WaitForMultipleObjectsEx(timerqueue->nItems,handles,FALSE,INFINITE,TRUE);
}
static DWORD CALLBACK TimerQueueProc(LPVOID Arg)
{
PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)Arg;
while (TRUE)
{
SetEvent(timerqueue->WaitEvent);
if (!timerqueue->nItems) //wait for items
{
SleepEx(INFINITE,TRUE);
continue;
}
DWORD status = TimerQueueWait(timerqueue);
ResetEvent(timerqueue->WaitEvent);
if (status == WAIT_FAILED) break; //uberfail
if (status >= WAIT_OBJECT_0 && status <= WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS )
{
PTIMER_ITEM timer = timerqueue->timers[status-WAIT_OBJECT_0];
if (timer->State != WAIT_STATE_ACTIVE) continue;
if (timer->CallbackEvent == NULL) //sync callback
{
SetEvent(timerqueue->WaitEvent);
timer->Callback(timer->Parameter,TRUE);
}
else
{
InterlockedIncrement(&timer->CallbacksPending);
QueueUserWorkItem_new(AsyncTimerCall,timer,timer->Flags);
}
}
}
FinishTimerQueue((ULONG_PTR)timerqueue);
SleepEx(0,TRUE);
return 0;
}
/* MAKE_EXPORT CreateTimerQueue_new=CreateTimerQueue */
HANDLE WINAPI CreateTimerQueue_new(VOID)
{
PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*timerqueue));
if (timerqueue != NULL)
{
DWORD dummy;
timerqueue->WaitEvent = CreateEvent(NULL,TRUE,TRUE,NULL);
timerqueue->hThread = CreateThread(NULL,0,TimerQueueProc,timerqueue,0,&dummy);
if (!timerqueue->hThread)
{
HeapFree(GetProcessHeap(),HEAP_ZERO_MEMORY,timerqueue);
timerqueue = NULL;
}
}
return (HANDLE)timerqueue;
}
/* MAKE_EXPORT CreateTimerQueueTimer_new=CreateTimerQueueTimer */
BOOL WINAPI CreateTimerQueueTimer_new( PHANDLE phNewTimer, HANDLE TimerQueue, WAITORTIMERCALLBACK Callback,
PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags)
{
if (!phNewTimer || IsBadCodePtr((FARPROC)Callback))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
*phNewTimer = NULL;
if (TimerQueue == NULL)
{
EnterCriticalSection(&wait_cs);
if (defaultTimerQueue == NULL)
defaultTimerQueue = (PTIMER_QUEUE)CreateTimerQueue_new();
TimerQueue = defaultTimerQueue;
LeaveCriticalSection(&wait_cs);
if (TimerQueue == NULL) return FALSE;
}
PTIMER_ITEM timer = (PTIMER_ITEM)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*timer));
timer->TimerQueue = TimerQueue;
timer->Callback = Callback;
timer->Parameter = Parameter;
timer->DueTime = DueTime;
timer->Period = Period;
timer->Flags = Flags;
timer->State = WAIT_STATE_ACTIVE;
timer->TimerObject = CreateWaitableTimer(NULL,FALSE,NULL);
if (!(Flags & WT_EXECUTEINTIMERTHREAD))
timer->CallbackEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
timer->CompletionEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
BOOL success = (BOOL)QueueUserAPC(AddTimerItem,((PTIMER_QUEUE)TimerQueue)->hThread,(ULONG_PTR)timer);
if (success)
{
WaitForSingleObject(timer->CompletionEvent,INFINITE);
CloseHandle(timer->CompletionEvent);
timer->CompletionEvent = NULL;
success = (timer->TimerObject != NULL);
if (success) *phNewTimer = timer;
}
return success;
}
/* MAKE_EXPORT DeleteTimerQueueEx_new=DeleteTimerQueueEx */
BOOL WINAPI DeleteTimerQueueEx_new( HANDLE TimerQueue, HANDLE CompletionEvent)
{
PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)TimerQueue;
if (!timerqueue)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (CompletionEvent != NULL)
{
if (CompletionEvent == INVALID_HANDLE_VALUE)
timerqueue->CompletionEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
else
timerqueue->CompletionEvent = CompletionEvent;
}
QueueUserAPC(FinishTimerQueue,timerqueue->hThread,(ULONG_PTR)timerqueue);
if (CompletionEvent == INVALID_HANDLE_VALUE)
{
WaitForSingleObject(timerqueue->CompletionEvent,INFINITE);
CloseHandle(timerqueue->CompletionEvent);
}
return TRUE;
}
/* MAKE_EXPORT DeleteTimerQueue_new=DeleteTimerQueue */
BOOL WINAPI DeleteTimerQueue_new( HANDLE TimerQueue)
{
return DeleteTimerQueueEx_new(TimerQueue,NULL);
}
/* MAKE_EXPORT ChangeTimerQueueTimer_new=ChangeTimerQueueTimer */
BOOL WINAPI ChangeTimerQueueTimer_new( HANDLE TimerQueue, HANDLE Timer, ULONG DueTime, ULONG Period)
{
PTIMER_ITEM timer = (PTIMER_ITEM)Timer;
if (!timer || timer->State != WAIT_STATE_ACTIVE)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
timer->DueTime = DueTime;
timer->Period = Period;
return (BOOL)QueueUserAPC((PAPCFUNC)ChangeTimerItem,((PTIMER_QUEUE)timer->TimerQueue)->hThread,(ULONG_PTR)timer);
}
//some undocumented APIs too...
/* MAKE_EXPORT CancelTimerQueueTimer_new=CancelTimerQueueTimer */
BOOL WINAPI CancelTimerQueueTimer_new( HANDLE TimerQueue, HANDLE Timer)
{
PTIMER_ITEM timer = (PTIMER_ITEM)Timer;
if (!timer || timer->State != WAIT_STATE_ACTIVE)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return CancelWaitableTimer(timer->TimerObject);
}
/* MAKE_EXPORT SetTimerQueueTimer_new=SetTimerQueueTimer */
HANDLE WINAPI SetTimerQueueTimer_new( HANDLE TimerQueue, WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, DWORD Period, BOOL PreferIo )
{
HANDLE newHandle = NULL;
CreateTimerQueueTimer_new(&newHandle,TimerQueue,Callback,Parameter,DueTime,Period,PreferIo ? WT_EXECUTEINIOTHREAD : WT_EXECUTEDEFAULT);
return newHandle;
}