Dqn/OS/dn_os_tls.h
2025-05-12 17:09:03 +10:00

140 lines
7.2 KiB
C

#if !defined(DN_OS_TLS_H)
#define DN_OS_TLS_H
// NOTE: DN_OSErrSink /////////////////////////////////////////////////////////////////////////////
enum DN_OSErrSinkMode
{
DN_OSErrSinkMode_Nil, // Default behaviour to accumulate errors into the sink
DN_OSErrSinkMode_DebugBreakOnEndAndLog, // Debug break (int3) when error is encountered and the sink is ended by the 'end and log' functions.
DN_OSErrSinkMode_ExitOnError, // When an error is encountered, exit the program with the error code of the error that was caught.
};
struct DN_OSErrSinkMsg
{
DN_I32 error_code;
DN_Str8 msg;
DN_CallSite call_site;
DN_OSErrSinkMsg *next;
DN_OSErrSinkMsg *prev;
};
struct DN_OSErrSinkNode
{
DN_CallSite call_site; // Call site that the node was created
DN_OSErrSinkMode mode; // Controls how the sink behaves when an error is registered onto the sink.
DN_OSErrSinkMsg *msg_sentinel; // List of error messages accumulated for the current scope
DN_U64 arena_pos; // Position to reset the arena when the scope is ended
};
struct DN_OSErrSink
{
DN_Arena * arena; // Dedicated allocator from the thread's local storage
DN_OSErrSinkNode stack[128]; // Each entry contains errors accumulated between a [begin, end] region of the active sink.
DN_USize stack_size;
};
enum DN_OSTLSArena
{
DN_OSTLSArena_Main, // NOTE: Arena for permanent allocations
DN_OSTLSArena_ErrorSink, // NOTE: Arena for logging error information for this thread
// NOTE: Per-thread scratch arenas (2 to prevent aliasing)
DN_OSTLSArena_TMem0,
DN_OSTLSArena_TMem1,
DN_OSTLSArena_Count,
};
struct DN_OSTLS
{
DN_B32 init; // Flag to track if TLS has been initialised
DN_U64 thread_id;
DN_CallSite call_site; // Stores call-site information when requested by thread
DN_OSErrSink err_sink; // Error handling state
DN_Arena arenas[DN_OSTLSArena_Count]; // Default arenas that the thread has access to implicitly
DN_Arena * arena_stack[8]; // Active stack of arenas push/popped arenas on into the TLS
DN_USize arena_stack_index;
DN_Arena * frame_arena;
char name[64];
DN_U8 name_size;
};
// Push the temporary memory arena when retrieved, popped when the arena goes
// out of scope. Pushed arenas are used automatically as the allocator in TLS
// suffixed function.
enum DN_OSTLSPushTMem
{
DN_OSTLSPushTMem_No,
DN_OSTLSPushTMem_Yes,
};
struct DN_OSTLSTMem
{
DN_OSTLSTMem(DN_OSTLS *context, uint8_t context_index, DN_OSTLSPushTMem push_scratch);
~DN_OSTLSTMem();
DN_Arena *arena;
DN_B32 destructed;
DN_OSTLSPushTMem push_arena;
DN_ArenaTempMem temp_mem;
};
struct DN_OSTLSInitArgs
{
DN_U64 reserve;
DN_U64 commit;
DN_U64 err_sink_reserve;
DN_U64 err_sink_commit;
};
// NOTE: DN_OSTLS ////////////////////////////////////////////////////////////////////////////////////
DN_API void DN_OS_TLSInit (DN_OSTLS *tls, DN_OSTLSInitArgs args);
DN_API void DN_OS_TLSDeinit (DN_OSTLS *tls);
DN_API DN_OSTLS * DN_OS_TLSGet ();
DN_API void DN_OS_TLSSetCurrentThreadTLS (DN_OSTLS *tls);
DN_API DN_Arena * DN_OS_TLSArena ();
DN_API DN_OSTLSTMem DN_OS_TLSGetTMem (void const *conflict_arena, DN_OSTLSPushTMem push_tmp_mem);
DN_API void DN_OS_TLSPushArena (DN_Arena *arena);
DN_API void DN_OS_TLSPopArena ();
DN_API DN_Arena * DN_OS_TLSTopArena ();
DN_API void DN_OS_TLSBeginFrame (DN_Arena *frame_arena);
DN_API DN_Arena * DN_OS_TLSFrameArena ();
#define DN_OS_TLSSaveCallSite do { DN_OS_TLSGet()->call_site = DN_CALL_SITE; } while (0)
#define DN_OS_TLSTMem(...) DN_OS_TLSGetTMem(__VA_ARGS__, DN_OSTLSPushTMem_No)
#define DN_OS_TLSPushTMem(...) DN_OS_TLSGetTMem(__VA_ARGS__, DN_OSTLSPushTMem_Yes)
// NOTE: DN_OS_ErrSink ////////////////////////////////////////////////////////////////////////////
DN_API DN_OSErrSink * DN_OS_ErrSinkBegin_ (DN_OSErrSinkMode mode, DN_CallSite call_site);
#define DN_OS_ErrSinkBegin(mode) DN_OS_ErrSinkBegin_(mode, DN_CALL_SITE)
#define DN_OS_ErrSinkBeginDefault() DN_OS_ErrSinkBegin(DN_OSErrSinkMode_Nil)
DN_API bool DN_OS_ErrSinkHasError (DN_OSErrSink *err);
DN_API DN_OSErrSinkMsg *DN_OS_ErrSinkEnd (DN_Arena *arena, DN_OSErrSink *err);
DN_API DN_Str8 DN_OS_ErrSinkEndStr8 (DN_Arena *arena, DN_OSErrSink *err);
DN_API void DN_OS_ErrSinkEndAndIgnore (DN_OSErrSink *err);
DN_API bool DN_OS_ErrSinkEndAndLogError_ (DN_OSErrSink *err, DN_CallSite call_site, DN_Str8 msg);
DN_API bool DN_OS_ErrSinkEndAndLogErrorFV_ (DN_OSErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API bool DN_OS_ErrSinkEndAndLogErrorF_ (DN_OSErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void DN_OS_ErrSinkEndAndExitIfErrorF_ (DN_OSErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void DN_OS_ErrSinkEndAndExitIfErrorFV_ (DN_OSErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void DN_OS_ErrSinkAppendFV_ (DN_OSErrSink *err, DN_U32 error_code, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void DN_OS_ErrSinkAppendF_ (DN_OSErrSink *err, DN_U32 error_code, DN_FMT_ATTRIB char const *fmt, ...);
#define DN_OS_ErrSinkEndAndLogError(err, err_msg) DN_OS_ErrSinkEndAndLogError_(err, DN_CALL_SITE, err_msg)
#define DN_OS_ErrSinkEndAndLogErrorFV(err, fmt, args) DN_OS_ErrSinkEndAndLogErrorFV_(err, DN_CALL_SITE, fmt, args)
#define DN_OS_ErrSinkEndAndLogErrorF(err, fmt, ...) DN_OS_ErrSinkEndAndLogErrorF_(err, DN_CALL_SITE, fmt, ##__VA_ARGS__)
#define DN_OS_ErrSinkEndAndExitIfErrorFV(err, exit_val, fmt, args) DN_OS_ErrSinkEndAndExitIfErrorFV_(err, DN_CALL_SITE, exit_val, fmt, args)
#define DN_OS_ErrSinkEndAndExitIfErrorF(err, exit_val, fmt, ...) DN_OS_ErrSinkEndAndExitIfErrorF_(err, DN_CALL_SITE, exit_val, fmt, ##__VA_ARGS__)
#define DN_OS_ErrSinkAppendFV(error, error_code, fmt, args) \
do { \
DN_OS_TLSSaveCallSite; \
DN_OS_ErrSinkAppendFV_(error, error_code, fmt, args); \
} while (0)
#define DN_OS_ErrSinkAppendF(error, error_code, fmt, ...) \
do { \
DN_OS_TLSSaveCallSite; \
DN_OS_ErrSinkAppendF_(error, error_code, fmt, ##__VA_ARGS__); \
} while (0)
#endif // defined(DN_OS_TLS_H)