//////////////////////////////// //~ Win32 headers #pragma warning(push, 0) # define UNICODE # define WIN32_LEAN_AND_MEAN # include # include # include # include # include # include # include # include # include # include # include #pragma warning(pop) //////////////////////////////// //~ Ticket mutex types Struct(P_W32_TicketMutex) { Atomic64Padded ticket; Atomic64Padded serving; }; //////////////////////////////// //~ Thread types #define P_W32_ThreadStackSize Kibi(64) #define P_W32_ThreadDef(name, arg_name) void name(void *arg_name) typedef P_W32_ThreadDef(P_W32_ThreadFunc, data); Struct(P_W32_Thread) { P_W32_ThreadFunc *entry_point; void *thread_data; char thread_name_cstr[256]; wchar_t thread_name_wstr[256]; i32 profiler_group; P_W32_Thread *next; P_W32_Thread *prev; HANDLE handle; }; //////////////////////////////// //~ Wait list types AlignedStruct(P_W32_WaitList, 64) { u64 value; i16 first_waiter; i16 last_waiter; i32 num_waiters; P_W32_WaitList *next_in_bin; P_W32_WaitList *prev_in_bin; }; StaticAssert(alignof(P_W32_WaitList) == 64); /* Avoid false sharing */ AlignedStruct(P_W32_WaitBin, 64) { P_W32_WaitList *first_wait_list; P_W32_WaitList *last_wait_list; P_W32_WaitList *first_free_wait_list; P_W32_TicketMutex lock; }; StaticAssert(alignof(P_W32_WaitBin) == 64); /* Avoid false sharing */ //////////////////////////////// //~ Fiber types #define P_W32_FiberStackSize Mebi(4) #define P_W32_FiberNamePrefixCstr "Fiber [" #define P_W32_FiberNameSuffixCstr "]" #define P_W32_FiberNameMaxSize 64 //- Yield param typedef i32 P_W32_YieldKind; enum { P_W32_YieldKind_None, P_W32_YieldKind_Done, P_W32_YieldKind_Wait, P_W32_YieldKind_Count }; Struct(P_W32_YieldParam) { P_W32_YieldKind kind; union { struct { volatile void *addr; void *cmp; u32 size; i64 timeout_ns; } wait; }; }; //- Fiber AlignedStruct(P_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 */ /* ---------------------------------------------------- */ u8 _pad1[8]; /* 08 bytes (padding) */ /* ---------------------------------------------------- */ u8 _pad2[8]; /* 08 bytes (padding) */ /* ---------------------------------------------------- */ /* -------------------- Cache line -------------------- */ /* ---------------------------------------------------- */ P_JobFunc *job_func; /* 08 bytes */ /* ---------------------------------------------------- */ void *job_sig; /* 08 bytes */ /* ---------------------------------------------------- */ i32 job_id; /* 04 bytes */ i16 job_pool; /* 02 bytes */ i16 job_priority; /* 02 bytes */ /* ---------------------------------------------------- */ P_Counter *job_counter; /* 08 bytes */ /* ---------------------------------------------------- */ P_W32_YieldParam *yield_param; /* 08 bytes */ /* ---------------------------------------------------- */ u8 _pad3[24]; /* 24 bytes (padding) */ }; StaticAssert(sizeof(P_W32_Fiber) == 128); /* Padding validation (increase if necessary) */ StaticAssert(alignof(P_W32_Fiber) == 64); /* Avoid false sharing */ StaticAssert(offsetof(P_W32_Fiber, wake_lock) % 4 == 0); /* Atomic must be aligned */ //////////////////////////////// //~ Job queue types //- Worker ctx AlignedStruct(P_W32_WorkerCtx, 64) { P_Pool pool_kind; i32 id; }; //- Job info Struct(P_W32_JobInfo) { i32 num_dispatched; i32 count; P_JobFunc *func; void *sig; P_Counter *counter; i16 fiber_id; /* If the job is being resumed from a yield */ P_W32_JobInfo *next; }; //- Job queue AlignedStruct(P_W32_JobQueue, 64) { P_W32_TicketMutex lock; Arena *arena; P_W32_JobInfo *first; P_W32_JobInfo *last; P_W32_JobInfo *first_free; }; //- Job pool AlignedStruct(P_W32_JobPool, 64) { /* Jobs */ P_W32_JobQueue job_queues[P_Priority_Count]; P_W32_TicketMutex free_fibers_lock; i16 first_free_fiber_id; /* Workers */ Atomic32Padded workers_shutdown; Atomic64Padded num_jobs_in_queue; P_W32_TicketMutex workers_wake_lock; i32 num_worker_threads; i32 thread_priority; u64 thread_affinity_mask; b32 thread_is_audio; Arena *worker_threads_arena; P_W32_Thread **worker_threads; P_W32_WorkerCtx *worker_contexts; }; //////////////////////////////// //~ Window types typedef i32 P_W32_CursorFlag; enum { P_W32_CursorFlag_None = (0 << 0), P_W32_CursorFlag_Position = (1 << 0), P_W32_CursorFlag_Hide = (1 << 1), P_W32_CursorFlag_Show = (1 << 2), P_W32_CursorFlag_EnableClip = (1 << 3), P_W32_CursorFlag_DisableClip = (1 << 4) }; Struct(P_W32_Window) { u32 flags; HWND hwnd; P_Counter ready_fence; u16 utf16_high_surrogate_last_input; P_Mutex settings_mutex; P_WindowSettings settings; i32 monitor_width; i32 monitor_height; /* NOTE: width & height are unaffected by window minimization (they retain * their pre-minimized values) */ i32 x, y, width, height; /* FIXME: Use a cmd buffer for updating cursor (and maybe also settings). * Current setup means cursor_set calls can be applied out of order */ u32 cursor_set_flags; Vec2 cursor_set_position; Rect cursor_clip_bounds; Atomic32 topmost_toggles; b32 is_topmost; P_Mutex event_arena_swp_mutex; i32 current_event_arena_index; Arena *event_arenas[2]; P_W32_Thread *window_thread; Atomic32 shutdown; P_W32_Window *next_free; }; //////////////////////////////// //~ Watch types Struct(P_W32_Watch) { HANDLE dir_handle; HANDLE wake_handle; P_W32_Watch *next_free; u8 results_buff[Kibi(64)]; }; //////////////////////////////// //~ Address types Struct(P_W32_Address) { i32 size; i32 family; union { struct sockaddr_storage sas; struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; }; }; //////////////////////////////// //~ Sock types Struct(P_W32_Sock) { SOCKET sock; P_W32_Sock *next_free; }; //////////////////////////////// //~ Shared state #define P_W32_WindowClassName L"power_play_window_class" /* Assume scheduler cycle is 20hz at start to be conservative */ #define P_W32_DefaultSchedulerPeriodNs 50000000 #define P_W32_NumRollingSchedulerPeriods 1000 #define P_W32_NumWaitAddrBins 16384 #define P_W32_NumWaitTimeBins 1024 #define P_W32_MaxOnExitFuncs 1024 /* Arbitrary threshold for determining when to fall back from a looped WakeByAddressSingle to WakeByAddressAll */ #define P_W32_WakeAllThreshold 16 Struct(P_W32_SharedCtx) { SYSTEM_INFO info; i64 timer_start_qpc; i64 ns_per_qpc; u32 main_thread_id; Atomic32 shutdown; wchar_t cmdline_args_wstr[8192]; //- Application control flow Atomic32 panicking; wchar_t panic_wstr[4096]; HANDLE panic_event; HANDLE startup_end_event; HANDLE exit_begin_event; HANDLE exit_end_event; //- Key lookup table P_Btn vk_btn_table[256]; //- Worker thread pool P_Mutex threads_mutex; Arena *threads_arena; P_W32_Thread *first_thread; P_W32_Thread *last_thread; P_W32_Thread *first_free_thread; //- Watches pool P_Mutex watches_mutex; Arena *watches_arena; P_W32_Watch *watches_first_free; //- Windows pool WNDCLASSEXW window_class; P_Mutex windows_mutex; Arena *windows_arena; P_W32_Window *first_free_window; //- Socket pool WSADATA wsa_data; Arena *socks_arena; P_Mutex socks_mutex; P_W32_Sock *first_free_sock; //- Exit funcs Atomic32 num_exit_funcs; P_ExitFunc *exit_funcs[P_W32_MaxOnExitFuncs]; //- Scheduler Atomic64Padded current_scheduler_cycle; Atomic64Padded current_scheduler_cycle_period_ns; //- Fibers P_W32_TicketMutex fibers_lock; i16 num_fibers; Arena *fiber_names_arena; P_W32_Fiber fibers[MaxFibers]; //- Wait lists Atomic64Padded waiter_wake_gen; P_W32_TicketMutex wait_lists_arena_lock; Arena *wait_lists_arena; //- Wait tables P_W32_WaitBin wait_addr_bins[P_W32_NumWaitAddrBins]; P_W32_WaitBin wait_time_bins[P_W32_NumWaitTimeBins]; //- Job pools P_W32_JobPool job_pools[P_Pool_Count]; }; extern P_W32_SharedCtx P_W32_shared_ctx; //////////////////////////////// //~ Ticket mutex operations void P_W32_LockTicketMutex(P_W32_TicketMutex *tm); void P_W32_UnlockTicketMutex(P_W32_TicketMutex *tm); //////////////////////////////// //~ Thread operations DWORD WINAPI P_W32_Win32ThreadProc(LPVOID vt); P_W32_Thread *P_W32_AllocThread(P_W32_ThreadFunc *entry_point, void *thread_data, String thread_name, i32 profiler_group); b32 P_W32_TryReleaseThread(P_W32_Thread *thread, f32 timeout_seconds); void P_W32_WaitReleaseThread(P_W32_Thread *thread); //////////////////////////////// //~ Wait list operations void P_W32_WakeLockedFibers(i32 num_fibers, P_W32_Fiber **fibers); void P_W32_WakeByAddress(void *addr, i32 count); void P_W32_WakeByTime(u64 time); //////////////////////////////// //~ Fiber operations P_W32_Fiber *P_W32_AllocFiber(P_W32_JobPool *pool); void P_W32_ReleaseFiber(P_W32_JobPool *pool, P_W32_Fiber *fiber); ForceInline P_W32_Fiber *P_W32_FiberFromId(i16 id); ForceNoInline void P_W32_FiberResume(P_W32_Fiber *fiber); void P_W32_YieldFiber(P_W32_Fiber *fiber, P_W32_Fiber *parent_fiber); void P_W32_FiberEntryPoint(void *id_ptr); //////////////////////////////// //~ Workers P_W32_ThreadDef(P_W32_JobWorkerEntryFunc, worker_ctx_arg); P_W32_ThreadDef(P_W32_JobSchedulerEntryFunc, _); //////////////////////////////// //~ Time operations P_DateTime P_W32_DateTimeFromWin32SystemTime(SYSTEMTIME st); //////////////////////////////// //~ File system operations String P_W32_StringFromWin32Path(Arena *arena, wchar_t *src); //////////////////////////////// //~ Window operations P_W32_Window *P_W32_AllocWindow(void); void P_W32_ReleaseWindow(P_W32_Window *window); HWND P_W32_InitWindow(P_W32_Window *window); //- Window settings void P_W32_UpdateWindowFromSystem(P_W32_Window *window); void P_W32_UpdateWindowFromSettings(P_W32_Window *window, P_WindowSettings *settings); //- Window thread P_W32_ThreadDef(P_W32_WindowThreadEntryFunc, arg); void P_W32_ProcessWindowEvent(P_W32_Window *window, P_WindowEvent event); void P_W32_WakeWindow(P_W32_Window *window); LRESULT CALLBACK P_W32_Win32WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); //////////////////////////////// //~ Address operations P_W32_Address P_W32_Win32AddressFromPlatformAddress(P_Address addr); P_W32_Address P_W32_ConvertAnyaddrToLocalhost(P_W32_Address addr); P_Address P_W32_PlatformAddressFromWin32Address(P_W32_Address ws_addr); //////////////////////////////// //~ Entry point void P_W32_InitBtnTable(void); P_JobDef(P_W32_AppStartupJob, _); P_JobDef(P_W32_AppShutdownJob, _);