Simplify, simplify, simplify. Kill code that was unloved and unused

This commit is contained in:
2026-06-18 22:11:30 +10:00
parent b813543659
commit ab4eaa5bb3
32 changed files with 3637 additions and 9925 deletions
+108 -180
View File
@@ -883,27 +883,122 @@ DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip(DN_OSThreadLane lane)
return result;
}
// NOTE: DN_OSHttp
DN_API void DN_OS_HttpRequestWait(DN_OSHttpResponse *response)
static DN_I32 DN_OS_AsyncThreadEntryPoint_(DN_OSThread *thread)
{
if (response && response->on_complete_semaphore.handle != 0)
DN_OS_SemaphoreWait(&response->on_complete_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
DN_OS_ThreadSetNameFmt("%.*s", DN_Str8PrintFmt(thread->name));
DN_OSAsyncCore *async = DN_Cast(DN_OSAsyncCore *) thread->user_context;
DN_Ring *ring = &async->ring;
for (;;) {
DN_OS_SemaphoreWait(&async->worker_sem, UINT32_MAX);
if (async->join_threads)
break;
DN_OSAsyncTask task = {};
for (DN_OS_MutexScope(&async->ring_mutex)) {
if (DN_RingHasData(ring, sizeof(task)))
DN_RingRead(ring, &task, sizeof(task));
}
if (task.work.func) {
DN_OS_ConditionVariableBroadcast(&async->ring_write_cv); // Resume any blocked ring write(s)
DN_OSAsyncWorkArgs args = {};
args.input = task.work.input;
args.thread = thread;
DN_AtomicAddU32(&async->busy_threads, 1);
task.work.func(args);
DN_AtomicSubU32(&async->busy_threads, 1);
if (task.completion_sem.handle != 0)
DN_OS_SemaphoreIncrement(&task.completion_sem, 1);
}
}
return 0;
}
DN_API DN_OSHttpResponse DN_OS_HttpRequest(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_AsyncInit(DN_OSAsyncCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size)
{
// TODO(doyle): Revise the memory allocation and its lifetime
DN_OSHttpResponse result = {};
DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1);
result.scratch_arena = scratch.arena;
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;
DN_OS_ThreadInit(thread, DN_OS_AsyncThreadEntryPoint_, /*lane=*/ nullptr, DN_TCInitArgsDefault(), async);
}
}
DN_API void DN_OS_AsyncDeinit(DN_OSAsyncCore *async)
{
DN_Assert(async);
DN_AtomicSetValue32(&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_ThreadJoin(it.data, DN_TCDeinitArenas_Yes);
}
static bool DN_OS_AsyncQueueTask_(DN_OSAsyncCore *async, DN_OSAsyncTask const *task, 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_RingHasSpace(&async->ring, sizeof(*task))) {
DN_RingWriteStruct(&async->ring, task);
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
DN_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers);
DN_OS_HttpRequestWait(&result);
DN_TCScratchEnd(&scratch);
return result;
}
// NOTE: DN_OSPrint
DN_API bool DN_OS_AsyncQueueWork(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms)
{
DN_OSAsyncTask task = {};
task.work.func = func;
task.work.input = input;
bool result = DN_OS_AsyncQueueTask_(async, &task, wait_time_ms);
return result;
}
DN_API DN_OSAsyncTask DN_OS_AsyncQueueTask(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms)
{
DN_OSAsyncTask result = {};
result.work.func = func;
result.work.input = input;
result.completion_sem = DN_OS_SemaphoreInit(0);
result.queued = DN_OS_AsyncQueueTask_(async, &result, wait_time_ms);
if (!result.queued)
DN_OS_SemaphoreDeinit(&result.completion_sem);
return result;
}
DN_API bool DN_OS_AsyncWaitTask(DN_OSAsyncTask *task, DN_U32 timeout_ms)
{
bool result = true;
if (!task->queued)
return result;
DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&task->completion_sem, timeout_ms);
result = wait == DN_OSSemaphoreWaitResult_Success;
if (result)
DN_OS_SemaphoreDeinit(&task->completion_sem);
return result;
}
DN_API DN_LogStyle DN_OS_PrintStyleColour(uint8_t r, uint8_t g, uint8_t b, DN_LogBold bold)
{
DN_LogStyle result = {};
@@ -1077,173 +1172,6 @@ DN_API void DN_OS_PrintLnFVStyle(DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_
DN_OS_Print(dest, DN_Str8Lit("\n"));
}
// NOTE: DN_VArray
template <typename T>
DN_VArray<T> DN_OS_VArrayInitByteSize(DN_USize byte_size)
{
DN_VArray<T> result = {};
result.data = DN_Cast(T *) DN_OS_MemReserve(byte_size, DN_MemCommit_No, DN_MemPage_ReadWrite);
if (result.data)
result.max = byte_size / sizeof(T);
return result;
}
template <typename T>
DN_VArray<T> DN_OS_VArrayInit(DN_USize max)
{
DN_VArray<T> result = DN_OS_VArrayInitByteSize<T>(max * sizeof(T));
DN_Assert(result.max >= max);
return result;
}
template <typename T, DN_USize N>
DN_VArray<T> DN_OS_VArrayInitCArray(T const (&items)[N], DN_USize max)
{
DN_USize real_max = DN_Max(N, max);
DN_VArray<T> result = DN_OS_VArrayInit<T>(real_max);
if (DN_OS_VArrayIsValid(&result))
DN_OS_VArrayAddArray(&result, items, N);
return result;
}
template <typename T>
void DN_OS_VArrayDeinit(DN_VArray<T> *array)
{
DN_OS_MemRelease(array->data, array->max * sizeof(T));
*array = {};
}
template <typename T>
bool DN_OS_VArrayIsValid(DN_VArray<T> const *array)
{
bool result = array->data && array->size <= array->max;
return result;
}
template <typename T>
T *DN_OS_VArrayAddArray(DN_VArray<T> *array, T const *items, DN_USize count)
{
T *result = DN_OS_VArrayMakeArray(array, count, DN_ZMem_No);
if (result)
DN_Memcpy(result, items, count * sizeof(T));
return result;
}
template <typename T, DN_USize N>
T *DN_OS_VArrayAddCArray(DN_VArray<T> *array, T const (&items)[N])
{
T *result = DN_OS_VArrayAddArray(array, items, N);
return result;
}
template <typename T>
T *DN_OS_VArrayAdd(DN_VArray<T> *array, T const &item)
{
T *result = DN_OS_VArrayAddArray(array, &item, 1);
return result;
}
template <typename T>
T *DN_OS_VArrayMakeArray(DN_VArray<T> *array, DN_USize count, DN_ZMem z_mem)
{
if (!DN_OS_VArrayIsValid(array))
return nullptr;
if (!DN_CheckF((array->size + count) < array->max, "Array is out of space (user requested +%zu items, array has %zu/%zu items)", count, array->size, array->max))
return nullptr;
if (!DN_OS_VArrayReserve(array, count))
return nullptr;
// TODO: Use placement new
T *result = array->data + array->size;
array->size += count;
if (z_mem == DN_ZMem_Yes)
DN_Memset(result, 0, count * sizeof(T));
return result;
}
template <typename T>
T *DN_OS_VArrayMake(DN_VArray<T> *array, DN_ZMem z_mem)
{
T *result = DN_OS_VArrayMakeArray(array, 1, z_mem);
return result;
}
template <typename T>
T *DN_OS_VArrayInsertArray(DN_VArray<T> *array, DN_USize index, T const *items, DN_USize count)
{
T *result = nullptr;
if (!DN_OS_VArrayIsValid(array))
return result;
if (DN_OS_VArrayReserve(array, array->size + count))
result = DN_ArrayInsertArray(array->data, &array->size, array->max, index, items, count);
return result;
}
template <typename T, DN_USize N>
T *DN_OS_VArrayInsertCArray(DN_VArray<T> *array, DN_USize index, T const (&items)[N])
{
T *result = DN_OS_VArrayInsertArray(array, index, items, N);
return result;
}
template <typename T>
T *DN_OS_VArrayInsert(DN_VArray<T> *array, DN_USize index, T const &item)
{
T *result = DN_OS_VArrayInsertArray(array, index, &item, 1);
return result;
}
template <typename T>
T *DN_OS_VArrayPopFront(DN_VArray<T> *array, DN_USize count)
{
T *result = DN_Cast(T *)DN_ArrayPopFront(array->data, &array->size, sizeof(T), count);
return result;
}
template <typename T>
T *DN_OS_VArrayPopBack(DN_VArray<T> *array, DN_USize count)
{
T *result = DN_Cast(T *)DN_ArrayPopBack(array->data, &array->size, sizeof(T), count);
return result;
}
template <typename T>
DN_ArrayEraseResult DN_OS_VArrayEraseRange(DN_VArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase)
{
DN_ArrayEraseResult result = {};
if (!DN_OS_VArrayIsValid(array))
return result;
result = DN_ArrayEraseRange(array->data, &array->size, sizeof(T), begin_index, count, erase);
return result;
}
template <typename T>
void DN_OS_VArrayClear(DN_VArray<T> *array, DN_ZMem z_mem)
{
if (array) {
if (z_mem == DN_ZMem_Yes)
DN_Memset(array->data, 0, array->size * sizeof(T));
array->size = 0;
}
}
template <typename T>
bool DN_OS_VArrayReserve(DN_VArray<T> *array, DN_USize count)
{
if (!DN_OS_VArrayIsValid(array) || count == 0)
return false;
DN_USize real_commit = (array->size + count) * sizeof(T);
DN_USize aligned_commit = DN_AlignUpPowerOfTwo(real_commit, DN_Get()->os.page_size);
if (array->commit >= aligned_commit)
return true;
bool result = DN_OS_MemCommit(array->data, aligned_commit, DN_MemPage_ReadWrite);
array->commit = aligned_commit;
return result;
}
DN_API DN_StackTrace DN_StackTraceFromAllocator(DN_Allocator allocator, DN_U16 limit)
{
DN_StackTrace result = {};
+46 -86
View File
@@ -31,10 +31,6 @@
#endif
#endif
#if defined(DN_PLATFORM_EMSCRIPTEN)
#include <emscripten/fetch.h> // emscripten_fetch (for DN_OSHttpResponse)
#endif
extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
struct DN_OSTimer /// Record time between two time-points using the OS's performance counter.
@@ -229,42 +225,6 @@ struct DN_OSThread
DN_TCInitArgs tc_init_args;
};
// NOTE: DN_OSHttp
enum DN_OSHttpRequestSecure
{
DN_OSHttpRequestSecure_No,
DN_OSHttpRequestSecure_Yes,
};
struct DN_OSHttpResponse
{
// NOTE: Response data
DN_U32 error_code;
DN_Str8 error_msg;
DN_U16 http_status;
DN_Str8 body;
DN_B32 done;
// NOTE: Book-keeping
DN_Arena *arena; // Allocates memory for the response
// NOTE: Async book-keeping
// Synchronous HTTP response uses the TLS scratch arena whereas async
// calls use their own dedicated arena.
DN_Arena tmp_arena;
DN_Arena scratch_arena;
DN_Str8Builder builder;
DN_OSSemaphore on_complete_semaphore;
#if defined(DN_PLATFORM_EMSCRIPTEN)
emscripten_fetch_t *em_handle;
#elif defined(DN_PLATFORM_WIN32)
HINTERNET w32_request_session;
HINTERNET w32_request_connection;
HINTERNET w32_request_handle;
#endif
};
struct DN_OSCore
{
DN_CPUReport cpu_report;
@@ -509,10 +469,52 @@ DN_API DN_OSThreadLane* DN_OS_TCThreadLane ()
DN_API void DN_OS_TCThreadLaneSync (void **ptr_to_share);
DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip (DN_OSThreadLane lane);
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);
DN_API DN_OSHttpResponse DN_OS_HttpRequest (DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
enum DN_OSAsyncPriority
{
DN_OSAsyncPriority_Low,
DN_OSAsyncPriority_High,
DN_OSAsyncPriority_Count,
};
struct DN_OSAsyncCore
{
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;
};
struct DN_OSAsyncWorkArgs
{
DN_OSThread *thread;
void *input;
};
typedef void(DN_OSAsyncWorkFunc)(DN_OSAsyncWorkArgs work_args);
struct DN_OSAsyncWork
{
DN_OSAsyncWorkFunc *func;
void *input;
void *output;
};
struct DN_OSAsyncTask
{
bool queued;
DN_OSAsyncWork work;
DN_OSSemaphore completion_sem;
};
DN_API void DN_OS_AsyncInit (DN_OSAsyncCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size);
DN_API void DN_OS_AsyncDeinit (DN_OSAsyncCore *async);
DN_API bool DN_OS_AsyncQueueWork(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms);
DN_API DN_OSAsyncTask DN_OS_AsyncQueueTask(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms);
DN_API bool DN_OS_AsyncWaitTask (DN_OSAsyncTask *task, DN_U32 timeout_ms);
// NOTE: DN_OSPrint
enum DN_OSPrintDest
@@ -570,46 +572,4 @@ DN_API void DN_OS_PrintLnFV (DN_OSPrintDest dest, DN_FMT_ATTRIB char
DN_API void DN_OS_PrintLnStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_Str8 string);
DN_API void DN_OS_PrintLnFStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void DN_OS_PrintLnFVStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, va_list args);
// NOTE: DN_VArray
// TODO(doyle): Add an API for shrinking the array by decomitting pages back to the OS.
template <typename T> struct DN_VArray
{
T *data; // Pointer to the start of the array items in the block of memory
DN_USize size; // Number of items currently in the array
DN_USize max; // Maximum number of items this array can store
DN_USize commit; // Bytes committed
T *begin() { return data; }
T *end () { return data + size; }
T const *begin() const { return data; }
T const *end () const { return data + size; }
};
template <typename T> DN_VArray<T> DN_OS_VArrayInitByteSize (DN_USize byte_size);
template <typename T> DN_VArray<T> DN_OS_VArrayInit (DN_USize max);
template <typename T, DN_USize N> DN_VArray<T> DN_OS_VArrayInitCArray (T const (&items)[N], DN_USize max);
template <typename T> void DN_OS_VArrayDeinit (DN_VArray<T> *array);
template <typename T> bool DN_OS_VArrayIsValid (DN_VArray<T> const *array);
template <typename T> bool DN_OS_VArrayReserve (DN_VArray<T> *array, DN_USize count);
template <typename T> T * DN_OS_VArrayAddArray (DN_VArray<T> *array, T const *items, DN_USize count);
template <typename T, DN_USize N> T * DN_OS_VArrayAddCArray (DN_VArray<T> *array, T const (&items)[N]);
template <typename T> T * DN_OS_VArrayAdd (DN_VArray<T> *array, T const &item);
#define DN_OS_VArrayAddArrayAssert(...) DN_HardAssert(DN_OS_VArrayAddArray(__VA_ARGS__))
#define DN_OS_VArrayAddCArrayAssert(...) DN_HardAssert(DN_OS_VArrayAddCArray(__VA_ARGS__))
#define DN_OS_VArrayAddAssert(...) DN_HardAssert(DN_OS_VArrayAdd(__VA_ARGS__))
template <typename T> T * DN_OS_VArrayMakeArray (DN_VArray<T> *array, DN_USize count, DN_ZMem z_mem);
template <typename T> T * DN_OS_VArrayMake (DN_VArray<T> *array, DN_ZMem z_mem);
#define DN_OS_VArrayMakeArrayAssert(...) DN_HardAssert(DN_OS_VArrayMakeArray(__VA_ARGS__))
#define DN_OS_VArrayMakeAssert(...) DN_HardAssert(DN_OS_VArrayMake(__VA_ARGS__))
template <typename T> T * DN_OS_VArrayInsertArray (DN_VArray<T> *array, DN_USize index, T const *items, DN_USize count);
template <typename T, DN_USize N> T * DN_OS_VArrayInsertCArray (DN_VArray<T> *array, DN_USize index, T const (&items)[N]);
template <typename T> T * DN_OS_VArrayInsert (DN_VArray<T> *array, DN_USize index, T const &item);
#define DN_OS_VArrayInsertArrayAssert(...) DN_HardAssert(DN_OS_VArrayInsertArray(__VA_ARGS__))
#define DN_OS_VArrayInsertCArrayAssert(...) DN_HardAssert(DN_OS_VArrayInsertCArray(__VA_ARGS__))
#define DN_OS_VArrayInsertAssert(...) DN_HardAssert(DN_OS_VArrayInsert(__VA_ARGS__))
template <typename T> T DN_OS_VArrayPopFront (DN_VArray<T> *array, DN_USize count);
template <typename T> T DN_OS_VArrayPopBack (DN_VArray<T> *array, DN_USize count);
template <typename T> DN_ArrayEraseResult DN_OS_VArrayEraseRange (DN_VArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
template <typename T> void DN_OS_VArrayClear (DN_VArray<T> *array, DN_ZMem z_mem);
#endif // !defined(DN_OS_H)
-151
View File
@@ -1444,154 +1444,3 @@ DN_API DN_OSPosixProcSelfStatus DN_OS_PosixProcSelfStatus()
DN_OS_FileClose(&file);
return result;
}
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
#if 0 // TODO(doyle): Implement websockets for Windows and Emscripten
static EM_BOOL EMWebSocketOnOpenCallback(int type, const EmscriptenWebSocketOpenEvent *event, void *user_context)
{
(void)user_context;
(void)type;
(void)event;
// EMSCRIPTEN_RESULT result = emscripten_websocket_send_utf8_text(event->socket, R"({"jsonrpc":"2.0","id":1,"method": "eth_subscribe","params":["newHeads"]})");
// if (result)
// DN_LogInfoF("Failed to emscripten_websocket_send_utf8_text(): %d\n", result);
return EM_TRUE;
}
static EM_BOOL EMWebSocketOnMsgCallback(int type, const EmscriptenWebSocketMessageEvent *event __attribute__((nonnull)), void *user_context)
{
(void)type;
(void)user_context;
(void)event;
if (event->isText) {
DN_LogInfoF("Received: %.*s", event->numBytes, event->data);
} else {
DN_LogInfoF("Received: %d bytes", event->numBytes);
}
return EM_TRUE;
}
static EM_BOOL EMWebSocketOnErrorCallback(int type, const EmscriptenWebSocketErrorEvent *event, void *user_context)
{
(void)user_context;
(void)type;
(void)event;
return EM_TRUE;
}
static EM_BOOL EMWebSocketOnCloseCallback(int type, const EmscriptenWebSocketCloseEvent *event, void *user_context)
{
(void)user_context;
(void)type;
(void)event;
return EM_TRUE;
}
#endif
#if defined(DN_PLATFORM_EMSCRIPTEN)
static void DN_OS_HttpRequestEMFetchOnSuccessCallback(emscripten_fetch_t *fetch)
{
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData;
if (!DN_Check(response))
return;
response->http_status = DN_Cast(DN_U32) fetch->status;
response->body = DN_Str8AllocArena(fetch->numBytes, DN_ZMem_No, response->arena);
if (response->body.data)
DN_Memcpy(response->body.data, fetch->data, fetch->numBytes);
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
DN_AtomicAddU32(&response->done, 1);
}
static void DN_OS_HttpRequestEMFetchOnErrorCallback(emscripten_fetch_t *fetch)
{
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData;
if (!DN_Check(response))
return;
response->http_status = DN_Cast(DN_U32) fetch->status;
response->body = DN_Str8AllocArena(fetch->numBytes, DN_ZMem_No, response->arena);
if (response->body.size)
DN_Memcpy(response->body.data, fetch->data, fetch->numBytes);
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
DN_AtomicAddU32(&response->done, 1);
}
#endif
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)
{
if (!response || !arena)
return;
response->arena = arena;
response->builder.arena = response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena;
DN_Arena *scratch = &response->scratch_arena;
DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1);
DN_DEFER { DN_TCScratchEnd(&scratch_); };
if (!scratch)
scratch = &scratch_.arena;
#if defined(DN_PLATFORM_EMSCRIPTEN)
emscripten_fetch_attr_t fetch_attribs = {};
emscripten_fetch_attr_init(&fetch_attribs);
if (method.size >= sizeof(fetch_attribs.requestMethod)) {
response->error_msg =
DN_Str8FromFmtArena(arena,
"Request method in EM has a size limit of 31 characters, method was "
"'%.*s' which is %zu characters long",
DN_Str8PrintFmt(method),
method.size);
DN_CheckF(method.size < sizeof(fetch_attribs.requestMethod),
"%.*s",
DN_Str8PrintFmt(response->error_msg));
response->error_code = DN_Cast(DN_U32) - 1;
DN_AtomicAddU32(&response->done, 1);
return;
}
DN_Memcpy(fetch_attribs.requestMethod, method.data, method.size);
fetch_attribs.requestData = body.data;
fetch_attribs.requestDataSize = body.size;
fetch_attribs.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
fetch_attribs.onsuccess = DN_OS_HttpRequestEMFetchOnSuccessCallback;
fetch_attribs.onerror = DN_OS_HttpRequestEMFetchOnErrorCallback;
fetch_attribs.userData = response;
DN_Str8 url = DN_Str8FromFmtArena(scratch, "%.*s%.*s", DN_Str8PrintFmt(host), DN_Str8PrintFmt(path));
DN_LogInfoF("Initiating HTTP '%s' request to '%.*s' with payload '%.*s'",
fetch_attribs.requestMethod,
DN_Str8PrintFmt(url),
DN_Str8PrintFmt(body));
response->on_complete_semaphore = DN_OS_SemaphoreInit(0);
response->em_handle = emscripten_fetch(&fetch_attribs, url.data);
#else // #elif defined(DN_OS_WIN32)
DN_InvalidCodePathF("Unimplemented function");
#endif
}
DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response)
{
// NOTE: Cleanup
#if defined(DN_PLATFORM_EMSCRIPTEN)
if (response->em_handle) {
emscripten_fetch_close(response->em_handle);
response->em_handle = nullptr;
}
#endif // #elif defined(DN_OS_WIN32)
DN_MemListDeinit(response->tmp_arena.mem);
DN_OS_SemaphoreDeinit(&response->on_complete_semaphore);
*response = {};
}
+12 -212
View File
@@ -395,15 +395,9 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
if (!file || !file->handle || file->error || !buffer || size <= 0)
return result;
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
if (!DN_Check(size <= (unsigned long)-1)) {
DN_Str8x32 buffer_size_str8 = DN_Str8x32FromByteCountU64Auto(size);
DN_ErrSinkAppendF(
err,
1 /*error_code*/,
"Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO",
DN_Str8PrintFmt(buffer_size_str8));
DN_TCScratchEnd(&scratch);
if (size > ULONG_MAX) {
DN_Str8x32 desc = DN_Str8x32FromByteCountU64Auto(size);
DN_ErrSinkAppendF(err, 1 /*error_code*/, "Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO", DN_Str8PrintFmt(desc));
return result;
}
@@ -414,6 +408,7 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
/*LPDWORD lpNumberOfByesRead*/ &bytes_read,
/*LPOVERLAPPED lpOverlapped*/ nullptr);
if (read_result == 0) {
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
DN_ErrSinkAppendF(err, win_error.code, "Failed to read data from file: (%u) %.*s", win_error.code, DN_Str8PrintFmt(win_error.msg));
DN_TCScratchEnd(&scratch);
@@ -421,6 +416,7 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
}
if (bytes_read != size) {
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
DN_ErrSinkAppendF(
err,
@@ -436,7 +432,6 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
result.bytes_read = bytes_read;
result.success = true;
DN_TCScratchEnd(&scratch);
return result;
}
@@ -732,7 +727,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle,
DN_ErrSinkAppendF(err, result.os_error_code, "Executed command failed to terminate: %.*s", DN_Str8PrintFmt(win_error.msg));
DN_TCScratchEnd(&scratch);
return result;
} else if (DN_Check(exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0)) {
} else if (exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0) {
// NOTE: Read stdout from process
// If the pipes are full, the process will block. We periodically
// flush the pipes to make sure this doesn't happen
@@ -1341,204 +1336,6 @@ DN_API void DN_OS_W32ThreadSetName(DN_Str8 name)
DN_TCScratchEnd(&scratch);
}
void DN_OS_HttpRequestWin32Callback(HINTERNET session, DWORD *dwContext, DWORD dwInternetStatus, VOID *lpvStatusInformation, DWORD dwStatusInformationLength)
{
(void)session;
(void)dwStatusInformationLength;
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) dwContext;
HINTERNET request = DN_Cast(HINTERNET) response->w32_request_handle;
DN_OSW32Error error = {};
DWORD const READ_BUFFER_SIZE = DN_Megabytes(1);
if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESOLVING_NAME) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_NAME_RESOLVED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDING_REQUEST) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_SENT) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CREATED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DETECTING_PROXY) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REDIRECT) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE) {
DWORD status = 0;
DWORD status_size = sizeof(status_size);
if (WinHttpQueryHeaders(request,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&status,
&status_size,
WINHTTP_NO_HEADER_INDEX)) {
response->http_status = DN_Cast(uint16_t) status;
// NOTE: You can normally call into WinHttpQueryDataAvailable which means the kernel
// will buffer the response into a single buffer and return us the full size of the
// request.
//
// or
//
// You may call WinHttpReadData directly to write the memory into our buffer directly.
// This is advantageous to avoid a copy from the kernel buffer into our buffer. If the
// end user application knows the typical payload size then they can optimise for this
// to prevent unnecessary allocation on the user side.
void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No);
if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr))
error = DN_OS_W32LastError(&response->tmp_arena);
} else {
error = DN_OS_W32LastError(&response->tmp_arena);
}
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE) {
DWORD bytes_read = dwStatusInformationLength;
if (bytes_read) {
DN_Str8 prev_buffer = DN_Str8FromPtr(DN_Cast(char *) lpvStatusInformation, bytes_read);
DN_Str8BuilderAppendRef(&response->builder, prev_buffer);
void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No);
if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr))
error = DN_OS_W32LastError(&response->tmp_arena);
}
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR) {
WINHTTP_ASYNC_RESULT *async_result = DN_Cast(WINHTTP_ASYNC_RESULT *) lpvStatusInformation;
error = DN_OS_W32ErrorCodeToMsg(&response->tmp_arena, DN_Cast(DN_U32) async_result->dwError);
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE) {
if (!WinHttpReceiveResponse(request, 0))
error = DN_OS_W32LastError(&response->tmp_arena);
}
// NOTE: If the request handle is missing, then, the response has been freed.
// MSDN says that this callback can still be called after closing the handle
// and trigger the WINHTTP_CALLBACK_STATUS_REQUEST_ERROR.
if (request) {
bool read_complete = dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE && dwStatusInformationLength == 0;
if (read_complete)
response->body = DN_Str8FromStr8BuilderArena(&response->builder, response->arena);
if (read_complete || dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR || error.code) {
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
DN_AtomicAddU32(&response->done, 1);
}
if (error.code) {
response->error_code = error.code;
response->error_msg = error.msg;
}
}
}
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)
{
if (!response || !arena)
return;
response->arena = arena;
response->builder = DN_Str8BuilderFromArena(response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena);
DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1);
if (!response->scratch_arena.mem)
response->scratch_arena = scratch_.arena;
DN_OSW32Error error = {};
DN_DEFER
{
response->error_msg = error.msg;
response->error_code = error.code;
if (error.code) {
// NOTE: 'Wait' handles failures gracefully, skipping the wait and
// cleans up the request
DN_OS_HttpRequestWait(response);
DN_AtomicAddU32(&response->done, 1);
}
DN_TCScratchEnd(&scratch_);
};
response->w32_request_session = WinHttpOpen(nullptr /*user agent*/, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC);
if (!response->w32_request_session) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DWORD callback_flags = WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE |
WINHTTP_CALLBACK_STATUS_READ_COMPLETE |
WINHTTP_CALLBACK_STATUS_REQUEST_ERROR |
WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE;
if (WinHttpSetStatusCallback(response->w32_request_session,
DN_Cast(WINHTTP_STATUS_CALLBACK) DN_OS_HttpRequestWin32Callback,
callback_flags,
DN_Cast(DWORD_PTR) nullptr /*dwReserved*/) == WINHTTP_INVALID_STATUS_CALLBACK) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DN_Str16 host16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, host);
response->w32_request_connection = WinHttpConnect(response->w32_request_session, host16.data, secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0 /*reserved*/);
if (!response->w32_request_connection) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DN_Str16 method16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, method);
DN_Str16 path16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, path);
response->w32_request_handle = WinHttpOpenRequest(response->w32_request_connection,
method16.data,
path16.data,
nullptr /*version*/,
nullptr /*referrer*/,
nullptr /*accept types*/,
secure ? WINHTTP_FLAG_SECURE : 0);
if (!response->w32_request_handle) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DN_Str16 headers16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, headers);
response->on_complete_semaphore = DN_OS_SemaphoreInit(0);
if (!WinHttpSendRequest(response->w32_request_handle,
headers16.data,
DN_Cast(DWORD) headers16.size,
body.data /*optional data*/,
DN_Cast(DWORD) body.size /*optional length*/,
DN_Cast(DWORD) body.size /*total content length*/,
DN_Cast(DWORD_PTR) response)) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
}
DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response)
{
// NOTE: Cleanup
// NOTE: These calls are synchronous even when the HTTP request is async.
WinHttpCloseHandle(response->w32_request_handle);
WinHttpCloseHandle(response->w32_request_connection);
WinHttpCloseHandle(response->w32_request_session);
response->w32_request_session = nullptr;
response->w32_request_connection = nullptr;
response->w32_request_handle = nullptr;
DN_MemListDeinit(response->tmp_arena.mem);
DN_OS_SemaphoreDeinit(&response->on_complete_semaphore);
*response = {};
}
// NOTE: DN_OS_W32
DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code)
{
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
@@ -1638,7 +1435,8 @@ DN_API DN_Str16 DN_OS_W32Str8ToStr16(DN_Arena *arena, DN_Str8 src)
return result;
int chars_written = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, buffer, required_size);
if (DN_Check(chars_written == required_size)) {
DN_Assert(chars_written == required_size);
if (chars_written == required_size) {
result.data = buffer;
result.size = chars_written;
result.data[result.size] = 0;
@@ -1700,7 +1498,8 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8(DN_Arena *arena, DN_Str16 src)
DN_Str8 buffer = DN_Str8AllocArena(required_size, DN_ZMem_No, &temp);
if (buffer.size) {
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr);
if (DN_Check(chars_written == required_size)) {
DN_Assert(chars_written == required_size);
if (chars_written == required_size) {
result = buffer;
result.data[result.size] = 0;
}
@@ -1730,7 +1529,8 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap(DN_Str16 src)
return result;
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr);
if (DN_Check(chars_written == required_size)) {
DN_Assert(chars_written == required_size);
if (chars_written == required_size) {
result = buffer;
result.data[result.size] = 0;
} else {