Get latest changes from LPP bot

This commit is contained in:
doylet 2025-06-03 16:26:40 +10:00
parent 2371297dda
commit b1394e6416
29 changed files with 2644 additions and 290 deletions

View File

@ -36,11 +36,11 @@ DN_API DN_USize DN_CPU_HasFeatureArray(DN_CPUReport const *report, DN_CPUFeature
{
DN_USize result = 0;
DN_USize const BITS = sizeof(report->features[0]) * 8;
DN_ForIndexU(feature_index, features_size) {
for (DN_ForIndexU(feature_index, features_size)) {
DN_CPUFeatureQuery *query = features + feature_index;
DN_USize chunk_index = query->feature / BITS;
DN_USize chunk_bit = query->feature % BITS;
DN_U64 chunk = report->features[chunk_index];
DN_U64 chunk = report->features[chunk_index];
query->available = chunk & (1ULL << chunk_bit);
result += DN_CAST(int) query->available;
}

View File

@ -16,11 +16,11 @@
#include <stdlib.h> // exit()
#endif
#define DN_ForIndexU(index, size) for (DN_USize index = 0; index < size; index++)
#define DN_ForIndexI(index, size) for (DN_ISize index = 0; index < size; index++)
#define DN_ForItSize(it, T, array, size) for (struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (size); it.index++, it.data++)
#define DN_ForIt(it, T, array) for (struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->size; it.index++, it.data++)
#define DN_ForItCArray(it, T, array) for (struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data++)
#define DN_ForIndexU(index, size) DN_USize index = 0; index < size; index++
#define DN_ForIndexI(index, size) DN_ISize index = 0; index < size; index++
#define DN_ForItSize(it, T, array, size) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (size); it.index++, it.data++
#define DN_ForIt(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->size; it.index++, it.data++
#define DN_ForItCArray(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data++
#define DN_AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1))
#define DN_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ~((uintptr_t)(pot) - 1))
@ -152,10 +152,10 @@ struct DN_DeferHelper
#define DN_DEFER const auto DN_UniqueName(defer_lambda_) = DN_DeferHelper() + [&]()
#endif // defined(__cplusplus)
#define DN_DEFER_LOOP(begin, end) \
for (bool DN_UniqueName(once) = (begin, true); \
DN_UniqueName(once); \
end, DN_UniqueName(once) = false)
#define DN_DeferLoop(begin, end) \
bool DN_UniqueName(once) = (begin, true); \
DN_UniqueName(once); \
end, DN_UniqueName(once) = false
// NOTE: Types /////////////////////////////////////////////////////////////////////////////////////
typedef intptr_t DN_ISize;
@ -246,20 +246,18 @@ struct DN_CallSite
#include <intrin.h>
#define DN_Atomic_CompareExchange64(dest, desired_val, prev_val) _InterlockedCompareExchange64((__int64 volatile *)dest, desired_val, prev_val)
#define DN_Atomic_CompareExchange32(dest, desired_val, prev_val) _InterlockedCompareExchange((long volatile *)dest, desired_val, prev_val)
#define DN_Atomic_LoadU64(target) *(target)
#define DN_Atomic_LoadU32(target) *(target)
#define DN_Atomic_AddU32(target, value) _InterlockedExchangeAdd((long volatile *)target, value)
#define DN_Atomic_AddU64(target, value) _InterlockedExchangeAdd64((__int64 volatile *)target, value)
#define DN_Atomic_SubU32(target, value) DN_Atomic_AddU32(DN_CAST(long volatile *) target, (long)-value)
#define DN_Atomic_SubU64(target, value) DN_Atomic_AddU64(target, (DN_U64) - value)
#define DN_CountLeadingZerosU64(value) __lzcnt64(value)
#define DN_CPU_TSC() __rdtsc()
#define DN_CompilerReadBarrierAndCPUReadFence \
_ReadBarrier(); \
_mm_lfence()
#define DN_CompilerWriteBarrierAndCPUWriteFence \
_WriteBarrier(); \
_mm_sfence()
#define DN_CountLeadingZerosU64(value) __lzcnt64(value)
#define DN_CPU_TSC() __rdtsc()
#define DN_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence()
#define DN_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence()
#elif defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
#if defined(__ANDROID__)
#elif defined(DN_PLATFORM_EMSCRIPTEN)
@ -268,17 +266,20 @@ struct DN_CallSite
#include <x86intrin.h>
#endif
#define DN_Atomic_AddU32(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
#define DN_Atomic_AddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
#define DN_Atomic_SubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
#define DN_Atomic_SubU64(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
#define DN_Atomic_LoadU64(target) __atomic_load_n(x, __ATOMIC_SEQ_CST)
#define DN_Atomic_LoadU32(target) __atomic_load_n(x, __ATOMIC_SEQ_CST)
#define DN_Atomic_AddU32(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
#define DN_Atomic_AddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
#define DN_Atomic_SubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
#define DN_Atomic_SubU64(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
#define DN_CountLeadingZerosU64(value) __builtin_clzll(value)
#define DN_CountLeadingZerosU64(value) __builtin_clzll(value)
#if defined(DN_COMPILER_GCC)
#define DN_CPU_TSC() __rdtsc()
#else
#define DN_CPU_TSC() __builtin_readcyclecounter()
#endif
#if defined(DN_PLATFORM_EMSCRIPTEN)
#define DN_CompilerReadBarrierAndCPUReadFence
#define DN_CompilerWriteBarrierAndCPUWriteFence
@ -406,7 +407,7 @@ struct DN_TicketMutex
// NOTE: Intrinsics ////////////////////////////////////////////////////////////////////////////////
DN_FORCE_INLINE DN_U64 DN_Atomic_SetValue64 (DN_U64 volatile *target, DN_U64 value);
DN_FORCE_INLINE long DN_Atomic_SetValue32 (long volatile *target, long value);
DN_FORCE_INLINE DN_U32 DN_Atomic_SetValue32 (DN_U32 volatile *target, DN_U32 value);
#if !defined (DN_PLATFORM_ARM64)
DN_API DN_CPUIDResult DN_CPU_ID (DN_CPUIDArgs args);
@ -515,7 +516,7 @@ DN_FORCE_INLINE DN_U64 DN_Atomic_SetValue64(DN_U64 volatile *target, DN_U64 valu
#endif
}
DN_FORCE_INLINE long DN_Atomic_SetValue32(long volatile *target, long value)
DN_FORCE_INLINE DN_U32 DN_Atomic_SetValue32(DN_U32 volatile *target, DN_U32 value)
{
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
long result;

View File

@ -1,5 +1,49 @@
#define DN_CONTAINERS_CPP
#include "../dn_base_inc.h"
DN_API bool DN_Ring_HasSpace(DN_Ring const *ring, DN_U64 size)
{
DN_U64 avail = ring->write_pos - ring->read_pos;
DN_U64 space = ring->size - avail;
bool result = space >= size;
return result;
}
DN_API bool DN_Ring_HasData(DN_Ring const *ring, DN_U64 size)
{
DN_U64 data = ring->write_pos - ring->read_pos;
bool result = data >= size;
return result;
}
DN_API void DN_Ring_Write(DN_Ring *ring, void const *src, DN_U64 src_size)
{
DN_Assert(src_size <= ring->size);
DN_U64 offset = ring->write_pos % ring->size;
DN_U64 bytes_before_split = ring->size - offset;
DN_U64 pre_split_bytes = DN_Min(bytes_before_split, src_size);
DN_U64 post_split_bytes = src_size - pre_split_bytes;
void const *pre_split_data = src;
void const *post_split_data = ((char *)src + pre_split_bytes);
DN_Memcpy(ring->base + offset, pre_split_data, pre_split_bytes);
DN_Memcpy(ring->base, post_split_data, post_split_bytes);
ring->write_pos += src_size;
}
DN_API void DN_Ring_Read(DN_Ring *ring, void *dest, DN_U64 dest_size)
{
DN_Assert(dest_size <= ring->size);
DN_U64 offset = ring->read_pos % ring->size;
DN_U64 bytes_before_split = ring->size - offset;
DN_U64 pre_split_bytes = DN_Min(bytes_before_split, dest_size);
DN_U64 post_split_bytes = dest_size - pre_split_bytes;
DN_Memcpy(dest, ring->base + offset, pre_split_bytes);
DN_Memcpy((char *)dest + pre_split_bytes, ring->base, post_split_bytes);
ring->read_pos += dest_size;
}
#define DN_Ring_WriteStruct(ring, item) DN_Ring_Write((ring), (item), sizeof(*(item)))
// NOTE: DN_CArray /////////////////////////////////////////////////////////////////////////////////
template <typename T>
DN_ArrayEraseResult DN_CArray_EraseRange(T *data, DN_USize *size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase)
@ -936,7 +980,7 @@ template <typename T, size_t N>
DN_List<T> DN_List_InitCArray(DN_Arena *arena, DN_USize chunk_size, T const (&array)[N])
{
DN_List<T> result = DN_List_Init<T>(arena, chunk_size);
DN_ForIndexU(index, N)
for (DN_ForIndexU(index, N))
DN_List_Add(&result, array[index]);
return result;
}
@ -945,7 +989,7 @@ template <typename T>
DN_List<T> DN_List_InitSliceCopy(DN_Arena *arena, DN_USize chunk_size, DN_Slice<T> slice)
{
DN_List<T> result = DN_List_Init<T>(arena, chunk_size);
DN_ForIndexU(index, slice.size)
for (DN_ForIndexU(index, slice.size))
DN_List_Add(&result, slice.data[index]);
return result;
}

View File

@ -1,6 +1,16 @@
#if !defined(DN_CONTAINERS_H)
#define DN_CONTAINERS_H
#include "../dn_base_inc.h"
struct DN_Ring
{
DN_U64 size;
char *base;
DN_U64 write_pos;
DN_U64 read_pos;
};
// NOTE: DN_CArray /////////////////////////////////////////////////////////////////////////////////
enum DN_ArrayErase
{
@ -149,6 +159,11 @@ template <typename T> struct DN_List
};
#endif // !defined(DN_NO_LIST)
DN_API bool DN_Ring_HasSpace (DN_Ring const *ring, DN_U64 size);
DN_API bool DN_Ring_HasData (DN_Ring const *ring, DN_U64 size);
DN_API void DN_Ring_Write (DN_Ring *ring, void const *src, DN_U64 src_size);
DN_API void DN_Ring_Read (DN_Ring *ring, void *dest, DN_U64 dest_size);
template <typename T> DN_ArrayEraseResult DN_CArray_EraseRange (T *data, DN_USize *size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
template <typename T> T * DN_CArray_MakeArray (T *data, DN_USize *size, DN_USize max, DN_USize count, DN_ZeroMem zero_mem);
template <typename T> T * DN_CArray_InsertArray (T *data, DN_USize *size, DN_USize max, DN_USize index, T const *items, DN_USize count);

View File

@ -151,7 +151,7 @@ DN_API DN_Str8 DN_CVT_F64ToAge(DN_Arena *arena, DN_F64 age_s, DN_CVTU64AgeUnit u
if (!arena)
return result;
char buffer[128];
char buffer[256];
DN_Arena stack_arena = DN_Arena_InitFromBuffer(buffer, sizeof(buffer), DN_ArenaFlags_NoPoison);
DN_Str8Builder builder = DN_Str8Builder_Init(&stack_arena);
DN_F64 remainder = age_s;

View File

@ -60,12 +60,14 @@ DN_API DN_LOGPrefixSize DN_LOG_MakePrefix(DN_LOGStyle style, DN_LOGTypeParam typ
DN_GCC_WARNING_PUSH
DN_GCC_WARNING_DISABLE(-Wformat)
DN_GCC_WARNING_DISABLE(-Wformat-extra-args)
DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(4477)
int size = DN_SNPrintF(dest,
DN_CAST(int)dest_size,
"%04u-%02u-%02uT%02u:%02u:%02u" // date
"%S" // colour
"%S" // bold
"%S" // type
" %S" // type
"%.*s" // type padding
"%S" // reset
" %S" // file name
@ -85,6 +87,7 @@ DN_API DN_LOGPrefixSize DN_LOG_MakePrefix(DN_LOGStyle style, DN_LOGTypeParam typ
reset_esc, // reset
file_name, // file name
call_site.line); // line number
DN_MSVC_WARNING_POP // '%S' requires an argument of type 'wchar_t *', but variadic argument 7 has type 'DN_Str8'
DN_GCC_WARNING_POP
static DN_USize max_header_length = 0;

View File

@ -336,13 +336,12 @@ DN_API bool DN_Arena_OwnsPtr(DN_Arena const *arena, void *ptr)
DN_API DN_ArenaStats DN_Arena_SumStatsArray(DN_ArenaStats const *array, DN_USize size)
{
DN_ArenaStats result = {};
DN_ForItSize(it, DN_ArenaStats const, array, size)
{
DN_ArenaStats stats = *it.data;
result.info.used += stats.info.used;
result.info.commit += stats.info.commit;
for (DN_ForItSize(it, DN_ArenaStats const, array, size)) {
DN_ArenaStats stats = *it.data;
result.info.used += stats.info.used;
result.info.commit += stats.info.commit;
result.info.reserve += stats.info.reserve;
result.info.blocks += stats.info.blocks;
result.info.blocks += stats.info.blocks;
result.hwm.used = DN_Max(result.hwm.used, result.info.used);
result.hwm.commit = DN_Max(result.hwm.commit, result.info.commit);

View File

@ -272,7 +272,7 @@ DN_API DN_Str8 DN_Str8_Segment(DN_Arena *arena, DN_Str8 src, DN_USize segment_si
DN_USize segment_counter = 0;
DN_Str8 result = DN_Str8_Alloc(arena, src.size + segments, DN_ZeroMem_Yes);
DN_USize write_index = 0;
DN_ForIndexU(src_index, src.size) {
for (DN_ForIndexU(src_index, src.size)) {
result.data[write_index++] = src.data[src_index];
if ((src_index + 1) % segment_size == 0 && segment_counter < segments) {
result.data[write_index++] = segment_char;
@ -664,7 +664,7 @@ DN_API DN_Str8DotTruncateResult DN_Str8_DotTruncateMiddle(DN_Arena *arena, DN_St
DN_API DN_Str8 DN_Str8_Lower(DN_Arena *arena, DN_Str8 string)
{
DN_Str8 result = DN_Str8_Copy(arena, string);
DN_ForIndexU(index, result.size)
for (DN_ForIndexU(index, result.size))
result.data[index] = DN_Char_ToLower(result.data[index]);
return result;
}
@ -672,7 +672,7 @@ DN_API DN_Str8 DN_Str8_Lower(DN_Arena *arena, DN_Str8 string)
DN_API DN_Str8 DN_Str8_Upper(DN_Arena *arena, DN_Str8 string)
{
DN_Str8 result = DN_Str8_Copy(arena, string);
DN_ForIndexU(index, result.size)
for (DN_ForIndexU(index, result.size))
result.data[index] = DN_Char_ToUpper(result.data[index]);
return result;
}
@ -774,7 +774,7 @@ DN_API bool DN_Str8Builder_AddArrayRef(DN_Str8Builder *builder, DN_Str8 const *s
return false;
if (add == DN_Str8BuilderAdd_Append) {
DN_ForIndexU(index, size) {
for (DN_ForIndexU(index, size)) {
DN_Str8 string = strings[index];
DN_Str8Link *link = links + index;
@ -821,8 +821,7 @@ DN_API bool DN_Str8Builder_AddArrayCopy(DN_Str8Builder *builder, DN_Str8 const *
DN_ArenaTempMem tmp_mem = DN_Arena_TempMemBegin(builder->arena);
bool result = true;
DN_Str8 *strings_copy = DN_Arena_NewArray(builder->arena, DN_Str8, size, DN_ZeroMem_No);
DN_ForIndexU(index, size)
{
for (DN_ForIndexU(index, size)) {
strings_copy[index] = DN_Str8_Copy(builder->arena, strings[index]);
if (strings_copy[index].size != strings[index].size) {
result = false;

View File

@ -1,30 +1,22 @@
#if !defined(DN_BASE_STRING_H)
#define DN_BASE_STRING_H
#if defined(DN_USE_STD_PRINTF)
#include <stdio.h>
#define DN_SPrintF(...) sprintf(__VA_ARGS__)
#define DN_SNPrintF(...) snprintf(__VA_ARGS__)
#define DN_VSPrintF(...) vsprintf(__VA_ARGS__)
#define DN_VSNPrintF(...) vsnprintf(__VA_ARGS__)
#else
#if !defined(DN_STB_SPRINTF_HEADER_ONLY)
#define STB_SPRINTF_IMPLEMENTATION
#define STB_SPRINTF_STATIC
#endif
DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(4505) // Unused function warning
DN_GCC_WARNING_PUSH
DN_GCC_WARNING_DISABLE(-Wunused-function)
#include "../External/stb_sprintf.h"
DN_GCC_WARNING_POP
DN_MSVC_WARNING_POP
#define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
#define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
#define DN_VSPrintF(...) STB_SPRINTF_DECORATE(vsprintf)(__VA_ARGS__)
#define DN_VSNPrintF(...) STB_SPRINTF_DECORATE(vsnprintf)(__VA_ARGS__)
#if !defined(DN_STB_SPRINTF_HEADER_ONLY)
#define STB_SPRINTF_IMPLEMENTATION
#define STB_SPRINTF_STATIC
#endif
DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(4505) // Unused function warning
DN_GCC_WARNING_PUSH
DN_GCC_WARNING_DISABLE(-Wunused-function)
#include "../External/stb_sprintf.h"
DN_GCC_WARNING_POP
DN_MSVC_WARNING_POP
#define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
#define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
#define DN_VSPrintF(...) STB_SPRINTF_DECORATE(vsprintf)(__VA_ARGS__)
#define DN_VSNPrintF(...) STB_SPRINTF_DECORATE(vsnprintf)(__VA_ARGS__)
/*
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -55,12 +55,12 @@ DN_API void DN_Core_Init(DN_Core *core, DN_CoreOnInit on_init)
DN_MSVC_WARNING_POP
DN_USize longest_feature_name = 0;
DN_ForIndexU(feature_index, DN_CPUFeature_Count) {
for (DN_ForIndexU(feature_index, DN_CPUFeature_Count)) {
DN_CPUFeatureDecl feature_decl = g_dn_cpu_feature_decl[feature_index];
longest_feature_name = DN_Max(longest_feature_name, feature_decl.label.size);
}
DN_ForIndexU(feature_index, DN_CPUFeature_Count) {
for (DN_ForIndexU(feature_index, DN_CPUFeature_Count)) {
DN_CPUFeatureDecl feature_decl = g_dn_cpu_feature_decl[feature_index];
bool has_feature = DN_CPU_HasFeature(report, feature_decl.value);
DN_Str8Builder_AppendF(&builder,

View File

@ -13,12 +13,13 @@ DN_API DN_StackTraceWalkResult DN_StackTrace_Walk(DN_Arena *arena, uint16_t limi
HANDLE thread = GetCurrentThread();
result.process = GetCurrentProcess();
if (!g_dn_os_core_->win32_sym_initialised) {
g_dn_os_core_->win32_sym_initialised = true;
DN_W32Core *w32 = DN_OS_GetW32Core_();
if (!w32->sym_initialised) {
w32->sym_initialised = true;
SymSetOptions(SYMOPT_LOAD_LINES);
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) {
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
DN_WinError error = DN_Win_LastError(tmem.arena);
DN_W32Error error = DN_W32_LastError(tmem.arena);
DN_LOG_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DN_STR_FMT(error.msg));
}
}
@ -177,8 +178,8 @@ DN_API DN_StackTraceFrame DN_StackTrace_RawFrameToFrame(DN_Arena *arena, DN_Stac
DN_StackTraceFrame result = {};
result.address = raw_frame.base_addr;
result.line_number = line.LineNumber;
result.file_name = DN_Win_Str16ToStr8(arena, file_name16);
result.function_name = DN_Win_Str16ToStr8(arena, function_name16);
result.file_name = DN_W32_Str16ToStr8(arena, file_name16);
result.function_name = DN_W32_Str16ToStr8(arena, function_name16);
if (!DN_Str8_HasData(result.function_name))
result.function_name = DN_STR8("<unknown function>");

View File

@ -403,10 +403,10 @@ void DN_Docs_Demo()
}
}
// NOTE: DN_LogProc ///////////////////////////////////////////////////////////////////////////
// NOTE: DN_LOGProc ///////////////////////////////////////////////////////////////////////////
//
// Function prototype of the logging interface exposed by this library. Logs
// emitted using the DN_Log_* family of functions are routed through this
// emitted using the DN_LOG_* family of functions are routed through this
// routine.
// NOTE: DN_FNV1A /////////////////////////////////////////////////////////////////////////////
@ -660,7 +660,7 @@ void DN_Docs_Demo()
// returned in add and multiply operations, and, the minimum is returned in
// subtraction and division.
// NOTE: DN_Safe_SaturateCast* ////////////////////////////////////////////////////////////////
// NOTE: DN_SaturateCast* ////////////////////////////////////////////////////////////////
//
// Truncate the passed in value to the return type clamping the resulting
// value to the max value of the desired data type. It DN_Check's the
@ -1175,31 +1175,31 @@ void DN_Docs_Demo()
DN_VArray_Deinit(&array);
}
// NOTE: DN_Win_LastError /////////////////////////////////////////////////////////////
// NOTE: DN_Win_ErrorCodeToMsg /////////////////////////////////////////////////////////////
// NOTE: DN_W32_LastError /////////////////////////////////////////////////////////////
// NOTE: DN_W32_ErrorCodeToMsg /////////////////////////////////////////////////////////////
#if defined(DN_PLATFORM_WIN32)
if (0) {
// Generate the error string for the last Win32 API called that return
// an error value.
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
DN_WinError get_last_error = DN_Win_LastError(tmem.arena);
DN_W32Error get_last_error = DN_W32_LastError(tmem.arena);
printf("Error (%lu): %.*s", get_last_error.code, DN_STR_FMT(get_last_error.msg));
// Alternatively, pass in the error code directly
DN_WinError error_msg_for_code = DN_Win_ErrorCodeToMsg(tmem.arena, /*error_code*/ 0);
DN_W32Error error_msg_for_code = DN_W32_ErrorCodeToMsg(tmem.arena, /*error_code*/ 0);
printf("Error (%lu): %.*s", error_msg_for_code.code, DN_STR_FMT(error_msg_for_code.msg));
}
// NOTE: DN_Win_MakeProcessDPIAware ///////////////////////////////////////////////////////////
// NOTE: DN_W32_MakeProcessDPIAware ///////////////////////////////////////////////////////////
//
// Call once at application start-up to ensure that the application is DPI
// aware on Windows and ensure that application UI is scaled up
// appropriately for the monitor.
// NOTE: DN_Win_Str8ToStr16 /////////////////////////////////////////////////////////////
// NOTE: DN_Win_Str8ToStr16Buffer /////////////////////////////////////////////////////////////
// NOTE: DN_Win_Str16ToStr8 /////////////////////////////////////////////////////////////
// NOTE: DN_Win_Str16ToStr8Buffer /////////////////////////////////////////////////////////////
// NOTE: DN_W32_Str8ToStr16 /////////////////////////////////////////////////////////////
// NOTE: DN_W32_Str8ToStr16Buffer /////////////////////////////////////////////////////////////
// NOTE: DN_W32_Str16ToStr8 /////////////////////////////////////////////////////////////
// NOTE: DN_W32_Str16ToStr8Buffer /////////////////////////////////////////////////////////////
//
// Convert a UTF8 <-> UTF16 string.
//
@ -1211,7 +1211,7 @@ void DN_Docs_Demo()
//
// Returns the number of u8's (for UTF16->8) OR u16's (for UTF8->16)
// written/required for conversion. 0 if there was a conversion error and can be
// queried using 'DN_Win_LastError'
// queried using 'DN_W32_LastError'
#endif
}

114
Extra/dn_async.cpp Normal file
View File

@ -0,0 +1,114 @@
#define DN_ASYNC_CPP
#include "../dn_base_inc.h"
#include "../dn_os_inc.h"
#include "dn_async.h"
static DN_I32 DN_ASYNC_ThreadEntryPoint_(DN_OSThread *thread)
{
DN_OS_ThreadSetName(DN_FStr8_ToStr8(&thread->name));
DN_ASYNCCore *async = DN_CAST(DN_ASYNCCore *) thread->user_context;
DN_Ring *ring = &async->ring;
for (;;) {
DN_OS_SemaphoreWait(&async->worker_sem, UINT32_MAX);
if (async->join_threads)
break;
DN_ASYNCJob job = {};
for (DN_OS_MutexScope(&async->ring_mutex)) {
if (DN_Ring_HasData(ring, sizeof(job))) {
DN_Ring_Read(ring, &job, sizeof(job));
break;
}
}
if (job.work.func) {
DN_OS_ConditionVariableBroadcast(&async->ring_write_cv); // Resume any blocked ring write(s)
DN_Atomic_AddU32(&async->busy_threads, 1);
job.work.func(job.work.input);
DN_Atomic_SubU32(&async->busy_threads, 1);
if (job.completion_sem.handle != 0)
DN_OS_SemaphoreIncrement(&job.completion_sem, 1);
}
}
return 0;
}
DN_API void DN_ASYNC_Init(DN_ASYNCCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size)
{
DN_Assert(async);
async->ring.size = base_size;
async->ring.base = base;
async->ring_mutex = DN_OS_MutexInit();
async->ring_write_cv = DN_OS_ConditionVariableInit();
async->worker_sem = DN_OS_SemaphoreInit(0);
async->thread_count = threads_size;
async->threads = threads;
for (DN_ForIndexU(index, async->thread_count)) {
DN_OSThread *thread = async->threads + index;
thread->name = DN_FStr8_InitF<64>("ASYNC W%zu", index);
DN_OS_ThreadInit(thread, DN_ASYNC_ThreadEntryPoint_, async);
}
}
DN_API void DN_ASYNC_Deinit(DN_ASYNCCore *async)
{
DN_Assert(async);
DN_Atomic_SetValue32(&async->join_threads, true);
DN_OS_SemaphoreIncrement(&async->worker_sem, async->thread_count);
for (DN_ForItSize(it, DN_OSThread, async->threads, async->thread_count))
DN_OS_ThreadDeinit(it.data);
}
static bool DN_ASYNC_QueueJob_(DN_ASYNCCore *async, DN_ASYNCJob const *job, DN_U64 wait_time_ms) {
DN_U64 end_time_ms = DN_OS_DateUnixTimeMs() + wait_time_ms;
bool result = false;
for (DN_OS_MutexScope(&async->ring_mutex)) {
for (;;) {
if (DN_Ring_HasSpace(&async->ring, sizeof(*job))) {
DN_Ring_WriteStruct(&async->ring, job);
result = true;
break;
}
DN_OS_ConditionVariableWaitUntil(&async->ring_write_cv, &async->ring_mutex, end_time_ms);
if (DN_OS_DateUnixTimeMs() >= end_time_ms)
break;
}
}
if (result)
DN_OS_SemaphoreIncrement(&async->worker_sem, 1); // Flag that a job is available
return result;
}
DN_API bool DN_ASYNC_QueueWork(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms)
{
DN_ASYNCJob job = {};
job.work.func = func;
job.work.input = input;
bool result = DN_ASYNC_QueueJob_(async, &job, wait_time_ms);
return result;
}
DN_API DN_OSSemaphore DN_ASYNC_QueueTask(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms)
{
DN_OSSemaphore result = DN_OS_SemaphoreInit(0);
DN_ASYNCJob job = {};
job.work.func = func;
job.work.input = input;
job.completion_sem = result;
DN_ASYNC_QueueJob_(async, &job, wait_time_ms);
return result;
}
DN_API void DN_ASYNC_WaitTask(DN_OSSemaphore *sem, DN_U32 timeout_ms)
{
DN_OS_SemaphoreWait(sem, timeout_ms);
DN_OS_SemaphoreDeinit(sem);
}

52
Extra/dn_async.h Normal file
View File

@ -0,0 +1,52 @@
#if !defined(DN_ASYNC_H)
#define DN_ASYNC_H
#include "../dn_base_inc.h"
#include "../dn_os_inc.h"
enum DN_ASYNCPriority
{
DN_ASYNCPriority_Low,
DN_ASYNCPriority_High,
DN_ASYNCPriority_Count,
};
struct DN_ASYNCCore
{
DN_OSMutex ring_mutex;
DN_OSConditionVariable ring_write_cv;
DN_OSSemaphore worker_sem;
DN_Ring ring;
DN_OSThread *threads;
DN_U32 thread_count;
DN_U32 busy_threads;
DN_U32 join_threads;
};
typedef void(DN_ASYNCWorkFunc)(void *input);
struct DN_ASYNCWork
{
DN_ASYNCWorkFunc *func;
void *input;
void *output;
};
struct DN_ASYNCJob
{
DN_ASYNCWork work;
DN_OSSemaphore completion_sem;
};
struct DN_ASYNCTask
{
DN_ASYNCWork work;
};
DN_API void DN_ASYNC_Init (DN_ASYNCCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size);
DN_API void DN_ASYNC_Deinit (DN_ASYNCCore *async);
DN_API bool DN_ASYNC_QueueWork(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms);
DN_API DN_OSSemaphore DN_ASYNC_QueueTask(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms);
DN_API void DN_ASYNC_WaitTask (DN_OSSemaphore *sem, DN_U32 timeout_ms);
#endif // DN_ASYNC_H

View File

@ -142,22 +142,21 @@ DN_API void DN_PCG32_Advance (DN_PCG32 *rng
#if !defined(DN_NO_JSON_BUILDER)
// NOTE: DN_JSONBuilder ////////////////////////////////////////////////////////////////////////////
#define DN_JSONBuilder_Object(builder) \
DN_DEFER_LOOP(DN_JSONBuilder_ObjectBegin(builder), \
DN_JSONBuilder_ObjectEnd(builder))
#define DN_JSONBuilder_Object(builder) \
DN_DeferLoop(DN_JSONBuilder_ObjectBegin(builder), \
DN_JSONBuilder_ObjectEnd(builder))
#define DN_JSONBuilder_ObjectNamed(builder, name) \
DN_DEFER_LOOP(DN_JSONBuilder_ObjectBeginNamed(builder, name), \
DN_JSONBuilder_ObjectEnd(builder))
#define DN_JSONBuilder_ObjectNamed(builder, name) \
DN_DeferLoop(DN_JSONBuilder_ObjectBeginNamed(builder, name), \
DN_JSONBuilder_ObjectEnd(builder))
#define DN_JSONBuilder_Array(builder) \
DN_DEFER_LOOP(DN_JSONBuilder_ArrayBegin(builder), \
DN_JSONBuilder_ArrayEnd(builder))
#define DN_JSONBuilder_ArrayNamed(builder, name) \
DN_DEFER_LOOP(DN_JSONBuilder_ArrayBeginNamed(builder, name), \
DN_JSONBuilder_ArrayEnd(builder))
#define DN_JSONBuilder_Array(builder) \
DN_DeferLoop(DN_JSONBuilder_ArrayBegin(builder), \
DN_JSONBuilder_ArrayEnd(builder))
#define DN_JSONBuilder_ArrayNamed(builder, name) \
DN_DeferLoop(DN_JSONBuilder_ArrayBeginNamed(builder, name), \
DN_JSONBuilder_ArrayEnd(builder))
DN_API DN_JSONBuilder DN_JSONBuilder_Init (DN_Arena *arena, int spaces_per_indent);
DN_API DN_Str8 DN_JSONBuilder_Build (DN_JSONBuilder const *builder, DN_Arena *arena);

View File

@ -407,22 +407,22 @@ void DN_JSON_ItErrorUnknownKeyValue_(DN_JSONIt *it, DN_CallSite call_site)
json_string_s const *key = curr->name;
if (it->flags & json_parse_flags_allow_location_information) {
json_string_ex_s const *info = DN_CAST(json_string_ex_s const *)key;
DN_Log_TypeFCallSite(DN_LogType_Warning,
call_site,
"Unknown key-value pair in object [loc=%zu:%zu, key=%.*s, value=%.*s]",
info->line_no,
info->row_no,
DN_CAST(int)key->string_size,
key->string,
DN_CAST(int)value_type_size,
value_type);
DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Warning),
call_site,
"Unknown key-value pair in object [loc=%zu:%zu, key=%.*s, value=%.*s]",
info->line_no,
info->row_no,
DN_CAST(int) key->string_size,
key->string,
DN_CAST(int) value_type_size,
value_type);
} else {
DN_Log_TypeFCallSite(DN_LogType_Warning,
call_site,
"Unknown key-value pair in object [key=%.*s, value=%.*s]",
DN_CAST(int)key->string_size,
key->string,
DN_CAST(int)value_type_size,
value_type);
DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Warning),
call_site,
"Unknown key-value pair in object [key=%.*s, value=%.*s]",
DN_CAST(int) key->string_size,
key->string,
DN_CAST(int) value_type_size,
value_type);
}
}

View File

@ -1151,8 +1151,8 @@ static DN_UTCore DN_Tests_Intrinsics()
DN_UT_Test(&result, "DN_Atomic_SetValue32")
{
long a = 0;
long b = 111;
DN_U32 a = 0;
DN_U32 b = 111;
DN_Atomic_SetValue32(&a, b);
DN_UT_AssertF(&result, a == b, "a: %ld, b: %ld", a, b);
}
@ -1574,6 +1574,8 @@ static DN_UTCore DN_Tests_M4()
static DN_UTCore DN_Tests_OS()
{
DN_UTCore result = DN_UT_Init();
#if defined(DN_OS_INC_CPP) || 1
DN_UT_LogF(&result, "DN_OS\n");
{
DN_UT_Test(&result, "Generate secure RNG bytes with nullptr")
@ -1690,6 +1692,58 @@ static DN_UTCore DN_Tests_OS()
}
}
DN_UT_LogF(&result, "\nSemaphore\n");
{
DN_OSSemaphore sem = DN_OS_SemaphoreInit(0);
DN_UT_Test(&result, "Wait timeout")
{
DN_U64 begin = DN_OS_PerfCounterNow();
DN_OSSemaphoreWaitResult wait_result = DN_OS_SemaphoreWait(&sem, 100 /*timeout_ms*/);
DN_U64 end = DN_OS_PerfCounterNow();
DN_UT_AssertF(&result, wait_result == DN_OSSemaphoreWaitResult_Timeout, "Received wait result %zu", wait_result);
DN_F64 elapsed_ms = DN_OS_PerfCounterMs(begin, end);
DN_UT_AssertF(&result, elapsed_ms >= 100, "Expected to sleep for >= 100ms, slept %f ms", elapsed_ms);
}
DN_UT_Test(&result, "Wait success")
{
DN_OS_SemaphoreIncrement(&sem, 1);
DN_OSSemaphoreWaitResult wait_result = DN_OS_SemaphoreWait(&sem, 0 /*timeout_ms*/);
DN_UT_AssertF(&result, wait_result == DN_OSSemaphoreWaitResult_Success, "Received wait result %zu", wait_result);
}
DN_OS_SemaphoreDeinit(&sem);
}
DN_UT_LogF(&result, "\nMutex\n");
{
DN_OSMutex mutex = DN_OS_MutexInit();
DN_UT_Test(&result, "Lock")
{
DN_OS_MutexLock(&mutex);
DN_OS_MutexUnlock(&mutex);
}
DN_OS_MutexDeinit(&mutex);
}
DN_UT_LogF(&result, "\nCondition Variable\n");
{
DN_OSMutex mutex = DN_OS_MutexInit();
DN_OSConditionVariable cv = DN_OS_ConditionVariableInit();
DN_UT_Test(&result, "Lock and timeout")
{
DN_U64 begin = DN_OS_PerfCounterNow();
DN_OS_ConditionVariableWait(&cv, &mutex, 100 /*sleep_ms*/);
DN_U64 end = DN_OS_PerfCounterNow();
DN_F64 elapsed_ms = DN_OS_PerfCounterMs(begin, end);
DN_UT_AssertF(&result, elapsed_ms >= 100, "Expected to sleep for >= 100ms, slept %f ms", elapsed_ms);
}
DN_OS_MutexDeinit(&mutex);
DN_OS_ConditionVariableDeinit(&cv);
}
#endif
return result;
}
@ -2372,25 +2426,25 @@ static DN_UTCore DN_Tests_Win()
DN_UT_Test(&result, "Str8 to Str16")
{
DN_Str16 str_result = DN_Win_Str8ToStr16(tmem.arena, input8);
DN_Str16 str_result = DN_W32_Str8ToStr16(tmem.arena, input8);
DN_UT_Assert(&result, str_result == input16);
}
DN_UT_Test(&result, "Str16 to Str8")
{
DN_Str8 str_result = DN_Win_Str16ToStr8(tmem.arena, input16);
DN_Str8 str_result = DN_W32_Str16ToStr8(tmem.arena, input16);
DN_UT_Assert(&result, str_result == input8);
}
DN_UT_Test(&result, "Str16 to Str8: Null terminates string")
{
int size_required = DN_Win_Str16ToStr8Buffer(input16, nullptr, 0);
int size_required = DN_W32_Str16ToStr8Buffer(input16, nullptr, 0);
char *string = DN_Arena_NewArray(tmem.arena, char, size_required + 1, DN_ZeroMem_No);
// Fill the string with error sentinels
DN_Memset(string, 'Z', size_required + 1);
int size_returned = DN_Win_Str16ToStr8Buffer(input16, string, size_required + 1);
int size_returned = DN_W32_Str16ToStr8Buffer(input16, string, size_required + 1);
char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0};
DN_UT_AssertF(&result, size_required == size_returned, "string_size: %d, result: %d", size_required, size_returned);
@ -2400,8 +2454,8 @@ static DN_UTCore DN_Tests_Win()
DN_UT_Test(&result, "Str16 to Str8: Arena null terminates string")
{
DN_Str8 string8 = DN_Win_Str16ToStr8(tmem.arena, input16);
int size_returned = DN_Win_Str16ToStr8Buffer(input16, nullptr, 0);
DN_Str8 string8 = DN_W32_Str16ToStr8(tmem.arena, input16);
int size_returned = DN_W32_Str16ToStr8Buffer(input16, nullptr, 0);
char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0};
DN_UT_AssertF(&result, DN_CAST(int) string8.size == size_returned, "string_size: %d, result: %d", DN_CAST(int) string8.size, size_returned);

View File

@ -1,5 +1,10 @@
#define DN_OS_CPP
#if defined(DN_PLATFORM_POSIX)
#include <sys/sysinfo.h> // get_nprocs
#include <unistd.h> // getpagesize
#endif
static DN_OSCore *g_dn_os_core_;
static void DN_OS_LOGEmitFromTypeTypeFV_(DN_LOGTypeParam type, void *user_data, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args)
@ -86,34 +91,46 @@ DN_API void DN_OS_Init(DN_OSCore *os, DN_OSInitArgs *args)
#if defined(DN_PLATFORM_WIN32)
SYSTEM_INFO system_info = {};
GetSystemInfo(&system_info);
os->page_size = system_info.dwPageSize;
os->alloc_granularity = system_info.dwAllocationGranularity;
QueryPerformanceFrequency(&os->win32_qpc_frequency);
HMODULE module = LoadLibraryA("kernel32.dll");
os->win32_set_thread_description = DN_CAST(DN_WinSetThreadDescriptionFunc *) GetProcAddress(module, "SetThreadDescription");
FreeLibrary(module);
os->logical_processor_count = system_info.dwNumberOfProcessors;
os->page_size = system_info.dwPageSize;
os->alloc_granularity = system_info.dwAllocationGranularity;
#else
// TODO(doyle): Get the proper page size from the OS.
os->page_size = DN_Kilobytes(4);
os->alloc_granularity = DN_Kilobytes(64);
os->logical_processor_count = get_nprocs();
os->page_size = getpagesize();
os->alloc_granularity = os->page_size;
#endif
}
// NOTE: Setup logging
DN_OS_EmitLogsWithOSPrintFunctions(os);
#if defined(DN_PLATFORM_WIN32)
// NOTE: win32 bcrypt
{
os->arena = DN_Arena_InitFromOSVMem(DN_Megabytes(1), DN_Kilobytes(4), DN_ArenaFlags_NoAllocTrack);
#if defined(DN_PLATFORM_WIN32)
os->platform_context = DN_Arena_New(&os->arena, DN_W32Core, DN_ZeroMem_Yes);
#elif defined(DN_PLATFORM_POSIX)
os->platform_context = DN_Arena_New(&os->arena, DN_POSIXCore, DN_ZeroMem_Yes);
#endif
#if defined(DN_PLATFORM_WIN32)
DN_W32Core *w32 = DN_CAST(DN_W32Core *) os->platform_context;
InitializeCriticalSection(&w32->sync_primitive_free_list_mutex);
QueryPerformanceFrequency(&w32->qpc_frequency);
HMODULE module = LoadLibraryA("kernel32.dll");
w32->set_thread_description = DN_CAST(DN_W32SetThreadDescriptionFunc *) GetProcAddress(module, "SetThreadDescription");
FreeLibrary(module);
// NOTE: win32 bcrypt
wchar_t const BCRYPT_ALGORITHM[] = L"RNG";
long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider(&os->win32_bcrypt_rng_handle, BCRYPT_ALGORITHM, nullptr /*implementation*/, 0 /*flags*/);
if (os->win32_bcrypt_rng_handle && init_status == 0)
os->win32_bcrypt_init_success = true;
long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider(&w32->bcrypt_rng_handle, BCRYPT_ALGORITHM, nullptr /*implementation*/, 0 /*flags*/);
if (w32->bcrypt_rng_handle && init_status == 0)
w32->bcrypt_init_success = true;
else
DN_LOG_ErrorF("Failed to initialise Windows secure random number generator, error: %d", init_status);
#endif
}
#endif
// NOTE: Initialise tmem arenas which allocate memory and will be
// recorded to the now initialised allocation table. The initialisation
@ -135,7 +152,6 @@ DN_API void DN_OS_Init(DN_OSCore *os, DN_OSInitArgs *args)
#define DN_CPU_FEAT_XENTRY(label) g_dn_cpu_feature_decl[DN_CPUFeature_##label] = {DN_CPUFeature_##label, DN_STR8(#label)};
DN_CPU_FEAT_XMACRO
#undef DN_CPU_FEAT_XENTRY
DN_Assert(g_dn_os_core_);
}
@ -702,13 +718,13 @@ static void DN_OS_ThreadExecute_(void *user_context)
DN_API void DN_OS_ThreadSetName(DN_Str8 name)
{
DN_OSTLS *tls = DN_OS_TLSGet();
DN_OSTLS *tls = DN_OS_TLSGet();
tls->name_size = DN_CAST(uint8_t) DN_Min(name.size, sizeof(tls->name) - 1);
DN_Memcpy(tls->name, name.data, tls->name_size);
tls->name[tls->name_size] = 0;
#if defined(DN_PLATFORM_WIN32)
DN_Win_ThreadSetName(name);
DN_W32_ThreadSetName(name);
#else
DN_Posix_ThreadSetName(name);
#endif
@ -717,7 +733,7 @@ DN_API void DN_OS_ThreadSetName(DN_Str8 name)
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
DN_API void DN_OS_HttpRequestWait(DN_OSHttpResponse *response)
{
if (response && DN_OS_SemaphoreIsValid(&response->on_complete_semaphore))
if (response && response->on_complete_semaphore.handle != 0)
DN_OS_SemaphoreWait(&response->on_complete_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
}

View File

@ -1,11 +1,13 @@
#if !defined(DN_OS_H)
#define DN_OS_H
#include <new> // operator new
#if defined(DN_PLATFORM_EMSCRIPTEN) || defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_ARM64)
#include "dn_os_posix.h"
#elif defined(DN_PLATFORM_WIN32)
#include "dn_os_windows.h"
#include "dn_os_win32.h"
#include "dn_os_w32.h"
#else
#error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
#endif
@ -38,10 +40,6 @@
#include <emscripten/fetch.h> // emscripten_fetch (for DN_OSHttpResponse)
#endif
#if defined(DN_PLATFORM_WIN32)
typedef HRESULT DN_WinSetThreadDescriptionFunc(HANDLE hThread, PWSTR const lpThreadDescription);
#endif
// NOTE: DN_OSDate /////////////////////////////////////////////////////////////////////////////////
struct DN_OSDateTimeStr8
{
@ -178,11 +176,11 @@ struct DN_OSExecAsyncHandle
struct DN_OSExecResult
{
bool finished;
DN_Str8 stdout_text;
DN_Str8 stderr_text;
DN_U32 os_error_code;
DN_U32 exit_code;
bool finished;
DN_Str8 stdout_text;
DN_Str8 stderr_text;
DN_U32 os_error_code;
DN_U32 exit_code;
};
struct DN_OSExecArgs
@ -192,18 +190,12 @@ struct DN_OSExecArgs
DN_Slice<DN_Str8> environment;
};
#if !defined(DN_NO_SEMAPHORE)
// NOTE: DN_OSSemaphore ////////////////////////////////////////////////////////////////////////////
DN_U32 const DN_OS_SEMAPHORE_INFINITE_TIMEOUT = UINT32_MAX;
struct DN_OSSemaphore
{
#if defined(DN_OS_WIN32) && !defined(DN_OS_WIN32_USE_PTHREADS)
void *win32_handle;
#else
sem_t posix_handle;
bool posix_init;
#endif
DN_U64 handle;
};
enum DN_OSSemaphoreWaitResult
@ -212,35 +204,31 @@ enum DN_OSSemaphoreWaitResult
DN_OSSemaphoreWaitResult_Success,
DN_OSSemaphoreWaitResult_Timeout,
};
#endif // !defined(DN_NO_SEMAPHORE)
// NOTE: DN_OSMutex ////////////////////////////////////////////////////////////////////////////////
struct DN_OSMutex
{
#if defined(DN_OS_WIN32) && !defined(DN_OS_WIN32_USE_PTHREADS)
char win32_handle[48];
#else
pthread_mutex_t posix_handle;
pthread_mutexattr_t posix_attribs;
#endif
DN_U64 handle;
};
struct DN_OSConditionVariable
{
DN_U64 handle;
};
// NOTE: DN_OSThread ///////////////////////////////////////////////////////////////////////////////
#if !defined(DN_NO_THREAD) && !defined(DN_NO_SEMAPHORE)
typedef int32_t(DN_OSThreadFunc)(struct DN_OSThread *);
typedef DN_I32(DN_OSThreadFunc)(struct DN_OSThread *);
struct DN_OSThread
{
DN_FStr8<64> name;
DN_OSTLS tls;
DN_OSTLSInitArgs tls_init_args;
DN_OSTLS tls;
DN_OSTLSInitArgs tls_init_args;
void *handle;
DN_U64 thread_id;
void *user_context;
DN_OSThreadFunc *func;
DN_OSSemaphore init_semaphore;
};
#endif // !defined(DN_NO_THREAD)
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
enum DN_OSHttpRequestSecure
@ -272,9 +260,9 @@ struct DN_OSHttpResponse
#if defined(DN_PLATFORM_EMSCRIPTEN)
emscripten_fetch_t *em_handle;
#elif defined(DN_PLATFORM_WIN32)
HINTERNET win32_request_session;
HINTERNET win32_request_connection;
HINTERNET win32_request_handle;
HINTERNET w32_request_session;
HINTERNET w32_request_connection;
HINTERNET w32_request_handle;
#endif
};
@ -300,6 +288,7 @@ struct DN_OSCore
bool log_no_colour; // Disable colours in the logging output
// NOTE: OS //////////////////////////////////////////////////////////////////////////////////////
DN_U32 logical_processor_count;
DN_U32 page_size;
DN_U32 alloc_granularity;
@ -312,14 +301,8 @@ struct DN_OSCore
DN_U64 mem_allocs_total;
DN_U64 mem_allocs_frame; // Total OS heap allocs since the last 'DN_Core_FrameBegin' was invoked
// NOTE: Win32 /////////////////////////////////////////////////////////////////////////////////
#if defined(DN_PLATFORM_WIN32)
DN_WinSetThreadDescriptionFunc *win32_set_thread_description;
LARGE_INTEGER win32_qpc_frequency;
void * win32_bcrypt_rng_handle;
bool win32_bcrypt_init_success;
bool win32_sym_initialised;
#endif
DN_Arena arena;
void *platform_context;
};
struct DN_OSDiskSpace
@ -349,6 +332,8 @@ DN_API DN_OSDateTime DN_OS_DateLocalTimeNow ();
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8Now(char date_separator = '-', char hms_separator = ':');
DN_API DN_OSDateTimeStr8 DN_OS_DateLocalTimeStr8 (DN_OSDateTime time, char date_separator = '-', char hms_separator = ':');
DN_API DN_U64 DN_OS_DateUnixTimeNs ();
#define DN_OS_DateUnixTimeUs() (DN_OS_DateUnixTimeNs() / 1000)
#define DN_OS_DateUnixTimeMs() (DN_OS_DateUnixTimeNs() / 1000 * 1000)
DN_API DN_U64 DN_OS_DateUnixTimeS ();
DN_API DN_OSDateTime DN_OS_DateUnixTimeSToDate (DN_U64 time);
DN_API DN_U64 DN_OS_DateLocalToUnixTimeS(DN_OSDateTime date);
@ -451,31 +436,30 @@ DN_API DN_OSExecResult DN_OS_Exec (DN_Slice<DN_Str8> c
DN_API DN_OSExecResult DN_OS_ExecOrAbort (DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_Arena *arena);
#define DN_OS_ExecOrAbortFromTLS(...) DN_OS_ExecOrAbort(__VA_ARGS__, DN_OS_TLSTopArena())
// NOTE: DN_OSSemaphore ////////////////////////////////////////////////////////////////////////////
#if !defined(DN_NO_SEMAPHORE)
DN_API DN_OSSemaphore DN_OS_SemaphoreInit (DN_U32 initial_count);
DN_API bool DN_OS_SemaphoreIsValid (DN_OSSemaphore *semaphore);
DN_API void DN_OS_SemaphoreDeinit (DN_OSSemaphore *semaphore);
DN_API void DN_OS_SemaphoreIncrement(DN_OSSemaphore *semaphore, DN_U32 amount);
DN_API DN_OSSemaphoreWaitResult DN_OS_SemaphoreWait (DN_OSSemaphore *semaphore, DN_U32 timeout_ms);
#endif // !defined(DN_NO_SEMAPHORE)
// NOTE: DN_OSMutex ////////////////////////////////////////////////////////////////////////////////
DN_API DN_OSMutex DN_OS_MutexInit ();
DN_API void DN_OS_MutexDeinit(DN_OSMutex *mutex);
DN_API void DN_OS_MutexLock (DN_OSMutex *mutex);
DN_API void DN_OS_MutexUnlock(DN_OSMutex *mutex);
#define DN_OS_Mutex(mutex) DN_DEFER_LOOP(DN_OS_MutexLock(mutex), DN_OS_MutexUnlock(mutex))
#define DN_OS_MutexScope(mutex) DN_DeferLoop(DN_OS_MutexLock(mutex), DN_OS_MutexUnlock(mutex))
DN_API DN_OSConditionVariable DN_OS_ConditionVariableInit ();
DN_API void DN_OS_ConditionVariableDeinit (DN_OSConditionVariable *cv);
DN_API bool DN_OS_ConditionVariableWait (DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 sleep_ms);
DN_API bool DN_OS_ConditionVariableWaitUntil(DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 end_ts_ms);
DN_API void DN_OS_ConditionVariableSignal (DN_OSConditionVariable *cv);
DN_API void DN_OS_ConditionVariableBroadcast(DN_OSConditionVariable *cv);
// NOTE: DN_OSThread ///////////////////////////////////////////////////////////////////////////////
#if !defined(DN_NO_THREAD) && !defined(DN_NO_SEMAPHORE)
DN_API bool DN_OS_ThreadInit (DN_OSThread *thread, DN_OSThreadFunc *func, void *user_context);
DN_API void DN_OS_ThreadDeinit(DN_OSThread *thread);
DN_API DN_U32 DN_OS_ThreadID ();
DN_API void DN_OS_ThreadSetName(DN_Str8 name);
#endif // !defined(DN_NO_THREAD)
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response, DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
DN_API void DN_OS_HttpRequestWait (DN_OSHttpResponse *response);
DN_API void DN_OS_HttpRequestFree (DN_OSHttpResponse *response);

View File

@ -4,11 +4,11 @@
#include <sys/statvfs.h>
// NOTE: DN_OSMem //////////////////////////////////////////////////////////////////////////////////
static uint32_t DN_OS_MemConvertPageToOSFlags_(uint32_t protect)
static DN_U32 DN_OS_MemConvertPageToOSFlags_(DN_U32 protect)
{
DN_Assert((protect & ~DN_MemPage_All) == 0);
DN_Assert(protect != 0);
uint32_t result = 0;
DN_U32 result = 0;
if (protect & (DN_MemPage_NoAccess | DN_MemPage_Guard)) {
result = PROT_NONE;
@ -21,7 +21,7 @@ static uint32_t DN_OS_MemConvertPageToOSFlags_(uint32_t protect)
return result;
}
DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, uint32_t page_flags)
DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_flags)
{
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
@ -36,7 +36,7 @@ DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, uint32_t page_
return result;
}
DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, uint32_t page_flags)
DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags)
{
bool result = false;
if (!ptr || size == 0)
@ -60,7 +60,7 @@ DN_API void DN_OS_MemRelease(void *ptr, DN_USize size)
munmap(ptr, size);
}
DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, uint32_t page_flags)
DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags)
{
if (!ptr || size == 0)
return 0;
@ -162,7 +162,7 @@ DN_API DN_OSDateTime DN_OS_DateUnixTimeSToDate(uint64_t time)
return result;
}
DN_API bool DN_OS_SecureRNGBytes(void *buffer, uint32_t size)
DN_API bool DN_OS_SecureRNGBytes(void *buffer, DN_U32 size)
{
#if defined(DN_PLATFORM_EMSCRIPTEN)
(void)buffer;
@ -182,7 +182,7 @@ DN_API bool DN_OS_SecureRNGBytes(void *buffer, uint32_t size)
// TODO(doyle):
// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c
// TODO(doyle): https://man7.org/linux/man-pages/man2/getrandom.2.html
uint32_t read_bytes = 0;
DN_U32 read_bytes = 0;
do {
read_bytes =
getrandom(buffer, size, 0); // NOTE: EINTR can not be triggered if size <= 32 bytes
@ -211,7 +211,7 @@ DN_API DN_OSDiskSpace DN_OS_DiskSpace(DN_Str8 path)
return result;
result.success = true;
result.free = info.f_bavail * info.f_frsize;
result.avail = info.f_bavail * info.f_frsize;
result.size = info.f_blocks * info.f_frsize;
return result;
}
@ -275,7 +275,7 @@ DN_API void DN_OS_SleepMs(DN_UInt milliseconds)
{
struct timespec ts;
ts.tv_sec = milliseconds / 1000;
ts.tv_nsec = (milliseconds % 1000) * 1000000; // Convert remaining milliseconds to nanoseconds
ts.tv_nsec = (milliseconds % 1000) * 1'000'000; // Convert remaining milliseconds to nanoseconds
// nanosleep can fail if interrupted by a signal, so we loop until the full sleep time has passed
while (nanosleep(&ts, &ts) == -1 && errno == EINTR)
;
@ -916,8 +916,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Slice<DN_Str8> cmd_line,
return result;
}
DN_ForIndexU(arg_index, cmd_line.size)
{
for (DN_ForIndexU(arg_index, cmd_line.size)) {
DN_Str8 arg = cmd_line.data[arg_index];
argv[arg_index] = DN_Str8_Copy(tmem.arena, arg).data; // NOTE: Copy string to guarantee it is null-terminated
}
@ -990,7 +989,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle,
size_t *stdout_size,
char *stderr_buffer,
size_t *stderr_size,
uint32_t timeout_ms,
DN_U32 timeout_ms,
DN_OSErrSink *err)
{
DN_InvalidCodePath;
@ -998,75 +997,111 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle,
return result;
}
#if !defined(DN_NO_SEMAPHORE)
// NOTE: DN_OSSemaphore ////////////////////////////////////////////////////////////////////////////
DN_API DN_OSSemaphore DN_OS_SemaphoreInit(uint32_t initial_count)
static DN_POSIXCore *DN_OS_GetPOSIXCore_()
{
DN_OSSemaphore result = {};
int pshared = 0; // Share the semaphore across all threads in the process
if (sem_init(&result.posix_handle, pshared, initial_count) == 0)
result.posix_init = true;
DN_Assert(g_dn_os_core_ && g_dn_os_core_->platform_context);
DN_POSIXCore *result = DN_CAST(DN_POSIXCore *)g_dn_os_core_->platform_context;
return result;
}
DN_API bool DN_OS_SemaphoreIsValid(DN_OSSemaphore *semaphore)
static DN_POSIXSyncPrimitive *DN_OS_U64ToPOSIXSyncPrimitive_(DN_U64 u64)
{
bool result = false;
if (semaphore)
result = semaphore->posix_init;
DN_POSIXSyncPrimitive *result = nullptr;
DN_Memcpy(&result, &u64, sizeof(u64));
return result;
}
static DN_U64 DN_POSIX_SyncPrimitiveToU64(DN_POSIXSyncPrimitive *primitive)
{
DN_U64 result = 0;
static_assert(sizeof(result) == sizeof(primitive), "Pointer size mis-match");
DN_Memcpy(&result, &primitive, sizeof(result));
return result;
}
static DN_POSIXSyncPrimitive *DN_POSIX_AllocSyncPrimitive_()
{
DN_POSIXCore *posix = DN_OS_GetPOSIXCore_();
DN_POSIXSyncPrimitive *result = nullptr;
pthread_mutex_lock(&posix->sync_primitive_free_list_mutex);
{
if (posix->sync_primitive_free_list) {
result = posix->sync_primitive_free_list;
posix->sync_primitive_free_list = posix->sync_primitive_free_list->next;
result->next = nullptr;
} else {
DN_OSCore *os = g_dn_os_core_;
result = DN_Arena_New(&os->arena, DN_POSIXSyncPrimitive, DN_ZeroMem_Yes);
}
}
pthread_mutex_unlock(&posix->sync_primitive_free_list_mutex);
return result;
}
static void DN_POSIX_DeallocSyncPrimitive_(DN_POSIXSyncPrimitive *primitive)
{
if (primitive) {
DN_POSIXCore *posix = DN_OS_GetPOSIXCore_();
pthread_mutex_lock(&posix->sync_primitive_free_list_mutex);
primitive->next = posix->sync_primitive_free_list;
posix->sync_primitive_free_list = primitive;
pthread_mutex_unlock(&posix->sync_primitive_free_list_mutex);
}
}
// NOTE: DN_OSSemaphore ////////////////////////////////////////////////////////////////////////////
DN_API DN_OSSemaphore DN_OS_SemaphoreInit(DN_U32 initial_count)
{
DN_POSIXCore *posix = g_dn_os_core->posix_context;
DN_Assert(posix);
DN_OSSemaphore result = {};
DN_POSIXSyncPrimitive *primitive = DN_POSIX_AllocSyncPrimitive_();
if (primitive) {
int pshared = 0; // Share the semaphore across all threads in the process
if (sem_init(&primitive->sem, pshared, initial_count) == 0)
result.handle = DN_POSIX_SyncPrimitiveToU64(primitive);
else
DN_POSIX_DeallocSyncPrimitive_(primitive);
}
return result;
}
DN_API void DN_OS_SemaphoreDeinit(DN_OSSemaphore *semaphore)
{
if (!DN_OS_SemaphoreIsValid(semaphore))
return;
// TODO(doyle): Error handling?
if (semaphore->posix_init)
sem_destroy(&semaphore->posix_handle);
*semaphore = {};
if (semaphore.handle != 0) {
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(semaphore->handle);
sem_destroy(&primitive->sem);
DN_POSIX_DeallocSyncPrimitive_(posix_sem);
*semaphore = {};
}
}
// NOTE: These functions don't need semaphore to be passed by pointer, **BUT**
// the POSIX implementation disallows copies of sem_t. In particular:
//
// Source: The Open Group Base Specifications Issue 7, 2018 edition
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_09
//
// 2.9.9 Synchronization Object Copies and Alternative Mappings
//
// For barriers, condition variables, mutexes, and read-write locks, [TSH]
// [Option Start] if the process-shared attribute is set to
// PTHREAD_PROCESS_PRIVATE, [Option End] only the synchronization object at the
// address used to initialize it can be used for performing synchronization. The
// effect of referring to another mapping of the same object when locking,
// unlocking, or destroying the object is undefined. [...] The effect of
// referring to a copy of the object when locking, unlocking, or destroying it
// is undefined.
DN_API void DN_OS_SemaphoreIncrement(DN_OSSemaphore *semaphore, uint32_t amount)
DN_API void DN_OS_SemaphoreIncrement(DN_OSSemaphore *semaphore, DN_U32 amount)
{
if (!DN_OS_SemaphoreIsValid(semaphore))
return;
#if defined(DN_OS_WIN32)
sem_post_multiple(&semaphore->posix_handle, amount); // mingw extension
#else
DN_ForIndexU(index, amount)
sem_post(&semaphore->posix_handle);
#endif // !defined(DN_OS_WIN32)
if (semaphore.handle != 0) {
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(semaphore->handle);
#if defined(DN_OS_WIN32)
sem_post_multiple(&primitive->sem, amount); // mingw extension
#else
for (DN_ForIndexU(index, amount))
sem_post(&primitive->sem);
#endif // !defined(DN_OS_WIN32)
}
}
DN_API DN_OSSemaphoreWaitResult DN_OS_SemaphoreWait(DN_OSSemaphore *semaphore,
uint32_t timeout_ms)
DN_U32 timeout_ms)
{
DN_OSSemaphoreWaitResult result = {};
if (!DN_OS_SemaphoreIsValid(semaphore))
if (semaphore.handle == 0)
return result;
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(semaphore->handle);
if (timeout_ms == DN_OS_SEMAPHORE_INFINITE_TIMEOUT) {
int wait_result = 0;
do {
wait_result = sem_wait(&semaphore->posix_handle);
wait_result = sem_wait(&primitive->sem);
} while (wait_result == -1 && errno == EINTR);
if (wait_result == 0)
@ -1075,47 +1110,115 @@ DN_API DN_OSSemaphoreWaitResult DN_OS_SemaphoreWait(DN_OSSemaphore *semaphore,
struct timespec abs_timeout = {};
abs_timeout.tv_sec = timeout_ms / 1000;
abs_timeout.tv_nsec = (timeout_ms % 1000) * 1'000'000;
if (sem_timedwait(&semaphore->posix_handle, &abs_timeout) == 0)
if (sem_timedwait(&primitive->sem) == 0)
result = DN_OSSemaphoreWaitResult_Success;
else if (errno == ETIMEDOUT)
result = DN_OSSemaphoreWaitResult_Timeout;
}
return result;
}
#endif // !defined(DN_NO_SEMAPHORE)
#if !defined(DN_NO_THREAD)
// NOTE: DN_OSMutex ////////////////////////////////////////////////////////////////////////////////
DN_API DN_OSMutex DN_OS_MutexInit()
{
DN_OSMutex result = {};
if (pthread_mutexattr_init(&result.posix_attribs) != 0)
return result;
if (pthread_mutex_init(&result.posix_handle, &result.posix_attribs) != 0)
return result;
DN_W32SyncPrimitive *primitive = DN_W32_AllocSyncPrimitive_();
DN_OSMutex result = {};
if (primitive) {
int pshared = 0; // Share the semaphore across all threads in the process
if (pthread_mutex_init(mutex, pshared, nullptr) == 0)
result.handle = DN_POSIX_SyncPrimitiveToU64(primitive);
else
DN_POSIX_DeallocSyncPrimitive_(primitive);
}
return result;
}
DN_API void DN_OS_MutexDeinit(DN_OSMutex *mutex)
{
if (!mutex)
return;
pthread_mutexattr_destroy(&mutex->posix_attribs);
pthread_mutex_destroy(&mutex->posix_handle);
if (mutex && mutex->handle != 0) {
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(semaphore->handle);
pthread_mutex_destroy(&primitive->mutex);
DN_POSIX_DeallocSyncPrimitive_(primitive);
*mutex = {};
}
}
DN_API void DN_OS_MutexLock(DN_OSMutex *mutex)
{
if (!mutex)
return;
pthread_mutex_lock(&mutex->posix_handle);
if (mutex && mutex->handle != 0) {
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(semaphore->handle);
pthread_mutex_lock(&primitive->mutex);
}
}
DN_API void DN_OS_MutexUnlock(DN_OSMutex *mutex)
{
if (!mutex)
return;
pthread_mutex_unlock(&mutex->posix_handle);
if (mutex && mutex->handle != 0) {
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(semaphore->handle);
pthread_mutex_unlock(&primitive->mutex);
}
}
DN_API DN_OSConditionVariable DN_OS_ConditionVariableInit()
{
DN_POSIXSyncPrimitive *primitive = DN_POSIX_AllocSyncPrimitive_();
DN_OSConditionVariable result = {};
if (primitive) {
if (pthread_cond_init(&primitive->cv) == 0)
result.handle = DN_POSIX_SyncPrimitiveToU64(primitive);
else
DN_POSIX_DeallocSyncPrimitive_(primitive);
}
return result;
}
DN_API bool DN_OS_ConditionVariableDeinit(DN_OSConditionVariable *cv)
{
if (cv && cv->handle != 0) {
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(cv->handle);
pthread_cond_destroy(&primitive->cv);
DN_POSIX_DeallocSyncPrimitive_(primitive);
*cv = {};
}
}
DN_API bool DN_OS_ConditionVariableWaitUntil(DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 end_ts_ms)
{
bool result = false;
if (cv && mutex && mutex->handle != 0 && cv->handle != 0) {
DN_POSIXSyncPrimitive *cv_primitive = DN_OS_U64ToPOSIXSyncPrimitive_(cv->handle);
DN_POSIXSyncPrimitive *mutex_primitive = DN_OS_U64ToPOSIXSyncPrimitive_(mutex->handle);
struct timespec time;
time.tv_sec = end_ts_ms / 1'000;
time.tv_nsec = 1'000'000 * (end_ts_ms - (end_ts_ms / 1'000) * 1'000);
int wait_result = pthread_cond_timedwait(&cv_primitive->cv, &mutex_primitive->mutex, &time);
result = (wait_result != ETIMEDOUT);
}
return result;
}
DN_API bool DN_OS_ConditionVariableWait(DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 sleep_ms)
{
DN_U64 end_ts_ms = DN_OS_DateUnixTimeMs() + sleep_ms;
bool result = DN_OS_ConditionVariableWaitUntil(cv, mutex, end_ts_ms);
return result;
}
DN_API void DN_OS_ConditionVariableSignal(DN_OSConditionVariable *cv)
{
if (cv && cv->handle != 0) {
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(cv->handle);
pthread_cond_signal(&primitive->cv);
}
}
DN_API void DN_OS_ConditionVariableBroadcast(DN_OSConditionVariable *cv)
{
if (cv && cv->handle != 0) {
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(cv->handle);
pthread_cond_broadcast(&primitive->cv);
}
}
// NOTE: DN_OSThread ///////////////////////////////////////////////////////////////////////////////
@ -1181,11 +1284,11 @@ DN_API void DN_OS_ThreadDeinit(DN_OSThread *thread)
thread->thread_id = {};
}
DN_API uint32_t DN_OS_ThreadID()
DN_API DN_U32 DN_OS_ThreadID()
{
pid_t result = gettid();
DN_Assert(gettid() >= 0);
return DN_CAST(uint32_t) result;
return DN_CAST(DN_U32) result;
}
DN_API void DN_Posix_ThreadSetName(DN_Str8 name)
@ -1195,7 +1298,6 @@ DN_API void DN_Posix_ThreadSetName(DN_Str8 name)
pthread_t thread = pthread_self();
pthread_setname_np(thread, (char *)copy.data);
}
#endif // !defined(DN_NO_THREAD)
DN_API DN_POSIXProcSelfStatus DN_Posix_ProcSelfStatus()
{
@ -1230,7 +1332,7 @@ DN_API DN_POSIXProcSelfStatus DN_Posix_ProcSelfStatus()
DN_Str8 status_buf = DN_Str8Builder_BuildFromTLS(&builder);
DN_Slice<DN_Str8> lines = DN_Str8_SplitAllocFromTLS(status_buf, DN_STR8("\n"), DN_Str8SplitIncludeEmptyStrings_No);
DN_ForIt(line_it, DN_Str8, &lines) {
for (DN_ForIt(line_it, DN_Str8, &lines)) {
DN_Str8 line = DN_Str8_TrimWhitespaceAround(*line_it.data);
if (DN_Str8_StartsWith(line, NAME, DN_Str8EqCase_Insensitive)) {
DN_Str8 str8 = DN_Str8_TrimWhitespaceAround(DN_Str8_Slice(line, NAME.size, line.size));
@ -1312,7 +1414,7 @@ static void DN_OS_HttpRequestEMFetchOnSuccessCallback(emscripten_fetch_t *fetch)
if (!DN_Check(response))
return;
response->http_status = DN_CAST(uint32_t) fetch->status;
response->http_status = DN_CAST(DN_U32) fetch->status;
response->body = DN_Str8_Alloc(response->arena, fetch->numBytes, DN_ZeroMem_No);
if (response->body.data)
DN_Memcpy(response->body.data, fetch->data, fetch->numBytes);
@ -1327,7 +1429,7 @@ static void DN_OS_HttpRequestEMFetchOnErrorCallback(emscripten_fetch_t *fetch)
if (!DN_Check(response))
return;
response->http_status = DN_CAST(uint32_t) fetch->status;
response->http_status = DN_CAST(DN_U32) fetch->status;
response->body = DN_Str8_Alloc(response->arena, fetch->numBytes, DN_ZeroMem_No);
if (response->body.size)
DN_Memcpy(response->body.data, fetch->data, fetch->numBytes);
@ -1372,7 +1474,7 @@ DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response,
DN_CheckF(method.size < sizeof(fetch_attribs.requestMethod),
"%.*s",
DN_STR_FMT(response->error_msg));
response->error_code = DN_CAST(uint32_t) - 1;
response->error_code = DN_CAST(DN_U32) - 1;
DN_Atomic_AddU32(&response->done, 1);
return;
}
@ -1409,7 +1511,6 @@ DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response)
#endif // #elif defined(DN_OS_WIN32)
DN_Arena_Deinit(&response->tmp_arena);
if (DN_OS_SemaphoreIsValid(&response->on_complete_semaphore))
DN_OS_SemaphoreDeinit(&response->on_complete_semaphore);
DN_OS_SemaphoreDeinit(&response->on_complete_semaphore);
*response = {};
}

View File

@ -1,5 +1,8 @@
#pragma once
#include <pthread.h>
#include <semaphore.h>
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
@ -26,5 +29,47 @@ struct DN_POSIXProcSelfStatus
DN_U32 vm_size;
};
// NOTE: The POSIX implementation disallows copies of synchronisation objects in
// general hence we have to dynamically allocate these primitives to maintain a
// consistent address.
//
//
// Source: The Open Group Base Specifications Issue 7, 2018 edition
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_09
//
// 2.9.9 Synchronization Object Copies and Alternative Mappings
//
// For barriers, condition variables, mutexes, and read-write locks, [TSH]
// [Option Start] if the process-shared attribute is set to
// PTHREAD_PROCESS_PRIVATE, [Option End] only the synchronization object at the
// address used to initialize it can be used for performing synchronization. The
// effect of referring to another mapping of the same object when locking,
// unlocking, or destroying the object is undefined. [...] The effect of
// referring to a copy of the object when locking, unlocking, or destroying it
// is undefined.
enum DN_POSIXSyncPrimitiveType
{
DN_OSPOSIXSyncPrimitiveType_Semaphore,
DN_OSPOSIXSyncPrimitiveType_Mutex,
DN_OSPOSIXSyncPrimitiveType_ConditionVariable,
};
struct DN_POSIXSyncPrimitive
{
union
{
sem_t sem;
pthread_mutex_t mutex;
pthread_cond_t cv;
};
DN_POSIXSyncPrimitive *next;
};
struct DN_POSIXCore
{
DN_POSIXSyncPrimitive *sync_primitive_free_list;
};
DN_API void DN_Posix_ThreadSetName(DN_Str8 name);
DN_API DN_POSIXProcSelfStatus DN_Posix_ProcSelfStatus();

View File

@ -35,9 +35,9 @@ DN_API void DN_OS_TLSInit(DN_OSTLS *tls, DN_OSTLSInitArgs args)
// TODO: We shouldn't have the no alloc track flag here but the initial TLS
// init on OS init happens before CORE init. CORE init is the one responsible
// for setting up the alloc tracking data structures.
DN_ForIndexU(index, DN_OSTLSArena_Count) {
DN_Arena *arena = tls->arenas + index;
switch (DN_CAST(DN_OSTLSArena) index) {
for (DN_ForItCArray(it, DN_Arena, tls->arenas)) {
DN_Arena *arena = it.data;
switch (DN_CAST(DN_OSTLSArena) it.index) {
default: *arena = DN_Arena_InitFromOSVMem(reserve, commit, DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_NoAllocTrack); break;
case DN_OSTLSArena_ErrorSink: *arena = DN_Arena_InitFromOSVMem(err_sink_reserve, err_sink_commit, DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_NoAllocTrack); break;
case DN_OSTLSArena_Count: DN_InvalidCodePath; break;
@ -54,10 +54,8 @@ DN_API void DN_OS_TLSDeinit(DN_OSTLS *tls)
tls->init = false;
tls->err_sink = {};
tls->arena_stack_index = {};
DN_ForIndexU(index, DN_OSTLSArena_Count) {
DN_Arena *arena = tls->arenas + index;
DN_Arena_Deinit(arena);
}
for (DN_ForItCArray(it, DN_Arena, tls->arenas))
DN_Arena_Deinit(it.data);
}
DN_THREAD_LOCAL DN_OSTLS *g_dn_curr_thread_tls;
@ -170,7 +168,7 @@ DN_API DN_OSErrSink *DN_OS_ErrSinkBegin_(DN_OSErrSinkMode mode, DN_CallSite call
if (tls->err_sink.stack_size == DN_ArrayCountU(err->stack)) {
DN_Str8Builder builder = DN_Str8Builder_InitFromTLS();
DN_USize counter = 0;
DN_ForItSize(it, DN_OSErrSinkNode, err->stack, err->stack_size) {
for (DN_ForItSize(it, DN_OSErrSinkNode, err->stack, err->stack_size)) {
DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(6284) // Object passed as _Param_(4) when a string is required in call to 'DN_Str8Builder_AppendF' Actual type: 'struct DN_Str8'.
DN_Str8Builder_AppendF(&builder, " [%04zu] %S:%u %S\n", counter++, it.data->call_site.file, it.data->call_site.line, it.data->call_site.function);
@ -185,9 +183,9 @@ DN_API DN_OSErrSink *DN_OS_ErrSinkBegin_(DN_OSErrSinkMode mode, DN_CallSite call
}
DN_OSErrSinkNode *node = tls->err_sink.stack + tls->err_sink.stack_size++;
node->arena_pos = arena_pos;
node->mode = mode;
node->call_site = call_site;
node->arena_pos = arena_pos;
node->mode = mode;
node->call_site = call_site;
DN_DLList_InitArena(node->msg_sentinel, DN_OSErrSinkMsg, result->arena);
// NOTE: Handle allocation error

1843
OS/dn_os_w32.cpp Normal file

File diff suppressed because it is too large Load Diff

71
OS/dn_os_w32.h Normal file
View File

@ -0,0 +1,71 @@
#if !defined(DN_OS_WIN32_H)
#define DN_OS_WIN32_H
struct DN_W32Error
{
unsigned long code;
DN_Str8 msg;
};
struct DN_W32FolderIteratorW
{
void *handle;
DN_Str16 file_name;
wchar_t file_name_buf[512];
};
enum DN_W32SyncPrimitiveType
{
DN_OSW32SyncPrimitiveType_Semaphore,
DN_OSW32SyncPrimitiveType_Mutex,
DN_OSW32SyncPrimitiveType_ConditionVariable,
};
struct DN_W32SyncPrimitive
{
union
{
void *sem;
CRITICAL_SECTION mutex;
CONDITION_VARIABLE cv;
};
DN_W32SyncPrimitive *next;
};
typedef HRESULT DN_W32SetThreadDescriptionFunc(HANDLE hThread, PWSTR const lpThreadDescription);
struct DN_W32Core
{
DN_W32SetThreadDescriptionFunc *set_thread_description;
LARGE_INTEGER qpc_frequency;
void *bcrypt_rng_handle;
bool bcrypt_init_success;
bool sym_initialised;
CRITICAL_SECTION sync_primitive_free_list_mutex;
DN_W32SyncPrimitive *sync_primitive_free_list;
};
DN_API void DN_W32_ThreadSetName (DN_Str8 name);
DN_API DN_Str16 DN_W32_ErrorCodeToMsg16Alloc(uint32_t error_code);
DN_API DN_W32Error DN_W32_ErrorCodeToMsg (DN_Arena *arena, uint32_t error_code);
DN_API DN_W32Error DN_W32_ErrorCodeToMsgAlloc (uint32_t error_code);
DN_API DN_W32Error DN_W32_LastError (DN_Arena *arena);
DN_API DN_W32Error DN_W32_LastErrorAlloc ();
DN_API void DN_W32_MakeProcessDPIAware ();
// NOTE: Windows Str8 <-> Str16 ////////////////////////////////////////////////////////////////////
DN_API DN_Str16 DN_W32_Str8ToStr16 (DN_Arena *arena, DN_Str8 src);
DN_API int DN_W32_Str8ToStr16Buffer (DN_Str16 src, char *dest, int dest_size);
DN_API DN_Str8 DN_W32_Str16ToStr8 (DN_Arena *arena, DN_Str16 src);
DN_API int DN_W32_Str16ToStr8Buffer (DN_Str16 src, char *dest, int dest_size);
DN_API DN_Str8 DN_W32_Str16ToStr8FromHeap(DN_Str16 src);
// NOTE: Path navigation ///////////////////////////////////////////////////////////////////////////
DN_API DN_Str16 DN_W32_EXEPathW (DN_Arena *arena);
DN_API DN_Str16 DN_W32_EXEDirW (DN_Arena *arena);
DN_API DN_Str8 DN_W32_WorkingDir (DN_Arena *arena, DN_Str8 suffix);
DN_API DN_Str16 DN_W32_WorkingDirW (DN_Arena *arena, DN_Str16 suffix);
DN_API bool DN_W32_DirWIterate (DN_Str16 path, DN_W32FolderIteratorW *it);
#endif // !defined(DN_OS_WIN32)

View File

@ -296,6 +296,10 @@
WORD Identifier;
} RTL_CRITICAL_SECTION_DEBUG, *PRTL_CRITICAL_SECTION_DEBUG, RTL_RESOURCE_DEBUG, *PRTL_RESOURCE_DEBUG;
typedef struct _RTL_CONDITION_VARIABLE {
PVOID Ptr;
} RTL_CONDITION_VARIABLE, *PRTL_CONDITION_VARIABLE;
#pragma pack(push, 8)
typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
@ -1065,8 +1069,15 @@
}
// NOTE: um/synchapi.h /////////////////////////////////////////////////////////////////////////
typedef RTL_CONDITION_VARIABLE CONDITION_VARIABLE, *PCONDITION_VARIABLE;
extern "C"
{
__declspec(dllimport) VOID __stdcall InitializeConditionVariable (CONDITION_VARIABLE *ConditionVariable);
__declspec(dllimport) VOID __stdcall WakeConditionVariable (CONDITION_VARIABLE *ConditionVariable);
__declspec(dllimport) VOID __stdcall WakeAllConditionVariable (CONDITION_VARIABLE *ConditionVariable);
__declspec(dllimport) BOOL __stdcall SleepConditionVariableCS (CONDITION_VARIABLE *ConditionVariable, CRITICAL_SECTION *CriticalSection, DWORD dwMilliseconds);
__declspec(dllimport) VOID __stdcall InitializeCriticalSection (CRITICAL_SECTION *lpCriticalSection);
__declspec(dllimport) VOID __stdcall EnterCriticalSection (CRITICAL_SECTION *lpCriticalSection);
__declspec(dllimport) VOID __stdcall LeaveCriticalSection (CRITICAL_SECTION *lpCriticalSection);
@ -1075,6 +1086,7 @@
__declspec(dllimport) DWORD __stdcall SetCriticalSectionSpinCount (CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount);
__declspec(dllimport) BOOL __stdcall TryEnterCriticalSection (CRITICAL_SECTION *lpCriticalSection);
__declspec(dllimport) VOID __stdcall DeleteCriticalSection (CRITICAL_SECTION *lpCriticalSection);
__declspec(dllimport) DWORD __stdcall WaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds);
__declspec(dllimport) BOOL __stdcall ReleaseSemaphore (HANDLE hSemaphore, LONG lReleaseCount, LONG *lpPreviousCount);
__declspec(dllimport) VOID __stdcall Sleep (DWORD dwMilliseconds);

View File

@ -1,3 +1,5 @@
#define DN_BASE_INC_CPP
#include "Base/dn_base.cpp"
#include "Base/dn_base_containers.cpp"
#include "Base/dn_base_convert.cpp"

View File

@ -1,3 +1,5 @@
#define DN_CORE_INC_CPP
#include "Core/dn_core.cpp"
#include "Core/dn_core_debug.cpp"
#include "Core/dn_core_demo.cpp"

View File

@ -1,3 +1,5 @@
#define DN_OS_INC_CPP
#include "OS/dn_os_tls.cpp"
#include "OS/dn_os.cpp"
#include "OS/dn_os_allocator.cpp"
@ -8,7 +10,7 @@
#if defined(DN_PLATFORM_POSIX)
#include "OS/dn_os_posix.cpp"
#elif defined(DN_PLATFORM_WIN32)
#include "OS/dn_os_win32.cpp"
#include "OS/dn_os_w32.cpp"
#else
#error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
#endif

View File

@ -1,3 +1,6 @@
#if !defined(DN_OS_INC_H)
#define DN_OS_INC_H
#include "OS/dn_os_tls.h"
#include "OS/dn_os.h"
#include "OS/dn_os_allocator.h"
@ -7,7 +10,9 @@
#if defined(DN_PLATFORM_WIN32)
#include "OS/dn_os_windows.h"
#include "OS/dn_os_win32.h"
#include "OS/dn_os_w32.h"
#elif defined(DN_PLATFORM_POSIX)
#include "OS/dn_os_posix.h"
#endif
#endif // DN_OS_INC_H