128 lines
5.5 KiB
C++
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);
|
|
}
|