296 lines
7.7 KiB
C
296 lines
7.7 KiB
C
////////////////////////////////
|
|
//~ Win32 headers
|
|
|
|
#pragma warning(push, 0)
|
|
# define UNICODE
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# include <Windows.h>
|
|
# include <WinSock2.h>
|
|
# include <TlHelp32.h>
|
|
# include <WS2tcpip.h>
|
|
# include <windowsx.h>
|
|
# include <ShlObj_core.h>
|
|
# include <fileapi.h>
|
|
# include <dwmapi.h>
|
|
# include <bcrypt.h>
|
|
# include <avrt.h>
|
|
# include <shellapi.h>
|
|
#pragma warning(pop)
|
|
|
|
////////////////////////////////
|
|
//~ Thread types
|
|
|
|
#define W32_ThreadStackSize Kibi(64)
|
|
#define W32_ThreadDef(name, arg_name) void name(void *arg_name)
|
|
typedef W32_ThreadDef(W32_ThreadFunc, data);
|
|
|
|
Struct(W32_Thread)
|
|
{
|
|
W32_ThreadFunc *entry_point;
|
|
void *thread_data;
|
|
char thread_name_cstr[256];
|
|
wchar_t thread_name_wstr[256];
|
|
i32 profiler_group;
|
|
|
|
W32_Thread *next;
|
|
W32_Thread *prev;
|
|
|
|
HANDLE handle;
|
|
};
|
|
|
|
////////////////////////////////
|
|
//~ Wait list types
|
|
|
|
AlignedStruct(W32_WaitList, 64)
|
|
{
|
|
u64 value;
|
|
i16 first_waiter;
|
|
i16 last_waiter;
|
|
i32 num_waiters;
|
|
W32_WaitList *next_in_bin;
|
|
W32_WaitList *prev_in_bin;
|
|
};
|
|
StaticAssert(alignof(W32_WaitList) == 64); /* Avoid false sharing */
|
|
|
|
AlignedStruct(W32_WaitBin, 64)
|
|
{
|
|
W32_WaitList *first_wait_list;
|
|
W32_WaitList *last_wait_list;
|
|
W32_WaitList *first_free_wait_list;
|
|
TicketMutex lock;
|
|
};
|
|
StaticAssert(alignof(W32_WaitBin) == 64); /* Avoid false sharing */
|
|
|
|
////////////////////////////////
|
|
//~ Fiber types
|
|
|
|
#define W32_FiberStackSize Mebi(4)
|
|
#define W32_FiberNamePrefixCstr "Fiber ["
|
|
#define W32_FiberNameSuffixCstr "]"
|
|
#define W32_FiberNameMaxSize 64
|
|
|
|
//- Yield param
|
|
typedef i32 W32_YieldKind; enum
|
|
{
|
|
W32_YieldKind_None,
|
|
W32_YieldKind_Done,
|
|
W32_YieldKind_Wait,
|
|
|
|
W32_YieldKind_Count
|
|
};
|
|
|
|
Struct(W32_YieldParam)
|
|
{
|
|
W32_YieldKind kind;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
volatile void *addr;
|
|
void *cmp;
|
|
u32 size;
|
|
i64 timeout_ns;
|
|
} wait;
|
|
};
|
|
};
|
|
|
|
//- Fiber
|
|
AlignedStruct(W32_Fiber, 64)
|
|
{
|
|
/* ---------------------------------------------------- */
|
|
void *addr; /* 08 bytes */
|
|
/* ---------------------------------------------------- */
|
|
char *name_cstr; /* 08 bytes */
|
|
/* ---------------------------------------------------- */
|
|
Atomic32 wake_lock; /* 04 bytes (4 byte alignment) */
|
|
i16 id; /* 02 bytes */
|
|
i16 parent_id; /* 02 bytes */
|
|
/* ---------------------------------------------------- */
|
|
u64 wait_addr; /* 08 bytes */
|
|
/* ---------------------------------------------------- */
|
|
u64 wait_time; /* 08 bytes */
|
|
/* ---------------------------------------------------- */
|
|
i16 next_addr_waiter; /* 02 bytes */
|
|
i16 prev_addr_waiter; /* 02 bytes */
|
|
i16 next_time_waiter; /* 02 bytes */
|
|
i16 prev_time_waiter; /* 02 bytes */
|
|
/* ---------------------------------------------------- */
|
|
GenericJobDesc *job_desc; /* 08 bytes */
|
|
/* ---------------------------------------------------- */
|
|
/* -------------------- Cache line -------------------- */
|
|
/* ---------------------------------------------------- */
|
|
i32 job_id; /* 04 bytes */
|
|
i16 job_pool; /* 02 bytes */
|
|
i16 job_priority; /* 02 bytes */
|
|
/* ---------------------------------------------------- */
|
|
W32_YieldParam *yield_param; /* 08 bytes */
|
|
/* ---------------------------------------------------- */
|
|
u8 _pad0[48]; /* 48 bytes (padding) */
|
|
|
|
};
|
|
StaticAssert(sizeof(W32_Fiber) == 128); /* Padding validation (increase if necessary) */
|
|
StaticAssert(alignof(W32_Fiber) == 64); /* Verify alignment to avoid false sharing */
|
|
StaticAssert(offsetof(W32_Fiber, wake_lock) % 4 == 0); /* Atomic must be aligned */
|
|
|
|
////////////////////////////////
|
|
//~ Job queue types
|
|
|
|
//- Worker ctx
|
|
AlignedStruct(W32_WorkerCtx, 64)
|
|
{
|
|
JobPool pool_kind;
|
|
i32 id;
|
|
};
|
|
|
|
//- Job info
|
|
Struct(W32_JobInfo)
|
|
{
|
|
GenericJobDesc *desc;
|
|
|
|
i32 count;
|
|
i32 num_dispatched;
|
|
i16 fiber_id; /* If the job is being resumed from a yield */
|
|
|
|
W32_JobInfo *next;
|
|
};
|
|
|
|
//- Job queue
|
|
AlignedStruct(W32_JobQueue, 64)
|
|
{
|
|
TicketMutex lock;
|
|
Arena *arena;
|
|
|
|
W32_JobInfo *first;
|
|
W32_JobInfo *last;
|
|
|
|
W32_JobInfo *first_free;
|
|
};
|
|
|
|
//- Job pool
|
|
AlignedStruct(W32_JobPool, 64)
|
|
{
|
|
/* Jobs */
|
|
W32_JobQueue job_queues[JobPriority_Count];
|
|
|
|
TicketMutex free_fibers_tm;
|
|
i16 first_free_fiber_id;
|
|
|
|
/* Workers */
|
|
Atomic32Padded workers_shutdown;
|
|
Atomic64Padded num_jobs_in_queue;
|
|
TicketMutex workers_wake_tm;
|
|
|
|
i32 num_worker_threads;
|
|
i32 thread_priority;
|
|
u64 thread_affinity_mask;
|
|
b32 thread_is_audio;
|
|
Arena *worker_threads_arena;
|
|
W32_Thread **worker_threads;
|
|
W32_WorkerCtx *worker_contexts;
|
|
};
|
|
|
|
////////////////////////////////
|
|
//~ Shared state
|
|
|
|
/* Assume scheduler cycle is 20hz at start to be conservative */
|
|
#define W32_DefaultSchedulerPeriodNs 50000000
|
|
#define W32_NumRollingSchedulerPeriods 1000
|
|
|
|
#define W32_NumWaitAddrBins 16384
|
|
#define W32_NumWaitTimeBins 1024
|
|
|
|
/* Arbitrary threshold for determining when to fall back from a looped WakeByAddressSingle to WakeByAddressAll */
|
|
#define W32_WakeAllThreshold 16
|
|
|
|
Struct(W32_SharedJobCtx)
|
|
{
|
|
i64 timer_start_qpc;
|
|
i64 ns_per_qpc;
|
|
Atomic32 shutdown;
|
|
|
|
//- Worker thread pool
|
|
TicketMutex threads_tm;
|
|
Arena *threads_arena;
|
|
W32_Thread *first_thread;
|
|
W32_Thread *last_thread;
|
|
W32_Thread *first_free_thread;
|
|
|
|
//- Scheduler
|
|
Atomic64Padded current_scheduler_cycle;
|
|
Atomic64Padded current_scheduler_cycle_period_ns;
|
|
|
|
//- Fibers
|
|
TicketMutex fibers_tm;
|
|
i16 num_fibers;
|
|
Arena *fiber_names_arena;
|
|
W32_Fiber fibers[MaxFibers];
|
|
|
|
//- Job descs
|
|
Arena *job_descs_arena;
|
|
TicketMutex job_descs_tm;
|
|
GenericJobDesc *first_free_job_desc;
|
|
|
|
//- Wait lists
|
|
Atomic64Padded waiter_wake_gen;
|
|
TicketMutex wait_lists_arena_tm;
|
|
Arena *wait_lists_arena;
|
|
|
|
//- Wait tables
|
|
W32_WaitBin wait_addr_bins[W32_NumWaitAddrBins];
|
|
W32_WaitBin wait_time_bins[W32_NumWaitTimeBins];
|
|
|
|
//- Job pools
|
|
W32_JobPool job_pools[JobPool_Count];
|
|
};
|
|
|
|
extern W32_SharedJobCtx W32_shared_job_ctx;
|
|
|
|
////////////////////////////////
|
|
//~ Startup
|
|
|
|
void StartupJobs(void);
|
|
|
|
////////////////////////////////
|
|
//~ Shutdown
|
|
|
|
void ShutdownJobs(void);
|
|
|
|
////////////////////////////////
|
|
//~ Thread operations
|
|
|
|
DWORD WINAPI W32_Win32ThreadProc(LPVOID vt);
|
|
W32_Thread *W32_AllocThread(W32_ThreadFunc *entry_point, void *thread_data, String thread_name, i32 profiler_group);
|
|
b32 W32_TryReleaseThread(W32_Thread *thread, f32 timeout_seconds);
|
|
void W32_WaitReleaseThread(W32_Thread *thread);
|
|
|
|
////////////////////////////////
|
|
//~ Wait list operations
|
|
|
|
void W32_WakeLockedFibers(i32 num_fibers, W32_Fiber **fibers);
|
|
void W32_WakeByAddress(void *addr, i32 count);
|
|
void W32_WakeByTime(u64 time);
|
|
|
|
////////////////////////////////
|
|
//~ Fiber operations
|
|
|
|
W32_Fiber *W32_AllocFiber(W32_JobPool *pool);
|
|
void W32_ReleaseFiber(W32_JobPool *pool, W32_Fiber *fiber);
|
|
ForceInline W32_Fiber *W32_FiberFromId(i16 id);
|
|
ForceNoInline void W32_FiberResume(W32_Fiber *fiber);
|
|
void W32_YieldFiber(W32_Fiber *fiber, W32_Fiber *parent_fiber);
|
|
|
|
////////////////////////////////
|
|
//~ Fiber entry
|
|
|
|
void W32_FiberEntryPoint(void *id_ptr);
|
|
|
|
////////////////////////////////
|
|
//~ Job worker entry
|
|
|
|
W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg);
|
|
|
|
////////////////////////////////
|
|
//~ Job scheduler entry
|
|
|
|
W32_ThreadDef(W32_JobSchedulerEntryFunc, _);
|