Dqn/dqn_thread_context.cpp

128 lines
5.5 KiB
C++

#pragma once
#include "dqn.h"
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
// \__$$ __|$$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ __$$\
// $$ | $$ | $$ |$$ | $$ |$$ | $$ / $$ |$$ | $$ |
// $$ | $$$$$$$$ |$$$$$$$ |$$$$$\ $$$$$$$$ |$$ | $$ |
// $$ | $$ __$$ |$$ __$$< $$ __| $$ __$$ |$$ | $$ |
// $$ | $$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ |
// $$ | $$ | $$ |$$ | $$ |$$$$$$$$\ $$ | $$ |$$$$$$$ |
// \__| \__| \__|\__| \__|\________|\__| \__|\_______/
//
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$$$\
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ _____|$$ | $$ |\__$$ __|
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ | \$$\ $$ | $$ |
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$\ \$$$$ / $$ |
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __| $$ $$< $$ |
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ /\$$\ $$ |
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$$$$$$$\ $$ / $$ | $$ |
// \______/ \______/ \__| \__| \__| \________|\__| \__| \__|
//
// dqn_thread_context.cpp
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
DQN_THREAD_LOCAL Dqn_ThreadContext g_dqn_thread_context;
// NOTE: [$TCTX] Dqn_ThreadContext /////////////////////////////////////////////////////////////////
Dqn_Scratch::Dqn_Scratch(Dqn_ThreadContext *context, uint8_t context_index)
{
arena = context->scratch_arenas[context_index];
temp_mem = Dqn_Arena_TempMemBegin(arena);
destructed = false;
}
Dqn_Scratch::~Dqn_Scratch()
{
DQN_ASSERT(destructed == false);
Dqn_Arena_TempMemEnd(temp_mem);
destructed = true;
}
DQN_API bool Dqn_Thread_ContextIsInit()
{
bool result = g_dqn_thread_context.init;
return result;
}
DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get()
{
Dqn_ThreadContext *result = &g_dqn_thread_context;
if (result->init)
return result;
result->init = true;
Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog;
DQN_HARD_ASSERTF(g_dqn_library && g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init()");
// NOTE: Setup scratch arenas //////////////////////////////////////////////////////////////////
Dqn_TicketMutex_Begin(&g_dqn_library->thread_context_init_mutex);
DQN_FOR_UINDEX (index, DQN_ARRAY_UCOUNT(result->scratch_arenas)) {
// NOTE: We allocate arenas so that they all come from the memory
// allocated from the address space of this library. This allows the
// library to be used across DLL boundaries for example as long as
// across the boundaries the same g_dqn_library instance is shared.
//
// On unload of the DLL, the address space is deallocated. If we stored
// these as TLS stack variables, these arenas would persist and point to
// invalid memory addresses.
Dqn_FStr8<128> label = Dqn_FStr8_InitF<128>("T%05u Scratch %zu", Dqn_OS_ThreadID(), index);
// NOTE: Hence here we search for the arena. If it already exists then
// we are in that DLL boundary situation where the TLS data has been
// reinitialised and zero-ed out. We will try and find the matching
// arena in the catalog and re-use it.
//
// NOTE: This operation is so infrequent and the number of arenas one
// has in their program should be low that a string look-up should be
// cheap and fine.
Dqn_ArenaCatalogItem *catalog_item = Dqn_ArenaCatalog_Find(catalog, Dqn_FStr8_ToStr8(&label));
if (catalog_item == &catalog->sentinel) {
Dqn_Arena *scratch = Dqn_ArenaCatalog_AllocLabelCopy(catalog, 0, 0, Dqn_ArenaFlag_AllocCanLeak, Dqn_FStr8_ToStr8(&label));
result->scratch_arenas[index] = scratch;
} else {
// NOTE: Reuse the arena
result->scratch_arenas[index] = catalog_item->arena;
}
}
// NOTE: Setup error sink //////////////////////////////////////////////////////////////////////
{
Dqn_FStr8<128> label = Dqn_FStr8_InitF<128>("T%05u Error Sink", Dqn_OS_ThreadID());
Dqn_ArenaCatalogItem *catalog_item = Dqn_ArenaCatalog_Find(catalog, Dqn_FStr8_ToStr8(&label));
if (catalog_item == &catalog->sentinel) {
result->error_sink_arena = Dqn_ArenaCatalog_AllocLabelCopy(catalog, 0, 0, Dqn_ArenaFlag_AllocCanLeak, Dqn_FStr8_ToStr8(&label));
} else {
// NOTE: Reuse the arena
result->error_sink_arena = catalog_item->arena;
}
result->error_sink.arena = result->error_sink_arena;
}
Dqn_TicketMutex_End(&g_dqn_library->thread_context_init_mutex);
return result;
}
// TODO: Is there a way to handle conflict arenas without the user needing to
// manually pass it in?
DQN_API Dqn_Scratch Dqn_Scratch_Get(void const *conflict_arena)
{
Dqn_ThreadContext *context = Dqn_ThreadContext_Get();
uint8_t context_index = (uint8_t)-1;
for (uint8_t index = 0; index < DQN_ARRAY_UCOUNT(context->scratch_arenas); index++) {
Dqn_Arena *arena = context->scratch_arenas[index];
if (!conflict_arena || arena != conflict_arena) {
context_index = index;
break;
}
}
DQN_ASSERT(context_index != (uint8_t)-1);
return Dqn_Scratch(context, context_index);
}