And started breaking apart the timer functions.
This commit is contained in:
parent
5537e823ef
commit
42623f92e9
144
test/testing.c
144
test/testing.c
|
@ -3,6 +3,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "timer.h"
|
||||||
#include "testing.h"
|
#include "testing.h"
|
||||||
#include "testingpriv.h"
|
#include "testingpriv.h"
|
||||||
|
|
||||||
|
@ -161,17 +162,17 @@ static void testsetRun(struct testset *set, int *anyFailed)
|
||||||
size_t i;
|
size_t i;
|
||||||
testingT *t;
|
testingT *t;
|
||||||
const char *status;
|
const char *status;
|
||||||
testingTimer *timer;
|
timerTime start, end;
|
||||||
char *timerstr;
|
char timerstr[timeDurationStringLen];
|
||||||
|
|
||||||
t = set->tests;
|
t = set->tests;
|
||||||
timer = testingNewTimer();
|
timer = testingNewTimer();
|
||||||
for (i = 0; i < set->len; i++) {
|
for (i = 0; i < set->len; i++) {
|
||||||
printf("=== RUN %s\n", t->name);
|
printf("=== RUN %s\n", t->name);
|
||||||
testingTimerStart(timer);
|
start = timerMonotonicNow();
|
||||||
if (setjmp(t->returnNowBuf) == 0)
|
if (setjmp(t->returnNowBuf) == 0)
|
||||||
(*(t->f))(t);
|
(*(t->f))(t);
|
||||||
testingTimerEnd(timer);
|
end = timerMonotonicNow();
|
||||||
t->returned = 1;
|
t->returned = 1;
|
||||||
runDefers(t);
|
runDefers(t);
|
||||||
status = "PASS";
|
status = "PASS";
|
||||||
|
@ -181,9 +182,8 @@ static void testsetRun(struct testset *set, int *anyFailed)
|
||||||
} else if (t->skipped)
|
} else if (t->skipped)
|
||||||
// note that failed overrides skipped
|
// note that failed overrides skipped
|
||||||
status = "SKIP";
|
status = "SKIP";
|
||||||
timerstr = testingNsecString(testingTimerNsec(timer));
|
timerDurationString(timerTimeSub(end, start), timerstr);
|
||||||
printf("--- %s: %s (%s)\n", status, t->name, timerstr);
|
printf("--- %s: %s (%s)\n", status, t->name, timerstr);
|
||||||
testingFreeNsecString(timerstr);
|
|
||||||
t++;
|
t++;
|
||||||
}
|
}
|
||||||
testingFreeTimer(timer);
|
testingFreeTimer(timer);
|
||||||
|
@ -277,135 +277,3 @@ void testingTDefer(testingT *t, void (*f)(testingT *t, void *data), void *data)
|
||||||
d->next = t->defers;
|
d->next = t->defers;
|
||||||
t->defers = d;
|
t->defers = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is based on the algorithm that Go uses for time.Duration.
|
|
||||||
// Of course, we're not expressing it the same way...
|
|
||||||
struct timerStringPart {
|
|
||||||
char suffix;
|
|
||||||
char suffix2;
|
|
||||||
int mode;
|
|
||||||
uint32_t maxOrMod;
|
|
||||||
int precision;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
modeMaxAndStop,
|
|
||||||
modeFracModContinue,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct timerStringPart parts[] = {
|
|
||||||
{ 'n', 's', modeMaxAndStop, 1000, 0 },
|
|
||||||
{ 'u', 's', modeMaxAndStop, 1000000, 3 },
|
|
||||||
{ 'm', 's', modeMaxAndStop, 1000000000, 6 },
|
|
||||||
{ 's', 0, modeFracModContinue, 60, 9 },
|
|
||||||
{ 'm', 0, modeFracModContinue, 60, 0 },
|
|
||||||
{ 'h', 0, modeFracModContinue, 60, 0 },
|
|
||||||
{ 0, 0, 0, 0, 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
static void fillFracPart(char *s, int precision, int *start, uint64_t *unsec)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int print;
|
|
||||||
uint64_t digit;
|
|
||||||
|
|
||||||
print = 0;
|
|
||||||
for (i = 0; i < precision; i++) {
|
|
||||||
digit = *unsec % 10;
|
|
||||||
print = print || (digit != 0);
|
|
||||||
if (print) {
|
|
||||||
s[*start - 1] = "0123456789"[digit];
|
|
||||||
(*start)--;
|
|
||||||
}
|
|
||||||
*unsec /= 10;
|
|
||||||
}
|
|
||||||
if (print) {
|
|
||||||
s[*start - 1] = '.';
|
|
||||||
(*start)--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fillIntPart(char *s, int *start, uint64_t unsec)
|
|
||||||
{
|
|
||||||
if (unsec == 0) {
|
|
||||||
s[*start - 1] = '0';
|
|
||||||
(*start)--;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while (unsec != 0) {
|
|
||||||
s[*start - 1] = "0123456789"[unsec % 10];
|
|
||||||
(*start)--;
|
|
||||||
unsec /= 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char *testingNsecString(int64_t nsec)
|
|
||||||
{
|
|
||||||
uint64_t unsec;
|
|
||||||
int neg;
|
|
||||||
char *s;
|
|
||||||
int start;
|
|
||||||
const struct timerStringPart *p;
|
|
||||||
|
|
||||||
// The Go algorithm says 32 should be enough.
|
|
||||||
s = testingprivNewArray(char, 33);
|
|
||||||
start = 32;
|
|
||||||
|
|
||||||
if (nsec == 0) {
|
|
||||||
s[0] = '0';
|
|
||||||
s[1] = 's';
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
unsec = (uint64_t) nsec;
|
|
||||||
neg = 0;
|
|
||||||
if (nsec < 0) {
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
// TODO figure out a more explicit way to do this; until then, just go with what the standard says should happen, because it's what we want (TODO verify this)
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable: 4146)
|
|
||||||
#endif
|
|
||||||
unsec = -unsec;
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
neg = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (p = parts; p->suffix != 0; p++) {
|
|
||||||
if (p->mode == modeMaxAndStop && unsec < p->maxOrMod) {
|
|
||||||
if (p->suffix2 != 0) {
|
|
||||||
s[start - 1] = p->suffix2;
|
|
||||||
start--;
|
|
||||||
}
|
|
||||||
s[start - 1] = p->suffix;
|
|
||||||
start--;
|
|
||||||
fillFracPart(s, p->precision, &start, &unsec);
|
|
||||||
fillIntPart(s, &start, unsec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (p->mode == modeFracModContinue && unsec != 0) {
|
|
||||||
if (p->suffix2 != 0) {
|
|
||||||
s[start - 1] = p->suffix2;
|
|
||||||
start--;
|
|
||||||
}
|
|
||||||
s[start - 1] = p->suffix;
|
|
||||||
start--;
|
|
||||||
fillFracPart(s, p->precision, &start, &unsec);
|
|
||||||
fillIntPart(s, &start, unsec % p->maxOrMod);
|
|
||||||
unsec /= p->maxOrMod;
|
|
||||||
// and move on to the next one
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (neg) {
|
|
||||||
s[start - 1] = '-';
|
|
||||||
start--;
|
|
||||||
}
|
|
||||||
memmove(s, s + start, 33 - start);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void testingFreeNsecString(char *s)
|
|
||||||
{
|
|
||||||
testingprivFree(s);
|
|
||||||
}
|
|
||||||
|
|
|
@ -172,48 +172,6 @@ static HRESULT WINAPI hrWaitForSingleObject(HANDLE handle, DWORD timeout)
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct testingTimer {
|
|
||||||
LARGE_INTEGER start;
|
|
||||||
LARGE_INTEGER end;
|
|
||||||
};
|
|
||||||
|
|
||||||
testingTimer *testingNewTimer(void)
|
|
||||||
{
|
|
||||||
return testingprivNew(testingTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testingFreeTimer(testingTimer *t)
|
|
||||||
{
|
|
||||||
testingprivFree(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testingTimerStart(testingTimer *t)
|
|
||||||
{
|
|
||||||
QueryPerformanceCounter(&(t->start));
|
|
||||||
}
|
|
||||||
|
|
||||||
void testingTimerEnd(testingTimer *t)
|
|
||||||
{
|
|
||||||
QueryPerformanceCounter(&(t->end));
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t testingTimerNsec(testingTimer *t)
|
|
||||||
{
|
|
||||||
LARGE_INTEGER qpf;
|
|
||||||
int64_t qpnsQuot, qpnsRem;
|
|
||||||
int64_t c;
|
|
||||||
int64_t ret;
|
|
||||||
|
|
||||||
QueryPerformanceFrequency(&qpf);
|
|
||||||
qpnsQuot = testingNsecPerSec / qpf.QuadPart;
|
|
||||||
qpnsRem = testingNsecPerSec % qpf.QuadPart;
|
|
||||||
c = t->end.QuadPart - t->start.QuadPart;
|
|
||||||
|
|
||||||
ret = c * qpnsQuot;
|
|
||||||
ret += (c * qpnsRem) / qpf.QuadPart;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: the idea for the SetThreadContext() nuttery is from https://www.codeproject.com/Articles/71529/Exception-Injection-Throwing-an-Exception-in-Other
|
// note: the idea for the SetThreadContext() nuttery is from https://www.codeproject.com/Articles/71529/Exception-Injection-Throwing-an-Exception-in-Other
|
||||||
|
|
||||||
static jmp_buf timeout_ret;
|
static jmp_buf timeout_ret;
|
||||||
|
@ -398,32 +356,6 @@ out:
|
||||||
testingTFailNow(t);
|
testingTFailNow(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testingSleep(int64_t nsec)
|
|
||||||
{
|
|
||||||
HANDLE timer;
|
|
||||||
LARGE_INTEGER duration;
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
duration.QuadPart = nsec / 100;
|
|
||||||
duration.QuadPart = -duration.QuadPart;
|
|
||||||
hr = hrCreateWaitableTimerW(NULL, TRUE, NULL, &timer);
|
|
||||||
if (hr != S_OK)
|
|
||||||
goto fallback;
|
|
||||||
hr = hrSetWaitableTimer(timer, &duration, 0, NULL, NULL, FALSE);
|
|
||||||
if (hr != S_OK) {
|
|
||||||
CloseHandle(timer);
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
hr = hrWaitForSingleObject(timer, INFINITE);
|
|
||||||
CloseHandle(timer);
|
|
||||||
if (hr == S_OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
fallback:
|
|
||||||
// this has lower resolution, but we can't detect a failure, so use it as a fallback
|
|
||||||
Sleep((DWORD) (nsec / testingNsecPerMsec));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct testingThread {
|
struct testingThread {
|
||||||
uintptr_t handle;
|
uintptr_t handle;
|
||||||
void (*f)(void *data);
|
void (*f)(void *data);
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
// 2 may 2019
|
||||||
|
#include <string.h>
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
// This is based on the algorithm that Go uses for time.Duration.
|
||||||
|
// Of course, we're not expressing it the same way...
|
||||||
|
struct timerStringPart {
|
||||||
|
char suffix;
|
||||||
|
char suffix2;
|
||||||
|
int mode;
|
||||||
|
uint32_t maxOrMod;
|
||||||
|
int precision;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
modeMaxAndStop,
|
||||||
|
modeFracModContinue,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct timerStringPart parts[] = {
|
||||||
|
{ 'n', 's', modeMaxAndStop, 1000, 0 },
|
||||||
|
{ 'u', 's', modeMaxAndStop, 1000000, 3 },
|
||||||
|
{ 'm', 's', modeMaxAndStop, 1000000000, 6 },
|
||||||
|
{ 's', 0, modeFracModContinue, 60, 9 },
|
||||||
|
{ 'm', 0, modeFracModContinue, 60, 0 },
|
||||||
|
{ 'h', 0, modeFracModContinue, 60, 0 },
|
||||||
|
{ 0, 0, 0, 0, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fillFracPart(char *buf, int precision, int start, uint64_t *unsec)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int print;
|
||||||
|
uint64_t digit;
|
||||||
|
|
||||||
|
print = 0;
|
||||||
|
for (i = 0; i < precision; i++) {
|
||||||
|
digit = *unsec % 10;
|
||||||
|
print = print || (digit != 0);
|
||||||
|
if (print) {
|
||||||
|
buf[start - 1] = "0123456789"[digit];
|
||||||
|
start--;
|
||||||
|
}
|
||||||
|
*unsec /= 10;
|
||||||
|
}
|
||||||
|
if (print) {
|
||||||
|
buf[start - 1] = '.';
|
||||||
|
start--;
|
||||||
|
}
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fillIntPart(char *buf, int start, uint64_t unsec)
|
||||||
|
{
|
||||||
|
if (unsec == 0) {
|
||||||
|
buf[start - 1] = '0';
|
||||||
|
start--;
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
while (unsec != 0) {
|
||||||
|
buf[start - 1] = "0123456789"[unsec % 10];
|
||||||
|
start--;
|
||||||
|
unsec /= 10;
|
||||||
|
}
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerDurationString(timerDuration d, char buf[timerDurationStringLen])
|
||||||
|
{
|
||||||
|
uint64_t unsec;
|
||||||
|
int neg;
|
||||||
|
int start;
|
||||||
|
const struct timerStringPart *p;
|
||||||
|
|
||||||
|
memset(buf, 0, timerTimeStringLen * sizeof (char));
|
||||||
|
start = 32;
|
||||||
|
|
||||||
|
if (d == 0) {
|
||||||
|
buf[0] = '0';
|
||||||
|
buf[1] = 's';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsec = (uint64_t) d;
|
||||||
|
neg = 0;
|
||||||
|
if (d < 0) {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
// TODO figure out a more explicit way to do this; until then, just go with what the standard says should happen, because it's what we want (TODO verify this)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4146)
|
||||||
|
#endif
|
||||||
|
unsec = -unsec;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
neg = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (p = parts; p->suffix != 0; p++) {
|
||||||
|
if (p->mode == modeMaxAndStop && unsec < p->maxOrMod) {
|
||||||
|
if (p->suffix2 != 0) {
|
||||||
|
buf[start - 1] = p->suffix2;
|
||||||
|
start--;
|
||||||
|
}
|
||||||
|
buf[start - 1] = p->suffix;
|
||||||
|
start--;
|
||||||
|
start = fillFracPart(buf, p->precision, start, &unsec);
|
||||||
|
start = fillIntPart(buf, start, unsec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (p->mode == modeFracModContinue && unsec != 0) {
|
||||||
|
if (p->suffix2 != 0) {
|
||||||
|
buf[start - 1] = p->suffix2;
|
||||||
|
start--;
|
||||||
|
}
|
||||||
|
buf[start - 1] = p->suffix;
|
||||||
|
start--;
|
||||||
|
start = fillFracPart(buf, p->precision, start, &unsec);
|
||||||
|
start = fillIntPart(buf, start, unsec % p->maxOrMod);
|
||||||
|
unsec /= p->maxOrMod;
|
||||||
|
// and move on to the next one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (neg) {
|
||||||
|
buf[start - 1] = '-';
|
||||||
|
start--;
|
||||||
|
}
|
||||||
|
memmove(buf, buf + start, 33 - start);
|
||||||
|
}
|
|
@ -11,13 +11,12 @@ typedef int64_t timerTime;
|
||||||
#define timerSecond ((Duration) 1000000000)
|
#define timerSecond ((Duration) 1000000000)
|
||||||
|
|
||||||
extern timerTime timerMonotonicNow(void);
|
extern timerTime timerMonotonicNow(void);
|
||||||
extern timerDuration timerTimeSub(timerTime start, timerTime end);
|
extern timerDuration timerTimeSub(timerTime end, timerTime start);
|
||||||
|
|
||||||
// The Go algorithm says 32 should be enough.
|
// The Go algorithm says 32 should be enough.
|
||||||
// We use 33 to count the terminating NUL.
|
// We use 33 to count the terminating NUL.
|
||||||
#define timerTimeStringLen 33
|
#define timerDurationStringLen 33
|
||||||
|
extern void timerDurationString(timerDuration d, char buf[timerDurationStringLen]);
|
||||||
extern void timerDurationString(timerDuration d, char buf[timerTimeStringLen]);
|
|
||||||
|
|
||||||
typedef uint64_t timerSysError;
|
typedef uint64_t timerSysError;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
// 23 april 2019
|
||||||
|
#define UNICODE
|
||||||
|
#define _UNICODE
|
||||||
|
#define STRICT
|
||||||
|
#define STRICT_TYPED_ITEMIDS
|
||||||
|
#define WINVER 0x0600
|
||||||
|
#define _WIN32_WINNT 0x0600
|
||||||
|
#define _WIN32_WINDOWS 0x0600
|
||||||
|
#define _WIN32_IE 0x0700
|
||||||
|
#define NTDDI_VERSION 0x06000000
|
||||||
|
#include <windows.h>
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
static HRESULT lastErrorCodeToHRESULT(DWORD lastError)
|
||||||
|
{
|
||||||
|
if (lastError == 0)
|
||||||
|
return E_FAIL;
|
||||||
|
return HRESULT_FROM_WIN32(lastError);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT lastErrorToHRESULT(void)
|
||||||
|
{
|
||||||
|
return lastErrorCodeToHRESULT(GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI hrWaitForMultipleObjectsEx(DWORD n, const HANDLE *objects, BOOL waitAll, DWORD timeout, BOOL alertable, DWORD *result)
|
||||||
|
{
|
||||||
|
SetLastError(0);
|
||||||
|
*result = WaitForMultipleObjectsEx(n, objects, waitAll, timeout, alertable);
|
||||||
|
if (*result == WAIT_FAILED)
|
||||||
|
return lastErrorToHRESULT();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI hrCreateWaitableTimerW(LPSECURITY_ATTRIBUTES attributes, BOOL manualReset, LPCWSTR name, HANDLE *handle)
|
||||||
|
{
|
||||||
|
SetLastError(0);
|
||||||
|
*handle = CreateWaitableTimerW(attributes, manualReset, name);
|
||||||
|
if (*handle == NULL)
|
||||||
|
return lastErrorToHRESULT();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI hrSetWaitableTimer(HANDLE timer, const LARGE_INTEGER *duration, LONG period, PTIMERAPCROUTINE completionRoutine, LPVOID completionData, BOOL resume)
|
||||||
|
{
|
||||||
|
BOOL ret;
|
||||||
|
|
||||||
|
SetLastError(0);
|
||||||
|
ret = SetWaitableTimer(timer, duration, period, completionRoutine, completionData, resume);
|
||||||
|
if (ret == 0)
|
||||||
|
return lastErrorToHRESULT();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI hrWaitForSingleObject(HANDLE handle, DWORD timeout)
|
||||||
|
{
|
||||||
|
DWORD ret;
|
||||||
|
|
||||||
|
SetLastError(0);
|
||||||
|
ret = WaitForSingleObject(handle, timeout);
|
||||||
|
if (ret == WAIT_FAILED)
|
||||||
|
return lastErrorToHRESULT();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
timerTime timerMonotonicNow(void)
|
||||||
|
{
|
||||||
|
LARGE_INTEGER qpc;
|
||||||
|
|
||||||
|
QueryPerformanceCounter(&qpc);
|
||||||
|
return qpc.QuadPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
timerDuration timerTimeSub(timerTime end, timerTime start)
|
||||||
|
{
|
||||||
|
LARGE_INTEGER qpf;
|
||||||
|
timerDuration qpnsQuot, qpnsRem;
|
||||||
|
timerTime c;
|
||||||
|
timerDuration ret;
|
||||||
|
|
||||||
|
QueryPerformanceFrequency(&qpf);
|
||||||
|
qpnsQuot = timerSecond / qpf.QuadPart;
|
||||||
|
qpnsRem = timerSecond % qpf.QuadPart;
|
||||||
|
c = end - start;
|
||||||
|
|
||||||
|
ret = ((timerDuration) c) * qpnsQuot;
|
||||||
|
ret += (c * qpnsRem) / qpf.QuadPart;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
timerSysError timerSleep(timerDuration nsec)
|
||||||
|
{
|
||||||
|
HANDLE timer;
|
||||||
|
LARGE_INTEGER duration;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
duration.QuadPart = nsec / 100;
|
||||||
|
duration.QuadPart = -duration.QuadPart;
|
||||||
|
hr = hrCreateWaitableTimerW(NULL, TRUE, NULL, &timer);
|
||||||
|
if (hr != S_OK)
|
||||||
|
return (timerSysError) hr;
|
||||||
|
hr = hrSetWaitableTimer(timer, &duration, 0, NULL, NULL, FALSE);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
CloseHandle(timer);
|
||||||
|
return (timerSysError) hr;
|
||||||
|
}
|
||||||
|
hr = hrWaitForSingleObject(timer, INFINITE);
|
||||||
|
CloseHandle(timer);
|
||||||
|
return (timerSysError) hr;
|
||||||
|
}
|
Loading…
Reference in New Issue