mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-10-28 21:25:06 +00:00
198 lines
6.6 KiB
C
198 lines
6.6 KiB
C
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
|
|
#define CHECK(code,condition) do { if(!(condition)) return code; } while(0)
|
|
|
|
static CRITICAL_SECTION section;
|
|
static volatile LONG counter;
|
|
static HANDLE gate_event;
|
|
static DWORD tls_slot;
|
|
static SRWLOCK lock;
|
|
static CONDITION_VARIABLE ready_variable;
|
|
static volatile LONG ready_flag;
|
|
static volatile LONG apc_value;
|
|
static HANDLE mutex;
|
|
static volatile LONG order[8];
|
|
static volatile LONG order_count;
|
|
|
|
static DWORD WINAPI counting(LPVOID parameter)
|
|
{
|
|
// Sleep(0) forces the scheduler to interleave the threads inside the critical section's neighbourhood.
|
|
for(int i=0;i<100;++i) {
|
|
EnterCriticalSection(§ion);
|
|
LONG seen=counter;
|
|
Sleep(0);
|
|
counter=seen+1;
|
|
LeaveCriticalSection(§ion);
|
|
Sleep(0);
|
|
}
|
|
return (DWORD)(ULONG_PTR)parameter;
|
|
}
|
|
|
|
static DWORD WINAPI waiting(LPVOID parameter)
|
|
{
|
|
(void)parameter;
|
|
if(WaitForSingleObject(gate_event,5000)!=WAIT_OBJECT_0) return 1;
|
|
return 42;
|
|
}
|
|
|
|
static DWORD WINAPI tls_reader(LPVOID parameter)
|
|
{
|
|
(void)parameter;
|
|
if(TlsGetValue(tls_slot)!=0) return 1;
|
|
TlsSetValue(tls_slot,(LPVOID)(ULONG_PTR)77);
|
|
Sleep(10);
|
|
return TlsGetValue(tls_slot)==(LPVOID)(ULONG_PTR)77 ? 0 : 2;
|
|
}
|
|
|
|
static DWORD WINAPI producer(LPVOID parameter)
|
|
{
|
|
(void)parameter;
|
|
Sleep(20);
|
|
AcquireSRWLockExclusive(&lock);
|
|
ready_flag=1;
|
|
ReleaseSRWLockExclusive(&lock);
|
|
WakeAllConditionVariable(&ready_variable);
|
|
return 0;
|
|
}
|
|
|
|
static DWORD WINAPI ordered(LPVOID parameter)
|
|
{
|
|
Sleep((DWORD)(ULONG_PTR)parameter);
|
|
order[order_count++]=(LONG)(ULONG_PTR)parameter;
|
|
return 0;
|
|
}
|
|
|
|
static VOID CALLBACK apc(ULONG_PTR parameter)
|
|
{
|
|
apc_value=(LONG)parameter;
|
|
}
|
|
|
|
static DWORD WINAPI mutex_owner(LPVOID parameter)
|
|
{
|
|
(void)parameter;
|
|
if(WaitForSingleObject(mutex,0)!=WAIT_TIMEOUT) return 1;
|
|
if(ReleaseMutex(mutex)) return 2;
|
|
return GetLastError()==ERROR_NOT_OWNER ? 0 : 3;
|
|
}
|
|
|
|
static int critical_options(void)
|
|
{
|
|
ULONG_PTR peb;
|
|
#ifdef __x86_64__
|
|
__asm__ volatile("movq %%gs:0x60,%0" : "=r"(peb));
|
|
const BYTE *version = (const BYTE *)(peb + 0x118);
|
|
#else
|
|
__asm__ volatile("movl %%fs:0x30,%0" : "=r"(peb));
|
|
const BYTE *version = (const BYTE *)(peb + 0xa4);
|
|
#endif
|
|
if (*(const DWORD *)version != 10 || *(const DWORD *)(version + 4) ||
|
|
*(const WORD *)(version + 8) != 19045 || *(const WORD *)(version + 10) ||
|
|
*(const DWORD *)(version + 12) != VER_PLATFORM_WIN32_NT) return 85;
|
|
CRITICAL_SECTION option;
|
|
const DWORD flags[] = {1, 0x00ffffff, 0x01000001, 0x10000000};
|
|
for (unsigned index = 0; index < sizeof(flags) / sizeof(flags[0]); ++index) {
|
|
for (unsigned at = 0; at < sizeof(option); ++at) ((BYTE *)&option)[at] = 0xa5;
|
|
if (!InitializeCriticalSectionEx(&option, 0, flags[index])) return 80;
|
|
if ((flags[index] & 0x01000000) && option.DebugInfo && (ULONG_PTR)option.DebugInfo != (ULONG_PTR)-1) return 81;
|
|
EnterCriticalSection(&option);
|
|
if (!TryEnterCriticalSection(&option)) return 82;
|
|
LeaveCriticalSection(&option);
|
|
LeaveCriticalSection(&option);
|
|
DeleteCriticalSection(&option);
|
|
}
|
|
for (unsigned at = 0; at < sizeof(option); ++at) ((BYTE *)&option)[at] = 0xa5;
|
|
if (InitializeCriticalSectionEx(&option, 0, 0x20000001) || GetLastError() != ERROR_INVALID_PARAMETER) return 83;
|
|
for (unsigned at = 0; at < sizeof(option); ++at) if (((BYTE *)&option)[at] != 0xa5) return 84;
|
|
return 0;
|
|
}
|
|
|
|
int raven_entry(void)
|
|
{
|
|
const int options = critical_options();
|
|
if (options) return options;
|
|
CHECK(79,InitializeCriticalSectionEx(§ion,0,1));
|
|
HANDLE workers[4];
|
|
for(int i=0;i<4;++i) {
|
|
workers[i]=CreateThread(0,0,counting,(LPVOID)(ULONG_PTR)(i+1),0,0);
|
|
CHECK(1,workers[i]!=0);
|
|
}
|
|
CHECK(2,WaitForMultipleObjects(4,workers,TRUE,5000)==WAIT_OBJECT_0);
|
|
CHECK(3,counter==400);
|
|
for(int i=0;i<4;++i) {
|
|
DWORD code=0;
|
|
CHECK(4,GetExitCodeThread(workers[i],&code) && code==(DWORD)(i+1));
|
|
CloseHandle(workers[i]);
|
|
}
|
|
|
|
gate_event=CreateEventW(0,TRUE,FALSE,0);
|
|
HANDLE waiter=CreateThread(0,0,waiting,0,0,0);
|
|
CHECK(5,waiter!=0);
|
|
DWORD code=0;
|
|
CHECK(6,WaitForSingleObject(waiter,30)==WAIT_TIMEOUT);
|
|
CHECK(7,GetExitCodeThread(waiter,&code) && code==STILL_ACTIVE);
|
|
SetEvent(gate_event);
|
|
CHECK(8,WaitForSingleObject(waiter,5000)==WAIT_OBJECT_0);
|
|
CHECK(9,GetExitCodeThread(waiter,&code) && code==42);
|
|
CloseHandle(waiter);
|
|
|
|
tls_slot=TlsAlloc();
|
|
CHECK(10,tls_slot!=TLS_OUT_OF_INDEXES);
|
|
CHECK(11,TlsSetValue(tls_slot,(LPVOID)(ULONG_PTR)5));
|
|
HANDLE reader=CreateThread(0,0,tls_reader,0,0,0);
|
|
CHECK(12,WaitForSingleObject(reader,5000)==WAIT_OBJECT_0);
|
|
CHECK(13,GetExitCodeThread(reader,&code) && code==0);
|
|
CHECK(14,TlsGetValue(tls_slot)==(LPVOID)(ULONG_PTR)5);
|
|
CloseHandle(reader);
|
|
TlsFree(tls_slot);
|
|
|
|
InitializeSRWLock(&lock);
|
|
InitializeConditionVariable(&ready_variable);
|
|
HANDLE make=CreateThread(0,0,producer,0,0,0);
|
|
AcquireSRWLockExclusive(&lock);
|
|
while(!ready_flag)
|
|
CHECK(15,SleepConditionVariableSRW(&ready_variable,&lock,5000,0));
|
|
ReleaseSRWLockExclusive(&lock);
|
|
CHECK(16,ready_flag==1);
|
|
CHECK(17,WaitForSingleObject(make,5000)==WAIT_OBJECT_0);
|
|
CloseHandle(make);
|
|
AcquireSRWLockExclusive(&lock);
|
|
CHECK(18,!SleepConditionVariableSRW(&ready_variable,&lock,15,0) && GetLastError()==ERROR_TIMEOUT);
|
|
ReleaseSRWLockExclusive(&lock);
|
|
|
|
// Sleeping threads wake in time order, not creation order.
|
|
HANDLE sleepers[3];
|
|
DWORD delays[3]={600,100,300};
|
|
for(int i=0;i<3;++i) sleepers[i]=CreateThread(0,0,ordered,(LPVOID)(ULONG_PTR)delays[i],0,0);
|
|
CHECK(19,WaitForMultipleObjects(3,sleepers,TRUE,5000)==WAIT_OBJECT_0);
|
|
CHECK(20,order[0]==100 && order[1]==300 && order[2]==600);
|
|
for(int i=0;i<3;++i) CloseHandle(sleepers[i]);
|
|
|
|
mutex=CreateMutexW(0,TRUE,0);
|
|
CHECK(21,mutex!=0);
|
|
HANDLE other=CreateThread(0,0,mutex_owner,0,0,0);
|
|
CHECK(22,WaitForSingleObject(other,5000)==WAIT_OBJECT_0);
|
|
CHECK(23,GetExitCodeThread(other,&code) && code==0);
|
|
CHECK(24,WaitForSingleObject(mutex,0)==WAIT_OBJECT_0);
|
|
CHECK(25,ReleaseMutex(mutex) && ReleaseMutex(mutex));
|
|
CHECK(26,!ReleaseMutex(mutex));
|
|
CloseHandle(other);
|
|
|
|
HANDLE semaphore=CreateSemaphoreW(0,2,3,0);
|
|
CHECK(27,WaitForSingleObject(semaphore,0)==WAIT_OBJECT_0 && WaitForSingleObject(semaphore,0)==WAIT_OBJECT_0);
|
|
CHECK(28,WaitForSingleObject(semaphore,10)==WAIT_TIMEOUT);
|
|
LONG previous=-1;
|
|
CHECK(29,ReleaseSemaphore(semaphore,3,&previous) && previous==0);
|
|
CHECK(30,!ReleaseSemaphore(semaphore,1,0) && GetLastError()==ERROR_TOO_MANY_POSTS);
|
|
|
|
CHECK(31,QueueUserAPC(apc,GetCurrentThread(),123));
|
|
CHECK(32,SleepEx(1000,TRUE)==WAIT_IO_COMPLETION && apc_value==123);
|
|
|
|
// A named mutex is the same object for every opener.
|
|
HANDLE first=CreateMutexW(0,FALSE,L"raven-named-mutex");
|
|
HANDLE second=CreateMutexW(0,FALSE,L"raven-named-mutex");
|
|
CHECK(33,first && second && GetLastError()==ERROR_ALREADY_EXISTS);
|
|
CHECK(34,GetCurrentThreadId()!=0);
|
|
return 0;
|
|
}
|