466 lines
12 KiB
C
466 lines
12 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)
|
|
|
|
////////////////////////////////
|
|
//~ 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, _);
|