304 lines
9.6 KiB
C++
304 lines
9.6 KiB
C++
#if OS_WINDOWS
|
|
constexpr s64 FILETIME_TO_UNIX = 116444736000000000i64;
|
|
f64 GetUnixTimestamp() {
|
|
FILETIME fileTime;
|
|
GetSystemTimePreciseAsFileTime(&fileTime);
|
|
s64 ticks = ((s64)fileTime.dwHighDateTime << (s64)32) | (s64)fileTime.dwLowDateTime;
|
|
return (ticks - FILETIME_TO_UNIX) / (10.0 * 1000.0 * 1000.0);
|
|
}
|
|
s64 GetUnixTimestampNanoseconds() {
|
|
FILETIME fileTime;
|
|
GetSystemTimePreciseAsFileTime(&fileTime);
|
|
|
|
s64 ticks = ((s64)fileTime.dwHighDateTime << (s64)32)
|
|
| (s64)fileTime.dwLowDateTime; // in 100ns ticks
|
|
s64 unix_time = (ticks - FILETIME_TO_UNIX); // in 100ns ticks
|
|
|
|
s64 unix_time_nanoseconds = unix_time * 100;
|
|
|
|
return unix_time_nanoseconds;
|
|
}
|
|
#endif
|
|
|
|
struct OS_System_Info {
|
|
s32 logical_processor_count;
|
|
s32 physical_core_count;
|
|
s32 primary_core_count;
|
|
s32 secondary_core_count; // Any weaker or "Efficiency" cores.
|
|
u64 page_size;
|
|
u64 large_page_size;
|
|
u64 allocation_granularity;
|
|
string machine_name;
|
|
};
|
|
|
|
struct OS_Process_Info {
|
|
u32 process_id;
|
|
b32 large_pages_allowed;
|
|
string binary_path;
|
|
string working_path;
|
|
string user_program_data_path;
|
|
Array<string> module_load_paths;
|
|
Array<string> environment_paths;
|
|
};
|
|
|
|
struct OS_State_Win32 {
|
|
Arena* arena;
|
|
|
|
OS_System_Info system_info;
|
|
OS_Process_Info process_info;
|
|
};
|
|
|
|
global OS_State_Win32 os_state_w32;
|
|
internal b32 win32_g_is_quiet = 0; // No console output
|
|
|
|
internal LONG WINAPI Win32_Exception_Filter (EXCEPTION_POINTERS* exception_ptrs) {
|
|
if (win32_g_is_quiet) { ExitProcess(1); }
|
|
|
|
static volatile LONG first = 0;
|
|
if(InterlockedCompareExchange(&first, 1, 0) != 0)
|
|
{ // prevent failures in other threads to popup same message box
|
|
// this handler just shows first thread that crashes
|
|
// we are terminating afterwards anyway
|
|
for (;;) Sleep(1000);
|
|
}
|
|
|
|
// #TODO: Exception handling code.
|
|
|
|
return 0;
|
|
}
|
|
|
|
// internal void Main_Entry_Point (int argc, WCHAR **argv);
|
|
internal void Win32_Entry_Point (int argc, WCHAR **argv) {
|
|
// See: w32_entry_point_caller(); (raddebugger)
|
|
SetUnhandledExceptionFilter(&Win32_Exception_Filter);
|
|
|
|
SYSTEM_INFO sysinfo = {0};
|
|
GetSystemInfo(&sysinfo);
|
|
|
|
// Try to allow large pages if we can.
|
|
// b32 large_pages_allowed = 0;
|
|
// {
|
|
// HANDLE token;
|
|
// if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
|
|
// {
|
|
// LUID luid;
|
|
// if(LookupPrivilegeValue(0, SE_LOCK_MEMORY_NAME, &luid))
|
|
// {
|
|
// TOKEN_PRIVILEGES priv;
|
|
// priv.PrivilegeCount = 1;
|
|
// priv.Privileges[0].Luid = luid;
|
|
// priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
// large_pages_allowed = !!AdjustTokenPrivileges(token, 0, &priv, sizeof(priv), 0, 0);
|
|
// }
|
|
// CloseHandle(token);
|
|
// }
|
|
// }
|
|
|
|
Bootstrap_Main_Thread_Context();
|
|
|
|
push_arena(get_thread_context()->arena);
|
|
|
|
// #TODO: Need to write Win32 abstraction layer first.
|
|
{ OS_System_Info* info = &os_state_w32.system_info;
|
|
info->logical_processor_count = (s32)sysinfo.dwNumberOfProcessors;
|
|
info->page_size = sysinfo.dwPageSize;
|
|
info->large_page_size = GetLargePageMinimum();
|
|
info->allocation_granularity = sysinfo.dwAllocationGranularity;
|
|
}
|
|
{ OS_Process_Info* info = &os_state_w32.process_info;
|
|
info->large_pages_allowed = false;
|
|
info->process_id = GetCurrentProcessId();
|
|
}
|
|
/*{ OS_System_Info* info = &os_state_w32.system_info;
|
|
// [ ] Extract input args
|
|
u32 length;
|
|
GetLogicalProcessorInformationEx(RelationProcessorCore, nullptr, (PDWORD)&length);
|
|
u8* cpu_information_buffer = NewArray<u8>(length);
|
|
GetLogicalProcessorInformationEx(RelationProcessorCore, // *sigh*
|
|
(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)cpu_information_buffer, (PDWORD)&length);
|
|
|
|
u32 offset = 0;
|
|
|
|
u32 all_cpus_count = 0;
|
|
u32 max_performance = 0;
|
|
u32 performance_core_count = 0;
|
|
// u32 efficient_core_count;
|
|
|
|
while (offset < length) {
|
|
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* cpu_information
|
|
= (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(cpu_information_buffer + offset);
|
|
|
|
offset += cpu_information->Size;
|
|
|
|
u32 count_per_group_physical = 1;
|
|
u32 value = (u32)cpu_information->Processor.GroupMask->Mask;
|
|
u32 count_per_group = __popcnt(value); // logical
|
|
if (cpu_information->Relationship != RelationProcessorCore) continue;
|
|
|
|
if (cpu_information->Processor.EfficiencyClass > max_performance) {
|
|
max_performance = cpu_information->Processor.EfficiencyClass;
|
|
performance_core_count = count_per_group_physical;
|
|
} else if (cpu_information->Processor.EfficiencyClass == max_performance) {
|
|
performance_core_count += count_per_group_physical;
|
|
}
|
|
|
|
all_cpus_count += count_per_group;
|
|
}
|
|
|
|
info->physical_core_count = (s32)all_cpus_count;
|
|
info->primary_core_count = (s32)performance_core_count;
|
|
}
|
|
// info->secondary_core_count = #TODO;
|
|
*/
|
|
{ OS_System_Info* info = &os_state_w32.system_info;
|
|
u8 buffer[MAX_COMPUTERNAME_LENGTH + 1] = {0};
|
|
DWORD size = MAX_COMPUTERNAME_LENGTH + 1;
|
|
if(GetComputerNameA((char*)buffer, &size)) {
|
|
string machine_name_temp = string(size, buffer);
|
|
info->machine_name = copy_string(machine_name_temp);
|
|
}
|
|
}
|
|
|
|
{ OS_Process_Info* info = &os_state_w32.process_info;
|
|
DWORD length = GetCurrentDirectoryW(0, 0);
|
|
// This can be freed later when we call temp_reset();
|
|
u16* memory = NewArray<u16>(get_temp_allocator(), length + 1);
|
|
length = GetCurrentDirectoryW(length + 1, (WCHAR*)memory);
|
|
info->working_path = wide_to_utf8(memory, length);
|
|
Assert(is_valid(info->working_path));
|
|
}
|
|
|
|
// [ ] Get Working directory (info->working_path)
|
|
// [ ] GetEnvironmentStringsW
|
|
// temp_reset();
|
|
printf("Hello there!\n\n");
|
|
}
|
|
|
|
C_LINKAGE DWORD OS_Windows_Thread_Entry_Point (void* parameter) {
|
|
Thread* thread = (Thread*)parameter;
|
|
|
|
set_thread_context(thread->context);
|
|
|
|
DWORD result = (DWORD)thread->proc(thread);
|
|
|
|
return result;
|
|
}
|
|
|
|
// Individual Thread API
|
|
internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name="") {
|
|
Assert(thread != nullptr && proc != nullptr);
|
|
|
|
DWORD windows_thread_id = 0;
|
|
|
|
HANDLE windows_thread = CreateThread(nullptr, 0, OS_Windows_Thread_Entry_Point,
|
|
thread, CREATE_SUSPENDED, &windows_thread_id);
|
|
|
|
if (windows_thread == 0 || windows_thread == INVALID_HANDLE_VALUE) {
|
|
return false;
|
|
}
|
|
|
|
s64 this_thread_index = InterlockedIncrement(&next_thread_index);
|
|
|
|
ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16);
|
|
|
|
thread->context = New<Thread_Context>(get_allocator(arena_ex));
|
|
thread->context->temp = expandable_arena_new(Arena_Reserve::Size_2M, 16);
|
|
thread->context->arena = arena_ex;
|
|
thread->context->allocator = get_allocator(arena_ex);
|
|
thread->context->thread_idx = (s32)this_thread_index;
|
|
push_arena(arena_ex);
|
|
thread->context->thread_name = copy_string(thread_name);
|
|
|
|
thread->os_thread.windows_thread = windows_thread;
|
|
thread->os_thread.windows_thread_id = windows_thread_id;
|
|
|
|
thread->proc = proc;
|
|
thread->index = this_thread_index;
|
|
|
|
return true;
|
|
}
|
|
|
|
internal void thread_deinit (Thread* thread) {
|
|
if (thread->os_thread.windows_thread) {
|
|
CloseHandle(thread->os_thread.windows_thread);
|
|
}
|
|
thread->os_thread.windows_thread = nullptr;
|
|
|
|
// #TODO: Thread cleanup:
|
|
arena_delete(thread->context->temp);
|
|
arena_delete(thread->context->arena);
|
|
}
|
|
|
|
internal void thread_start (Thread* thread) {
|
|
ResumeThread(thread->os_thread.windows_thread);
|
|
}
|
|
|
|
internal bool thread_is_done (Thread* thread, s32 milliseconds=0) {
|
|
Assert(milliseconds >= -1);
|
|
|
|
DWORD result = WaitForSingleObject(thread->os_thread.windows_thread, (DWORD)milliseconds);
|
|
return result != WAIT_TIMEOUT;
|
|
}
|
|
|
|
// #thread_primitives
|
|
internal void mutex_init (Mutex* mutex) {
|
|
InitializeCriticalSection(&mutex->csection);
|
|
}
|
|
internal void mutex_destroy (Mutex* mutex) {
|
|
DeleteCriticalSection(&mutex->csection);
|
|
}
|
|
internal void lock (Mutex* mutex) {
|
|
EnterCriticalSection(&mutex->csection);
|
|
}
|
|
internal void unlock (Mutex* mutex) {
|
|
LeaveCriticalSection(&mutex->csection);
|
|
}
|
|
internal void semaphore_init (Semaphore* sem, s32 initial_value = 0) {
|
|
Assert(initial_value >= 0);
|
|
sem->event = CreateSemaphoreW(nullptr, initial_value, 0x7fffffff, nullptr);
|
|
}
|
|
internal void semaphore_destroy (Semaphore* sem) {
|
|
CloseHandle(sem->event);
|
|
}
|
|
internal void signal (Semaphore* sem) {
|
|
ReleaseSemaphore(sem->event, 1, nullptr);
|
|
}
|
|
|
|
enum class Wait_For_Result : s32 {
|
|
SUCCESS = 0,
|
|
TIMEOUT = 1,
|
|
ERROR = 2 // can't use ERROR because of Windows.h *sigh*
|
|
};
|
|
|
|
internal Wait_For_Result wait_for (Semaphore* sem, s32 milliseconds = -1) {
|
|
DWORD res = 0;
|
|
if (milliseconds < 0) {
|
|
res = WaitForSingleObject(sem->event, INFINITE);
|
|
} else {
|
|
res = WaitForSingleObject(sem->event, (u32)milliseconds);
|
|
}
|
|
|
|
Assert(res != WAIT_FAILED);
|
|
|
|
if (res == WAIT_OBJECT_0) return Wait_For_Result::SUCCESS;
|
|
if (res == WAIT_TIMEOUT) return Wait_For_Result::TIMEOUT;
|
|
|
|
return Wait_For_Result::ERROR;
|
|
}
|
|
|
|
internal void condition_variable_init (Condition_Variable* cv) {
|
|
InitializeConditionVariable(&cv->condition_variable);
|
|
}
|
|
internal void condition_variable_destroy (Condition_Variable* cv) {
|
|
// No action required.
|
|
}
|
|
internal void wait (Condition_Variable* cv, Mutex* mutex, s32 wait_time_ms = -1) {
|
|
SleepConditionVariableCS(&cv->condition_variable, &mutex->csection, (DWORD)wait_time_ms);
|
|
}
|
|
internal void wake (Condition_Variable* cv) {
|
|
WakeConditionVariable(&cv->condition_variable);
|
|
}
|
|
internal void wake_all (Condition_Variable* cv) {
|
|
WakeAllConditionVariable(&cv->condition_variable);
|
|
}
|