Skip to content

Instantly share code, notes, and snippets.

@rbmm
Created May 20, 2023 17:47
Show Gist options
  • Save rbmm/1afb0938b5cd004e7a78a6d9328d9c6a to your computer and use it in GitHub Desktop.
Save rbmm/1afb0938b5cd004e7a78a6d9328d9c6a to your computer and use it in GitHub Desktop.
#define printf DbgPrint
#ifndef IDC_STATIC
#define IDC_STATIC 65535 // MAXUSHORT
#endif
struct DTimer
{
HANDLE _M_hTimer = 0;
LONG _M_dwRefCount = 1;
ULONG _M_n = 0;
ULONG _M_dwThreadId = GetCurrentThreadId();
LONG _M_stopping = FALSE;
void AddRef()
{
InterlockedIncrementNoFence(&_M_dwRefCount);
}
void Release()
{
if (!InterlockedDecrement(&_M_dwRefCount))
{
delete this;
}
}
void OnTimer()
{
ULONG_PTR Cookie;
if (0 <= LdrLockLoaderLock(0, 0, &Cookie))
{
LdrUnlockLoaderLock(0, Cookie);
}
printf("%x: %s<%p>(%x)\n", GetCurrentThreadId(), __FUNCTION__, this, _M_n);
if (!--_M_n)
{
Stop();
}
}
static VOID NTAPI WaitOrTimerCallback(PVOID This, BOOLEAN )
{
reinterpret_cast<DTimer*>(This)->OnTimer();
}
static void NTAPI timer_canceled_callback(PVOID This)
{
printf("%x: %s<%p>\n", GetCurrentThreadId(), __FUNCTION__, This);
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// Simulate a long cleanup here
Sleep(5000);
reinterpret_cast<DTimer*>(This)->Release();
}
DTimer(ULONG n) : _M_n(n)
{
printf("%x: %s<%p>\n", GetCurrentThreadId(), __FUNCTION__, this);
}
~DTimer()
{
printf("%x: %s<%p>\n", GetCurrentThreadId(), __FUNCTION__, this);
if (_M_hTimer)
{
timer_release(_M_hTimer);
}
PostThreadMessageW(_M_dwThreadId, WM_QUIT, 0, 0);
}
BOOL Start(_In_ DWORD DueTime, _In_ DWORD Period)
{
AddRef();
if (timer_create(&_M_hTimer, WaitOrTimerCallback, this, DueTime, Period))
{
return TRUE;
}
Release();
return FALSE;
}
void Stop()
{
if (!InterlockedExchangeNoFence(&_M_stopping, TRUE))
{
timer_cancel(_M_hTimer, timer_canceled_callback, this);
}
}
};
void t_test(ULONG n)
{
printf("%x: +++++++++++\n", GetCurrentThreadId());
if (DTimer* timer = new DTimer(n))
{
if (timer->Start(0, 1000))
{
Sleep(1000);
timer->Stop();
}
timer->Release();
}
MessageBox(0, 0, L"demo delay", MB_ICONWARNING);
printf("%x: -----------\n", GetCurrentThreadId());
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL timer_create(
_Outptr_ PHANDLE phTimer,
_In_ WAITORTIMERCALLBACK Callback,
_In_opt_ PVOID Parameter,
_In_ DWORD DueTime,
_In_ DWORD Period
)
{
return CreateTimerQueueTimer(phTimer, 0, Callback, Parameter, DueTime, Period, 0);
}
void timer_release(HANDLE /*hTimer*/)
{
}
BOOL timer_cancel(HANDLE hTimer, void (NTAPI *timer_canceled_callback)(_In_opt_ PVOID Parameter), _In_opt_ PVOID Parameter )
{
if (HANDLE hEvent = CreateEvent(0, 0, 0, 0))
{
ULONG_PTR Cookie;
if (0 <= LdrLockLoaderLock(0, 0, &Cookie))
{
Sleep(1500);
ULONG dwError = DeleteTimerQueueTimer(0, hTimer, hEvent) ? NOERROR : GetLastError();
LdrUnlockLoaderLock(0, Cookie);
printf("DeleteTimerQueueTimer = %u\r\n", dwError);
switch (dwError)
{
case NOERROR:
case ERROR_IO_PENDING:
break;
default:
__debugbreak();
}
}
struct WO
{
HANDLE hEvent;
HANDLE hWaitObj = 0;
void (NTAPI *timer_canceled_callback)(PVOID Parameter);
PVOID Parameter;
WO(HANDLE hEvent, void (NTAPI *timer_canceled_callback)(PVOID Parameter), PVOID Parameter) : hEvent(hEvent),
timer_canceled_callback(timer_canceled_callback), Parameter(Parameter)
{
}
void OnWait()
{
printf("%x: %s<%p>\n", GetCurrentThreadId(), __FUNCTION__, this);
ULONG dwError = UnregisterWaitEx(hWaitObj, 0) ? NOERROR : GetLastError();
printf("UnregisterWaitEx = %u\r\n", dwError);
switch (dwError)
{
case NOERROR:
case ERROR_IO_PENDING:
break;
default:
__debugbreak();
}
CloseHandle(hEvent);
timer_canceled_callback(Parameter);
delete this;
}
static VOID NTAPI WaitOrTimerCallback(PVOID This, BOOLEAN TimerOrWaitFired )
{
if (TimerOrWaitFired)
{
__debugbreak();
}
reinterpret_cast<WO*>(This)->OnWait();
}
};
if (WO* pwo = new WO(hEvent, timer_canceled_callback, Parameter))
{
if (RegisterWaitForSingleObject(&pwo->hWaitObj, hEvent,
WO::WaitOrTimerCallback, pwo, INFINITE, /*WT_EXECUTEINWAITTHREAD|*/WT_EXECUTEONLYONCE))
{
return TRUE;
}
delete pwo;
}
CloseHandle(hEvent);
}
return FALSE;
}
t_test(3);
a8: +++++++++++
a8: NT::DTimer::DTimer<000001F02DE2E390>
22e8: NT::DTimer::OnTimer<000001F02DE2E390>(3)
DeleteTimerQueueTimer = 997
22e8: NT::DTimer::OnTimer<000001F02DE2E390>(2)
a38: NT::timer_cancel::WO::OnWait<000001F02DE1B9D0>
UnregisterWaitEx = 997
a38: NT::DTimer::timer_canceled_callback<000001F02DE2E390>
a38: NT::DTimer::~DTimer<000001F02DE2E390>
a8: -----------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment