2024-04-18 12:59:11 +00:00
# pragma once
# include "dqn.h"
2024-08-01 03:34:36 +00:00
/*
2024-01-31 12:49:23 +00:00
////////////////////////////////////////////////////////////////////////////////////////////////////
//
/ / $ $ $ $ $ $ \ $ $ $ $ $ $ \ $ $ \ $ $ \ $ $ $ $ $ $ \ $ $ \ $ $ \ $ $ $ $ $ $ \ $ $ $ $ $ $ \
/ / $ $ __ $ $ \ $ $ __ $ $ \ $ $ | $ \ $ $ | \ _ $ $ _ | $ $ $ \ $ $ | $ $ ___ $ $ \ $ $ __ $ $ \
// $$ / $$ |$$ / \__| $$ |$$$\ $$ | $$ | $$$$\ $$ |\_/ $$ |\__/ $$ |
// $$ | $$ |\$$$$$$\ $$ $$ $$\$$ | $$ | $$ $$\$$ | $$$$$ / $$$$$$ |
// $$ | $$ | \____$$\ $$$$ _$$$$ | $$ | $$ \$$$$ | \___$$\ $$ ____/
// $$ | $$ |$$\ $$ | $$$ / \$$$ | $$ | $$ |\$$$ |$$\ $$ |$$ |
/ / $ $ $ $ $ $ | \ $ $ $ $ $ $ | $ $ / \ $ $ | $ $ $ $ $ $ \ $ $ | \ $ $ | \ $ $ $ $ $ $ | $ $ $ $ $ $ $ $ \
// \______/ \______/ \__/ \__|\______|\__| \__| \______/ \________|
//
// dqn_os_win32.cpp
//
////////////////////////////////////////////////////////////////////////////////////////////////////
2024-08-01 03:34:36 +00:00
*/
2024-01-31 12:49:23 +00:00
2024-08-01 03:34:36 +00:00
// NOTE: [$VMEM] Dqn_OSMem /////////////////////////////////////////////////////////////////////////
2024-01-31 12:49:23 +00:00
static uint32_t Dqn_OS_MemConvertPageToOSFlags_ ( uint32_t protect )
{
DQN_ASSERT ( ( protect & ~ Dqn_OSMemPage_All ) = = 0 ) ;
DQN_ASSERT ( protect ! = 0 ) ;
uint32_t result = 0 ;
if ( protect & Dqn_OSMemPage_NoAccess ) {
result = PAGE_NOACCESS ;
} else {
if ( protect & Dqn_OSMemPage_ReadWrite ) {
result = PAGE_READWRITE ;
} else if ( protect & Dqn_OSMemPage_Read ) {
result = PAGE_READONLY ;
} else if ( protect & Dqn_OSMemPage_Write ) {
Dqn_Log_WarningF ( " Windows does not support write-only pages, granting read+write access " ) ;
result = PAGE_READWRITE ;
}
}
if ( protect & Dqn_OSMemPage_Guard )
result | = PAGE_GUARD ;
DQN_ASSERTF ( result ! = PAGE_GUARD , " Page guard is a modifier, you must also specify a page permission like read or/and write " ) ;
return result ;
}
DQN_API void * Dqn_OS_MemReserve ( Dqn_usize size , Dqn_OSMemCommit commit , uint32_t page_flags )
{
unsigned long os_page_flags = Dqn_OS_MemConvertPageToOSFlags_ ( page_flags ) ;
unsigned long flags = MEM_RESERVE | ( commit = = Dqn_OSMemCommit_Yes ? MEM_COMMIT : 0 ) ;
void * result = VirtualAlloc ( nullptr , size , flags , os_page_flags ) ;
return result ;
}
DQN_API bool Dqn_OS_MemCommit ( void * ptr , Dqn_usize size , uint32_t page_flags )
{
bool result = false ;
if ( ! ptr | | size = = 0 )
return false ;
unsigned long os_page_flags = Dqn_OS_MemConvertPageToOSFlags_ ( page_flags ) ;
result = VirtualAlloc ( ptr , size , MEM_COMMIT , os_page_flags ) ! = nullptr ;
return result ;
}
DQN_API void Dqn_OS_MemDecommit ( void * ptr , Dqn_usize size )
{
// NOTE: This is a decommit call, which is explicitly saying to free the
// pages but not the address space, you would use OS_MemRelease to release
// everything.
DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE ( 6250 ) // Calling 'VirtualFree' without the MEM_RELEASE flag might free memory but not address descriptors (VADs). This causes address space leaks.
VirtualFree ( ptr , size , MEM_DECOMMIT ) ;
DQN_MSVC_WARNING_POP
}
DQN_API void Dqn_OS_MemRelease ( void * ptr , Dqn_usize size )
{
( void ) size ;
VirtualFree ( ptr , 0 , MEM_RELEASE ) ;
}
DQN_API int Dqn_OS_MemProtect ( void * ptr , Dqn_usize size , uint32_t page_flags )
{
if ( ! ptr | | size = = 0 )
return 0 ;
static Dqn_Str8 const ALIGNMENT_ERROR_MSG =
DQN_STR8 ( " Page protection requires pointers to be page aligned because we "
" can only guard memory at a multiple of the page boundary. " ) ;
DQN_ASSERTF ( Dqn_IsPowerOfTwoAligned ( DQN_CAST ( uintptr_t ) ptr , g_dqn_library - > os_page_size ) , " %s " , ALIGNMENT_ERROR_MSG . data ) ;
DQN_ASSERTF ( Dqn_IsPowerOfTwoAligned ( size , g_dqn_library - > os_page_size ) , " %s " , ALIGNMENT_ERROR_MSG . data ) ;
unsigned long os_page_flags = Dqn_OS_MemConvertPageToOSFlags_ ( page_flags ) ;
unsigned long prev_flags = 0 ;
int result = VirtualProtect ( ptr , size , os_page_flags , & prev_flags ) ;
( void ) prev_flags ;
if ( result = = 0 )
DQN_ASSERTF ( result , " VirtualProtect failed " ) ;
return result ;
}
// NOTE: [$DATE] Date //////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_OSDateTime Dqn_OS_DateLocalTimeNow ( )
{
SYSTEMTIME sys_time ;
GetLocalTime ( & sys_time ) ;
Dqn_OSDateTime result = { } ;
result . hour = DQN_CAST ( uint8_t ) sys_time . wHour ;
result . minutes = DQN_CAST ( uint8_t ) sys_time . wMinute ;
result . seconds = DQN_CAST ( uint8_t ) sys_time . wSecond ;
result . day = DQN_CAST ( uint8_t ) sys_time . wDay ;
result . month = DQN_CAST ( uint8_t ) sys_time . wMonth ;
result . year = DQN_CAST ( int16_t ) sys_time . wYear ;
return result ;
}
2024-02-25 11:37:14 +00:00
const uint64_t DQN_OS_WIN32_UNIX_TIME_START = 0x019DB1DED53E8000 ; // January 1, 1970 (start of Unix epoch) in "ticks"
const uint64_t DQN_OS_WIN32_FILE_TIME_TICKS_PER_SECOND = 10'000'000 ; // Filetime returned is in intervals of 100 nanoseconds
2024-01-31 12:49:23 +00:00
DQN_API uint64_t Dqn_OS_DateUnixTime ( )
{
FILETIME file_time ;
GetSystemTimeAsFileTime ( & file_time ) ;
LARGE_INTEGER date_time ;
date_time . u . LowPart = file_time . dwLowDateTime ;
date_time . u . HighPart = file_time . dwHighDateTime ;
2024-02-25 11:37:14 +00:00
uint64_t result = ( date_time . QuadPart - DQN_OS_WIN32_UNIX_TIME_START ) / DQN_OS_WIN32_FILE_TIME_TICKS_PER_SECOND ;
return result ;
}
2024-04-18 12:59:11 +00:00
DQN_API Dqn_OSDateTime Dqn_OS_DateUnixTimeToDate ( uint64_t time )
{
// NOTE: Windows epoch time starts from Jan 1, 1601 and counts in
// 100-nanoseconds intervals.
//
// See: https://devblogs.microsoft.com/oldnewthing/20090306-00/?p=18913
uint64_t win32_time = 116'444'736'000'000'000 + ( time * 10'000'000 ) ;
SYSTEMTIME sys_time = { } ;
FILETIME file_time = { } ;
file_time . dwLowDateTime = ( DWORD ) win32_time ;
file_time . dwHighDateTime = win32_time > > 32 ;
FileTimeToSystemTime ( & file_time , & sys_time ) ;
Dqn_OSDateTime result = { } ;
result . year = DQN_CAST ( uint16_t ) sys_time . wYear ;
result . month = DQN_CAST ( uint8_t ) sys_time . wMonth ;
result . day = DQN_CAST ( uint8_t ) sys_time . wDay ;
result . hour = DQN_CAST ( uint8_t ) sys_time . wHour ;
result . minutes = DQN_CAST ( uint8_t ) sys_time . wMinute ;
result . seconds = DQN_CAST ( uint8_t ) sys_time . wSecond ;
return result ;
}
2024-02-25 11:37:14 +00:00
DQN_API uint64_t Dqn_OS_DateToUnixTime ( Dqn_OSDateTime date )
{
DQN_ASSERT ( Dqn_OS_DateIsValid ( date ) ) ;
SYSTEMTIME sys_time = { } ;
sys_time . wYear = date . year ;
sys_time . wMonth = date . month ;
sys_time . wDay = date . day ;
sys_time . wHour = date . hour ;
sys_time . wMinute = date . minutes ;
sys_time . wSecond = date . seconds ;
FILETIME file_time = { } ;
SystemTimeToFileTime ( & sys_time , & file_time ) ;
LARGE_INTEGER date_time ;
date_time . u . LowPart = file_time . dwLowDateTime ;
date_time . u . HighPart = file_time . dwHighDateTime ;
uint64_t result = ( date_time . QuadPart - DQN_OS_WIN32_UNIX_TIME_START ) / DQN_OS_WIN32_FILE_TIME_TICKS_PER_SECOND ;
2024-01-31 12:49:23 +00:00
return result ;
}
DQN_API bool Dqn_OS_SecureRNGBytes ( void * buffer , uint32_t size )
{
if ( ! buffer | | size < 0 )
return false ;
if ( size = = 0 )
return true ;
bool init = true ;
Dqn_TicketMutex_Begin ( & g_dqn_library - > win32_bcrypt_rng_mutex ) ;
if ( ! g_dqn_library - > win32_bcrypt_rng_handle )
{
wchar_t const BCRYPT_ALGORITHM [ ] = L " RNG " ;
long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider ( & g_dqn_library - > win32_bcrypt_rng_handle , BCRYPT_ALGORITHM , nullptr /*implementation*/ , 0 /*flags*/ ) ;
if ( ! g_dqn_library - > win32_bcrypt_rng_handle | | init_status ! = 0 )
{
Dqn_Log_ErrorF ( " Failed to initialise random number generator, error: %d " , init_status ) ;
init = false ;
}
}
Dqn_TicketMutex_End ( & g_dqn_library - > win32_bcrypt_rng_mutex ) ;
if ( ! init )
return false ;
long gen_status = BCryptGenRandom ( g_dqn_library - > win32_bcrypt_rng_handle , DQN_CAST ( unsigned char * ) buffer , size , 0 /*flags*/ ) ;
if ( gen_status ! = 0 )
{
Dqn_Log_ErrorF ( " Failed to generate random bytes: %d " , gen_status ) ;
return false ;
}
return true ;
}
DQN_API Dqn_Str8 Dqn_OS_EXEPath ( Dqn_Arena * arena )
{
Dqn_Str8 result = { } ;
if ( ! arena )
return result ;
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( arena ) ;
Dqn_Str16 exe_dir16 = Dqn_Win_EXEPathW ( t_mem . arena ) ;
2024-01-31 12:49:23 +00:00
result = Dqn_Win_Str16ToStr8 ( arena , exe_dir16 ) ;
return result ;
}
DQN_API void Dqn_OS_SleepMs ( Dqn_uint milliseconds )
{
Sleep ( milliseconds ) ;
}
DQN_API uint64_t Dqn_OS_PerfCounterFrequency ( )
{
uint64_t result = g_dqn_library - > win32_qpc_frequency . QuadPart ;
DQN_ASSERTF ( result , " Initialise the library with Dqn_Library_Init() to get a valid QPC frequency value " ) ;
return result ;
}
DQN_API uint64_t Dqn_OS_PerfCounterNow ( )
{
LARGE_INTEGER integer = { } ;
QueryPerformanceCounter ( & integer ) ;
uint64_t result = integer . QuadPart ;
return result ;
}
# if !defined(DQN_NO_OS_FILE_API)
static uint64_t Dqn_Win_FileTimeToSeconds_ ( FILETIME const * time )
{
ULARGE_INTEGER time_large_int = { } ;
time_large_int . u . LowPart = time - > dwLowDateTime ;
time_large_int . u . HighPart = time - > dwHighDateTime ;
uint64_t result = ( time_large_int . QuadPart / 10000000ULL ) - 11644473600ULL ;
return result ;
}
DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo ( Dqn_Str8 path )
{
Dqn_OSPathInfo result = { } ;
if ( ! Dqn_Str8_HasData ( path ) )
return result ;
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( nullptr ) ;
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , path ) ;
2024-01-31 12:49:23 +00:00
WIN32_FILE_ATTRIBUTE_DATA attrib_data = { } ;
if ( ! GetFileAttributesExW ( path16 . data , GetFileExInfoStandard , & attrib_data ) )
return result ;
result . exists = true ;
result . create_time_in_s = Dqn_Win_FileTimeToSeconds_ ( & attrib_data . ftCreationTime ) ;
result . last_access_time_in_s = Dqn_Win_FileTimeToSeconds_ ( & attrib_data . ftLastAccessTime ) ;
result . last_write_time_in_s = Dqn_Win_FileTimeToSeconds_ ( & attrib_data . ftLastWriteTime ) ;
LARGE_INTEGER large_int = { } ;
large_int . u . HighPart = DQN_CAST ( int32_t ) attrib_data . nFileSizeHigh ;
large_int . u . LowPart = attrib_data . nFileSizeLow ;
result . size = ( uint64_t ) large_int . QuadPart ;
if ( attrib_data . dwFileAttributes ! = INVALID_FILE_ATTRIBUTES ) {
if ( attrib_data . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
result . type = Dqn_OSPathInfoType_Directory ;
else
result . type = Dqn_OSPathInfoType_File ;
}
return result ;
}
2024-02-11 07:23:13 +00:00
DQN_API bool Dqn_OS_PathDelete ( Dqn_Str8 path )
{
bool result = false ;
if ( ! Dqn_Str8_HasData ( path ) )
return result ;
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( nullptr ) ;
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , path ) ;
2024-02-11 07:23:13 +00:00
if ( path16 . size ) {
result = DeleteFileW ( path16 . data ) ;
if ( ! result )
result = RemoveDirectoryW ( path16 . data ) ;
}
return result ;
}
2024-01-31 12:49:23 +00:00
DQN_API bool Dqn_OS_FileExists ( Dqn_Str8 path )
{
bool result = false ;
if ( ! Dqn_Str8_HasData ( path ) )
return result ;
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( nullptr ) ;
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , path ) ;
2024-01-31 12:49:23 +00:00
if ( path16 . size ) {
WIN32_FILE_ATTRIBUTE_DATA attrib_data = { } ;
if ( GetFileAttributesExW ( path16 . data , GetFileExInfoStandard , & attrib_data ) ) {
result = ( attrib_data . dwFileAttributes ! = INVALID_FILE_ATTRIBUTES ) & &
! ( attrib_data . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ;
}
}
return result ;
}
2024-02-11 07:23:13 +00:00
DQN_API bool Dqn_OS_CopyFile ( Dqn_Str8 src , Dqn_Str8 dest , bool overwrite , Dqn_ErrorSink * error )
2024-01-31 12:49:23 +00:00
{
bool result = false ;
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( nullptr ) ;
Dqn_Str16 src16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , src ) ;
Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , dest ) ;
2024-01-31 12:49:23 +00:00
int fail_if_exists = overwrite = = false ;
2024-02-11 05:14:00 +00:00
result = CopyFileW ( src16 . data , dest16 . data , fail_if_exists ) ! = 0 ;
2024-01-31 12:49:23 +00:00
if ( ! result ) {
2024-08-01 03:34:36 +00:00
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
2024-02-11 05:14:00 +00:00
Dqn_ErrorSink_MakeF ( error ,
win_error . code ,
" Failed to copy file '%.*s' to '%.*s': (%u) %.*s " ,
DQN_STR_FMT ( src ) ,
DQN_STR_FMT ( dest ) ,
win_error . code ,
DQN_STR_FMT ( win_error . msg ) ) ;
2024-01-31 12:49:23 +00:00
}
return result ;
}
2024-02-11 07:23:13 +00:00
DQN_API bool Dqn_OS_MoveFile ( Dqn_Str8 src , Dqn_Str8 dest , bool overwrite , Dqn_ErrorSink * error )
2024-01-31 12:49:23 +00:00
{
bool result = false ;
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( nullptr ) ;
Dqn_Str16 src16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , src ) ;
Dqn_Str16 dest16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , dest ) ;
2024-01-31 12:49:23 +00:00
unsigned long flags = MOVEFILE_COPY_ALLOWED ;
if ( overwrite ) {
flags | = MOVEFILE_REPLACE_EXISTING ;
}
result = MoveFileExW ( src16 . data , dest16 . data , flags ) ! = 0 ;
if ( ! result ) {
2024-08-01 03:34:36 +00:00
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
2024-02-11 05:14:00 +00:00
Dqn_ErrorSink_MakeF ( error ,
win_error . code ,
" Failed to move file '%.*s' to '%.*s': (%u) %.*s " ,
DQN_STR_FMT ( src ) ,
DQN_STR_FMT ( dest ) ,
win_error . code ,
DQN_STR_FMT ( win_error . msg ) ) ;
2024-01-31 12:49:23 +00:00
}
return result ;
}
2024-02-11 07:23:13 +00:00
DQN_API bool Dqn_OS_MakeDir ( Dqn_Str8 path )
2024-01-31 12:49:23 +00:00
{
2024-08-01 03:34:36 +00:00
bool result = true ;
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( nullptr ) ;
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , path ) ;
2024-01-31 12:49:23 +00:00
// NOTE: Go back from the end of the string to all the directories in the
// string, and try to create them. Since Win32 API cannot create
// intermediate directories that don't exist in a path we need to go back
// and record all the directories until we encounter one that exists.
//
// From that point onwards go forwards and make all the directories
// inbetween by null-terminating the string temporarily, creating the
// directory and so forth until we reach the end.
//
// If we find a file at some point in the path we fail out because the
// series of directories can not be made if a file exists with the same
// name.
for ( Dqn_usize index = 0 ; index < path16 . size ; index + + ) {
bool first_char = index = = ( path16 . size - 1 ) ;
wchar_t ch = path16 . data [ index ] ;
if ( ch = = ' / ' | | ch = = ' \\ ' | | first_char ) {
wchar_t temp = path16 . data [ index ] ;
if ( ! first_char )
path16 . data [ index ] = 0 ; // Temporarily null terminate it
WIN32_FILE_ATTRIBUTE_DATA attrib_data = { } ;
bool successful = GetFileAttributesExW ( path16 . data , GetFileExInfoStandard , & attrib_data ) ; // Check
if ( successful ) {
if ( attrib_data . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
// NOTE: The directory exists, continue iterating the path
} else {
// NOTE: There's some kind of file that exists at the path
// but it's not a directory. This request to make a
// directory is invalid.
return false ;
}
} else {
// NOTE: There's nothing that exists at this path, we can create
// a directory here
result | = ( CreateDirectoryW ( path16 . data , nullptr ) = = 0 ) ;
}
if ( ! first_char )
path16 . data [ index ] = temp ; // Undo null termination
}
}
return result ;
}
2024-02-11 07:23:13 +00:00
DQN_API bool Dqn_OS_DirExists ( Dqn_Str8 path )
2024-01-31 12:49:23 +00:00
{
bool result = false ;
if ( ! Dqn_Str8_HasData ( path ) )
return result ;
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( nullptr ) ;
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , path ) ;
2024-01-31 12:49:23 +00:00
if ( path16 . size ) {
2024-02-11 07:23:13 +00:00
WIN32_FILE_ATTRIBUTE_DATA attrib_data = { } ;
if ( GetFileAttributesExW ( path16 . data , GetFileExInfoStandard , & attrib_data ) ) {
result = ( attrib_data . dwFileAttributes ! = INVALID_FILE_ATTRIBUTES ) & &
( attrib_data . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ;
}
2024-01-31 12:49:23 +00:00
}
2024-02-11 07:23:13 +00:00
2024-01-31 12:49:23 +00:00
return result ;
}
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
2024-02-11 07:23:13 +00:00
DQN_API Dqn_OSFile Dqn_OS_FileOpen ( Dqn_Str8 path , Dqn_OSFileOpen open_mode , uint32_t access , Dqn_ErrorSink * error )
2024-01-31 12:49:23 +00:00
{
Dqn_OSFile result = { } ;
if ( ! Dqn_Str8_HasData ( path ) | | path . size < = 0 )
return result ;
if ( ( access & ~ Dqn_OSFileAccess_All ) | | ( ( access & Dqn_OSFileAccess_All ) = = 0 ) ) {
DQN_INVALID_CODE_PATH ;
return result ;
}
unsigned long create_flag = 0 ;
switch ( open_mode ) {
case Dqn_OSFileOpen_CreateAlways : create_flag = CREATE_ALWAYS ; break ;
case Dqn_OSFileOpen_OpenIfExist : create_flag = OPEN_EXISTING ; break ;
case Dqn_OSFileOpen_OpenAlways : create_flag = OPEN_ALWAYS ; break ;
default : DQN_INVALID_CODE_PATH ; return result ;
}
unsigned long access_mode = 0 ;
if ( access & Dqn_OSFileAccess_AppendOnly ) {
DQN_ASSERTF ( ( access & ~ Dqn_OSFileAccess_AppendOnly ) = = 0 ,
" Append can only be applied exclusively to the file, other access modes not permitted " ) ;
access_mode = FILE_APPEND_DATA ;
} else {
if ( access & Dqn_OSFileAccess_Read )
access_mode | = GENERIC_READ ;
if ( access & Dqn_OSFileAccess_Write )
access_mode | = GENERIC_WRITE ;
if ( access & Dqn_OSFileAccess_Execute )
access_mode | = GENERIC_EXECUTE ;
}
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( nullptr ) ;
Dqn_Str16 path16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , path ) ;
2024-01-31 12:49:23 +00:00
void * handle = CreateFileW ( /*LPCWSTR lpFileName*/ path16 . data ,
/*DWORD dwDesiredAccess*/ access_mode ,
/*DWORD dwShareMode*/ 0 ,
/*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ nullptr ,
/*DWORD dwCreationDisposition*/ create_flag ,
/*DWORD dwFlagsAndAttributes*/ FILE_ATTRIBUTE_NORMAL ,
/*HANDLE hTemplateFile*/ nullptr ) ;
if ( handle = = INVALID_HANDLE_VALUE ) {
2024-08-01 03:34:36 +00:00
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
2024-02-11 05:14:00 +00:00
result . error = true ;
Dqn_ErrorSink_MakeF ( error , win_error . code , " Failed to open file at '%.*s': '%.*s' " , DQN_STR_FMT ( path ) , DQN_STR_FMT ( win_error . msg ) ) ;
2024-01-31 12:49:23 +00:00
return result ;
}
result . handle = handle ;
return result ;
}
2024-02-11 07:23:13 +00:00
DQN_API bool Dqn_OS_FileRead ( Dqn_OSFile * file , void * buffer , Dqn_usize size , Dqn_ErrorSink * error )
{
if ( ! file | | ! file - > handle | | file - > error | | ! buffer | | size < = 0 )
return false ;
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( nullptr ) ;
2024-02-11 07:23:13 +00:00
if ( ! DQN_CHECK ( size < = ( unsigned long ) - 1 ) ) {
2024-08-01 03:34:36 +00:00
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8 ( t_mem . arena , size , Dqn_U64ByteSizeType_Auto ) ;
2024-02-11 07:23:13 +00:00
Dqn_ErrorSink_MakeF (
error ,
1 /*error_code*/ ,
" Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO " ,
DQN_STR_FMT ( buffer_size_str8 ) ) ;
return false ;
}
unsigned long bytes_read = 0 ;
unsigned long read_result = ReadFile ( /*HANDLE hFile*/ file - > handle ,
/*LPVOID lpBuffer*/ buffer ,
/*DWORD nNumberOfBytesToRead*/ DQN_CAST ( unsigned long ) size ,
/*LPDWORD lpNumberOfByesRead*/ & bytes_read ,
/*LPOVERLAPPED lpOverlapped*/ nullptr ) ;
if ( read_result = = 0 ) {
2024-08-01 03:34:36 +00:00
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
2024-02-11 07:23:13 +00:00
Dqn_ErrorSink_MakeF ( error , win_error . code , " Failed to read data from file: (%u) %.*s " , win_error . code , DQN_STR_FMT ( win_error . msg ) ) ;
return false ;
}
if ( bytes_read ! = size ) {
2024-08-01 03:34:36 +00:00
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
2024-02-11 07:23:13 +00:00
Dqn_ErrorSink_MakeF (
error ,
win_error . code ,
" Failed to read the desired number of bytes from file, we read %uB but we expected %uB: (%u) %.*s " ,
bytes_read ,
DQN_CAST ( unsigned long ) size ,
win_error . code ,
DQN_STR_FMT ( win_error . msg ) ) ;
return false ;
}
return true ;
}
DQN_API bool Dqn_OS_FileWritePtr ( Dqn_OSFile * file , void const * buffer , Dqn_usize size , Dqn_ErrorSink * error )
2024-01-31 12:49:23 +00:00
{
2024-02-11 05:14:00 +00:00
if ( ! file | | ! file - > handle | | file - > error | | ! buffer | | size < = 0 )
2024-01-31 12:49:23 +00:00
return false ;
bool result = true ;
char const * end = DQN_CAST ( char * ) buffer + size ;
for ( char const * ptr = DQN_CAST ( char const * ) buffer ; result & & ptr ! = end ; ) {
unsigned long write_size = DQN_CAST ( unsigned long ) DQN_MIN ( ( unsigned long ) - 1 , end - ptr ) ;
unsigned long bytes_written = 0 ;
result = WriteFile ( file - > handle , ptr , write_size , & bytes_written , nullptr /*lpOverlapped*/ ) ! = 0 ;
ptr + = bytes_written ;
}
if ( ! result ) {
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( nullptr ) ;
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8 ( t_mem . arena , size , Dqn_U64ByteSizeType_Auto ) ;
2024-02-11 05:14:00 +00:00
Dqn_ErrorSink_MakeF ( error , win_error . code , " Failed to write buffer (%.*s) to file handle: %.*s " , DQN_STR_FMT ( buffer_size_str8 ) , DQN_STR_FMT ( win_error . msg ) ) ;
2024-01-31 12:49:23 +00:00
}
return result ;
}
2024-02-11 07:23:13 +00:00
DQN_API void Dqn_OS_FileClose ( Dqn_OSFile * file )
2024-01-31 12:49:23 +00:00
{
2024-02-11 05:14:00 +00:00
if ( ! file | | ! file - > handle | | file - > error )
2024-01-31 12:49:23 +00:00
return ;
CloseHandle ( file - > handle ) ;
* file = { } ;
}
# endif // !defined(DQN_NO_OS_FILE_API)
// NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
2024-03-19 12:11:00 +00:00
DQN_API void Dqn_OS_Exit ( int32_t exit_code )
2024-01-31 12:49:23 +00:00
{
2024-03-19 12:11:00 +00:00
ExitProcess ( DQN_CAST ( UINT ) exit_code ) ;
2024-01-31 12:49:23 +00:00
}
2024-03-25 02:14:05 +00:00
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait ( Dqn_OSExecAsyncHandle handle , Dqn_Arena * arena , Dqn_ErrorSink * error )
2024-01-31 12:49:23 +00:00
{
Dqn_OSExecResult result = { } ;
2024-03-25 02:14:05 +00:00
if ( ! handle . process | | handle . os_error_code | | handle . exit_code ) {
if ( handle . os_error_code )
result . os_error_code = handle . os_error_code ;
else
result . exit_code = handle . exit_code ;
2024-01-31 12:49:23 +00:00
2024-03-25 02:14:05 +00:00
DQN_ASSERT ( ! handle . stdout_read ) ;
DQN_ASSERT ( ! handle . stdout_write ) ;
DQN_ASSERT ( ! handle . stderr_read ) ;
DQN_ASSERT ( ! handle . stderr_write ) ;
DQN_ASSERT ( ! handle . process ) ;
2024-01-31 12:49:23 +00:00
return result ;
}
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( arena ) ;
Dqn_Str8Builder stdout_builder = { } ;
Dqn_Str8Builder stderr_builder = { } ;
if ( arena ) {
stdout_builder . arena = t_mem . arena ;
stderr_builder . arena = t_mem . arena ;
}
2024-03-25 02:14:05 +00:00
2024-08-01 03:34:36 +00:00
DWORD const SLOW_WAIT_TIME_MS = 100 ;
DWORD const FAST_WAIT_TIME_MS = 20 ;
DWORD wait_ms = FAST_WAIT_TIME_MS ;
DWORD exec_result = WAIT_TIMEOUT ;
while ( exec_result = = WAIT_TIMEOUT ) {
exec_result = WaitForSingleObject ( handle . process , wait_ms ) ;
if ( exec_result = = WAIT_FAILED ) {
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
result . os_error_code = win_error . code ;
Dqn_ErrorSink_MakeF ( error , result . os_error_code , " Executed command failed to terminate: %.*s " , DQN_STR_FMT ( win_error . msg ) ) ;
} else if ( DQN_CHECK ( exec_result = = WAIT_TIMEOUT | | exec_result = = WAIT_OBJECT_0 ) ) {
// NOTE: If the pipes are full, the process will block. We
// periodically flush the pipes to make sure this doesn't happen
// NOTE: Read stdout from process //////////////////////////////////////////////////////
// TODO: Allow the user to pump the execution of the command w/ a user passed in buffer.
char output_buffer [ DQN_KILOBYTES ( 8 ) ] ;
DWORD stdout_bytes_available = 0 ;
if ( PeekNamedPipe ( handle . stdout_read , nullptr , 0 , nullptr , & stdout_bytes_available , nullptr ) ) {
if ( stdout_bytes_available ) {
for ( ; ; ) {
DWORD bytes_read = 0 ;
BOOL success = ReadFile ( handle . stdout_read , output_buffer , sizeof ( output_buffer ) , & bytes_read , NULL ) ;
if ( ! success | | bytes_read = = 0 )
break ;
if ( handle . stdout_write & & arena )
Dqn_Str8Builder_AddF ( & stdout_builder , " %.*s " , bytes_read , output_buffer ) ;
if ( bytes_read < sizeof ( output_buffer ) )
break ;
}
}
}
// NOTE: Read stderr from process //////////////////////////////////////////////////////
DWORD stderr_bytes_available = 0 ;
if ( PeekNamedPipe ( handle . stderr_read , nullptr , 0 , nullptr , & stderr_bytes_available , nullptr ) ) {
if ( stderr_bytes_available ) {
for ( ; ; ) {
DWORD bytes_read = 0 ;
BOOL success = ReadFile ( handle . stderr_read , output_buffer , sizeof ( output_buffer ) , & bytes_read , NULL ) ;
if ( ! success | | bytes_read = = 0 )
break ;
if ( handle . stderr_read & & arena )
Dqn_Str8Builder_AddF ( & stderr_builder , " %.*s " , bytes_read , output_buffer ) ;
if ( bytes_read < sizeof ( output_buffer ) )
break ;
}
}
}
// NOTE: If we produced some data, we'll sleep for a short amount of
// time because if we have the presence of data it's typical we deal
// with N amount of. Otherwise if we timed-out and we didn't produce
// any data, its more likely this is a long running operation so we
// sleep longer to avoid slamming the CPU.
if ( stdout_bytes_available | | stderr_bytes_available ) {
wait_ms = FAST_WAIT_TIME_MS ;
} else {
wait_ms = SLOW_WAIT_TIME_MS ;
}
}
}
// NOTE: Get stdout/stderr. If no arena is passed this is a no-op //////////////////////////////
result . stdout_text = Dqn_Str8Builder_Build ( & stdout_builder , arena ) ;
result . stderr_text = Dqn_Str8Builder_Build ( & stderr_builder , arena ) ;
// NOTE: Get exit code /////////////////////////////////////////////////////////////////////////
if ( exec_result ! = WAIT_FAILED ) {
2024-03-25 02:14:05 +00:00
DWORD exit_status ;
if ( GetExitCodeProcess ( handle . process , & exit_status ) ) {
result . exit_code = exit_status ;
} else {
2024-08-01 03:34:36 +00:00
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
2024-03-25 02:14:05 +00:00
result . os_error_code = win_error . code ;
Dqn_ErrorSink_MakeF ( error ,
result . os_error_code ,
" Failed to retrieve command exit code: %.*s " ,
DQN_STR_FMT ( win_error . msg ) ) ;
}
2024-01-31 12:49:23 +00:00
}
2024-08-01 03:34:36 +00:00
// NOTE: Cleanup ///////////////////////////////////////////////////////////////////////////////
CloseHandle ( handle . stdout_write ) ;
CloseHandle ( handle . stderr_write ) ;
2024-03-25 02:14:05 +00:00
CloseHandle ( handle . stdout_read ) ;
CloseHandle ( handle . stderr_read ) ;
2024-08-01 03:34:36 +00:00
CloseHandle ( handle . process ) ;
2024-01-31 12:49:23 +00:00
return result ;
}
2024-03-25 05:11:57 +00:00
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync ( Dqn_Slice < Dqn_Str8 > cmd_line , Dqn_Str8 working_dir , uint8_t exec_flags , Dqn_ErrorSink * error )
2024-01-31 12:49:23 +00:00
{
2024-03-25 02:14:05 +00:00
// NOTE: Pre-amble /////////////////////////////////////////////////////////////////////////////
2024-01-31 12:49:23 +00:00
Dqn_OSExecAsyncHandle result = { } ;
if ( cmd_line . size = = 0 )
return result ;
2024-08-01 03:34:36 +00:00
Dqn_TLSTMem t_mem = Dqn_TLS_TMem ( nullptr ) ;
Dqn_Str8 cmd_rendered = Dqn_Slice_Str8Render ( t_mem . arena , cmd_line , DQN_STR8 ( " " ) ) ;
Dqn_Str16 cmd16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , cmd_rendered ) ;
Dqn_Str16 working_dir16 = Dqn_Win_Str8ToStr16 ( t_mem . arena , working_dir ) ;
2024-01-31 12:49:23 +00:00
2024-03-25 02:14:05 +00:00
// NOTE: Stdout/err security attributes ////////////////////////////////////////////////////////
SECURITY_ATTRIBUTES save_std_security_attribs = { } ;
save_std_security_attribs . nLength = sizeof ( save_std_security_attribs ) ;
save_std_security_attribs . bInheritHandle = true ;
// NOTE: Redirect stdout ///////////////////////////////////////////////////////////////////////
HANDLE stdout_read = { } ;
HANDLE stdout_write = { } ;
2024-03-25 05:11:57 +00:00
DQN_DEFER {
if ( result . os_error_code | | result . exit_code ) {
CloseHandle ( stdout_read ) ;
CloseHandle ( stdout_write ) ;
}
} ;
2024-03-25 05:36:15 +00:00
if ( Dqn_Bit_IsSet ( exec_flags , Dqn_OSExecFlag_SaveStdout ) ) {
2024-03-25 02:14:05 +00:00
if ( ! CreatePipe ( & stdout_read , & stdout_write , & save_std_security_attribs , /*nSize*/ 0 ) ) {
2024-08-01 03:34:36 +00:00
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
2024-03-25 02:14:05 +00:00
result . os_error_code = win_error . code ;
Dqn_ErrorSink_MakeF (
error ,
result . os_error_code ,
" Failed to create stdout pipe to redirect the output of the command '%.*s': %.*s " ,
DQN_STR_FMT ( cmd_rendered ) ,
DQN_STR_FMT ( win_error . msg ) ) ;
return result ;
}
if ( ! SetHandleInformation ( stdout_read , HANDLE_FLAG_INHERIT , 0 ) ) {
2024-08-01 03:34:36 +00:00
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
2024-03-25 02:14:05 +00:00
result . os_error_code = win_error . code ;
Dqn_ErrorSink_MakeF ( error ,
result . os_error_code ,
" Failed to make stdout 'read' pipe non-inheritable when trying to "
" execute command '%.*s': %.*s " ,
DQN_STR_FMT ( cmd_rendered ) ,
DQN_STR_FMT ( win_error . msg ) ) ;
return result ;
}
}
// NOTE: Redirect stderr ///////////////////////////////////////////////////////////////////////
HANDLE stderr_read = { } ;
HANDLE stderr_write = { } ;
2024-03-25 05:11:57 +00:00
DQN_DEFER {
if ( result . os_error_code | | result . exit_code ) {
CloseHandle ( stderr_read ) ;
CloseHandle ( stderr_write ) ;
}
} ;
if ( Dqn_Bit_IsSet ( exec_flags , Dqn_OSExecFlag_SaveStderr ) ) {
if ( Dqn_Bit_IsSet ( exec_flags , Dqn_OSExecFlag_MergeStderrToStdout ) ) {
2024-03-25 02:14:05 +00:00
stderr_read = stdout_read ;
stderr_write = stdout_write ;
} else {
if ( ! CreatePipe ( & stderr_read , & stderr_write , & save_std_security_attribs , /*nSize*/ 0 ) ) {
2024-08-01 03:34:36 +00:00
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
2024-03-25 02:14:05 +00:00
result . os_error_code = win_error . code ;
Dqn_ErrorSink_MakeF (
error ,
result . os_error_code ,
" Failed to create stderr pipe to redirect the output of the command '%.*s': %.*s " ,
DQN_STR_FMT ( cmd_rendered ) ,
DQN_STR_FMT ( win_error . msg ) ) ;
return result ;
}
if ( ! SetHandleInformation ( stderr_read , HANDLE_FLAG_INHERIT , 0 ) ) {
2024-08-01 03:34:36 +00:00
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
2024-03-25 02:14:05 +00:00
result . os_error_code = win_error . code ;
Dqn_ErrorSink_MakeF ( error ,
result . os_error_code ,
" Failed to make stderr 'read' pipe non-inheritable when trying to "
" execute command '%.*s': %.*s " ,
DQN_STR_FMT ( cmd_rendered ) ,
DQN_STR_FMT ( win_error . msg ) ) ;
return result ;
}
}
}
// NOTE: Execute command ///////////////////////////////////////////////////////////////////////
2024-01-31 12:49:23 +00:00
PROCESS_INFORMATION proc_info = { } ;
STARTUPINFOW startup_info = { } ;
startup_info . cb = sizeof ( STARTUPINFOW ) ;
2024-03-25 02:14:05 +00:00
startup_info . hStdError = stderr_write ? stderr_write : GetStdHandle ( STD_ERROR_HANDLE ) ;
startup_info . hStdOutput = stdout_write ? stdout_write : GetStdHandle ( STD_OUTPUT_HANDLE ) ;
2024-01-31 12:49:23 +00:00
startup_info . hStdInput = GetStdHandle ( STD_INPUT_HANDLE ) ;
startup_info . dwFlags | = STARTF_USESTDHANDLES ;
BOOL create_result = CreateProcessW ( nullptr , cmd16 . data , nullptr , nullptr , true , 0 , nullptr , working_dir16 . data , & startup_info , & proc_info ) ;
if ( ! create_result ) {
2024-08-01 03:34:36 +00:00
Dqn_WinError win_error = Dqn_Win_LastError ( t_mem . arena ) ;
2024-03-25 02:14:05 +00:00
result . os_error_code = win_error . code ;
Dqn_ErrorSink_MakeF ( error ,
result . os_error_code ,
" Failed to execute command '%.*s': %.*s " ,
DQN_STR_FMT ( cmd_rendered ) ,
DQN_STR_FMT ( win_error . msg ) ) ;
2024-01-31 12:49:23 +00:00
return result ;
}
2024-03-25 02:14:05 +00:00
// NOTE: Post-amble ////////////////////////////////////////////////////////////////////////////
2024-01-31 12:49:23 +00:00
CloseHandle ( proc_info . hThread ) ;
2024-03-25 02:14:05 +00:00
result . process = proc_info . hProcess ;
result . stdout_read = stdout_read ;
result . stdout_write = stdout_write ;
2024-03-25 05:11:57 +00:00
if ( Dqn_Bit_IsSet ( exec_flags , Dqn_OSExecFlag_SaveStderr ) & & Dqn_Bit_IsNotSet ( exec_flags , Dqn_OSExecFlag_MergeStderrToStdout ) ) {
2024-03-25 02:14:05 +00:00
result . stderr_read = stderr_read ;
result . stderr_write = stderr_write ;
}
2024-03-25 05:11:57 +00:00
result . exec_flags = exec_flags ;
2024-01-31 12:49:23 +00:00
return result ;
}
# if !defined(DQN_NO_SEMAPHORE)
// NOTE: [$SEMA] Dqn_OSSemaphore ///////////////////////////////////////////////////////////////////
DQN_API Dqn_OSSemaphore Dqn_OS_SemaphoreInit ( uint32_t initial_count )
{
Dqn_OSSemaphore result = { } ;
SECURITY_ATTRIBUTES security_attribs = { } ;
result . win32_handle = CreateSemaphoreA ( & security_attribs , initial_count , INT32_MAX , nullptr /*name*/ ) ;
return result ;
}
2024-01-31 13:03:06 +00:00
DQN_API bool Dqn_OS_SemaphoreIsValid ( Dqn_OSSemaphore * semaphore )
2024-01-31 12:49:23 +00:00
{
bool result = false ;
if ( semaphore ) {
result = semaphore - > win32_handle ;
}
return result ;
}
DQN_API void Dqn_OS_SemaphoreDeinit ( Dqn_OSSemaphore * semaphore )
{
2024-01-31 13:03:06 +00:00
if ( ! Dqn_OS_SemaphoreIsValid ( semaphore ) )
2024-01-31 12:49:23 +00:00
return ;
CloseHandle ( semaphore - > win32_handle ) ;
* semaphore = { } ;
}
DQN_API void Dqn_OS_SemaphoreIncrement ( Dqn_OSSemaphore * semaphore , uint32_t amount )
{
2024-01-31 13:03:06 +00:00
if ( ! Dqn_OS_SemaphoreIsValid ( semaphore ) )
2024-01-31 12:49:23 +00:00
return ;
LONG prev_count = 0 ;
ReleaseSemaphore ( DQN_CAST ( HANDLE * ) semaphore - > win32_handle , amount , & prev_count ) ;
}
DQN_API Dqn_OSSemaphoreWaitResult Dqn_OS_SemaphoreWait ( Dqn_OSSemaphore * semaphore , uint32_t timeout_ms )
{
Dqn_OSSemaphoreWaitResult result = { } ;
2024-01-31 13:03:06 +00:00
if ( ! Dqn_OS_SemaphoreIsValid ( semaphore ) )
2024-01-31 12:49:23 +00:00
return result ;
if ( ! semaphore - > win32_handle )
return result ;
DWORD wait_result = WaitForSingleObject ( semaphore - > win32_handle , timeout_ms = = DQN_OS_SEMAPHORE_INFINITE_TIMEOUT ? INFINITE : timeout_ms ) ;
if ( wait_result = = WAIT_TIMEOUT )
result = Dqn_OSSemaphoreWaitResult_Timeout ;
else if ( wait_result = = WAIT_OBJECT_0 )
result = Dqn_OSSemaphoreWaitResult_Success ;
return result ;
}
# endif // !defined(DQN_NO_SEMAPHORE)
# if !defined(DQN_NO_THREAD)
// NOTE: [$MUTX] Dqn_OSMutex ///////////////////////////////////////////////////////////////////////
DQN_API Dqn_OSMutex Dqn_OS_MutexInit ( )
{
Dqn_OSMutex result = { } ;
CRITICAL_SECTION crit_section = { } ;
InitializeCriticalSection ( & crit_section ) ;
static_assert ( sizeof ( CRITICAL_SECTION ) < = sizeof ( result . win32_handle ) , " Insufficient bytes to store Win32 mutex opaquely in our abstracted Dqn_OSMutex " ) ;
DQN_MEMCPY ( result . win32_handle , & crit_section , sizeof ( crit_section ) ) ;
return result ;
}
DQN_API void Dqn_OS_MutexDeinit ( Dqn_OSMutex * mutex )
{
if ( ! mutex )
return ;
CRITICAL_SECTION * crit_section = DQN_CAST ( CRITICAL_SECTION * ) mutex - > win32_handle ;
DeleteCriticalSection ( crit_section ) ;
DQN_MEMSET ( mutex - > win32_handle , 0 , DQN_ARRAY_UCOUNT ( mutex - > win32_handle ) ) ;
}
DQN_API void Dqn_OS_MutexLock ( Dqn_OSMutex * mutex )
{
if ( ! mutex )
return ;
CRITICAL_SECTION * crit_section = DQN_CAST ( CRITICAL_SECTION * ) mutex - > win32_handle ;
EnterCriticalSection ( crit_section ) ;
}
DQN_API void Dqn_OS_MutexUnlock ( Dqn_OSMutex * mutex )
{
if ( ! mutex )
return ;
CRITICAL_SECTION * crit_section = DQN_CAST ( CRITICAL_SECTION * ) mutex - > win32_handle ;
LeaveCriticalSection ( crit_section ) ;
}
// NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
static DWORD __stdcall Dqn_OS_ThreadFunc_ ( void * user_context )
{
2024-08-01 03:34:36 +00:00
Dqn_OS_ThreadExecute_ ( user_context ) ;
2024-01-31 12:49:23 +00:00
return 0 ;
}
DQN_API bool Dqn_OS_ThreadInit ( Dqn_OSThread * thread , Dqn_OSThreadFunc * func , void * user_context )
{
bool result = false ;
if ( ! thread )
return result ;
thread - > func = func ;
thread - > user_context = user_context ;
thread - > init_semaphore = Dqn_OS_SemaphoreInit ( 0 /*initial_count*/ ) ;
// TODO(doyle): Check if semaphore is valid
DWORD thread_id = 0 ;
SECURITY_ATTRIBUTES security_attribs = { } ;
thread - > handle = CreateThread ( & security_attribs ,
0 /*stack_size*/ ,
Dqn_OS_ThreadFunc_ ,
thread ,
0 /*creation_flags*/ ,
& thread_id ) ;
result = thread - > handle ! = INVALID_HANDLE_VALUE ;
if ( result ) {
thread - > thread_id = thread_id ;
}
if ( result ) {
Dqn_OS_SemaphoreIncrement ( & thread - > init_semaphore , 1 ) ;