144 lines
3.4 KiB
C
144 lines
3.4 KiB
C
// 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 <process.h>
|
|
#include <stdlib.h>
|
|
#include "thread.h"
|
|
|
|
// Do not put any test cases in this file; they will not be run.
|
|
|
|
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 __cdecl hr_beginthreadex(void *security, unsigned stackSize, unsigned (__stdcall *threadProc)(void *arg), void *threadProcArg, unsigned flags, unsigned *thirdArg, uintptr_t *handle)
|
|
{
|
|
DWORD lastError;
|
|
|
|
// _doserrno is the equivalent of GetLastError(), or at least that's how _beginthreadex() uses it.
|
|
_doserrno = 0;
|
|
*handle = _beginthreadex(security, stackSize, threadProc, threadProcArg, flags, thirdArg);
|
|
if (*handle == 0) {
|
|
lastError = (DWORD) _doserrno;
|
|
return lastErrorCodeToHRESULT(lastError);
|
|
}
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
struct threadThread {
|
|
uintptr_t handle;
|
|
void (*f)(void *data);
|
|
void *data;
|
|
};
|
|
|
|
static unsigned __stdcall threadThreadProc(void *data)
|
|
{
|
|
threadThread *t = (threadThread *) data;
|
|
|
|
(*(t->f))(t->data);
|
|
return 0;
|
|
}
|
|
|
|
threadSysError threadNewThread(void (*f)(void *data), void *data, threadThread **t)
|
|
{
|
|
threadThread *tout;
|
|
HRESULT hr;
|
|
|
|
*t = NULL;
|
|
|
|
tout = (threadThread *) malloc(sizeof (threadThread));
|
|
if (tout == NULL)
|
|
return (threadSysError) E_OUTOFMEMORY;
|
|
tout->f = f;
|
|
tout->data = data;
|
|
|
|
hr = hr_beginthreadex(NULL, 0, threadThreadProc, tout, 0, NULL, &(tout->handle));
|
|
if (hr != S_OK) {
|
|
free(tout);
|
|
return (threadSysError) hr;
|
|
}
|
|
|
|
*t = tout;
|
|
return 0;
|
|
}
|
|
|
|
threadSysError threadThreadWaitAndFree(threadThread *t)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = hrWaitForSingleObject((HANDLE) (t->handle), INFINITE);
|
|
if (hr != S_OK)
|
|
return (threadSysError) hr;
|
|
CloseHandle((HANDLE) (t->handle));
|
|
free(t);
|
|
return 0;
|
|
}
|
|
|
|
threadSysError threadSleep(threadDuration d)
|
|
{
|
|
HANDLE timer;
|
|
LARGE_INTEGER duration;
|
|
HRESULT hr;
|
|
|
|
duration.QuadPart = d / 100;
|
|
duration.QuadPart = -duration.QuadPart;
|
|
hr = hrCreateWaitableTimerW(NULL, TRUE, NULL, &timer);
|
|
if (hr != S_OK)
|
|
return (threadSysError) hr;
|
|
hr = hrSetWaitableTimer(timer, &duration, 0, NULL, NULL, FALSE);
|
|
if (hr != S_OK) {
|
|
CloseHandle(timer);
|
|
return (threadSysError) hr;
|
|
}
|
|
hr = hrWaitForSingleObject(timer, INFINITE);
|
|
CloseHandle(timer);
|
|
return (threadSysError) hr;
|
|
}
|