os_win32.c (11780B)
1 /* See LICENSE for license details. */ 2 3 #define OS_SHARED_MEMORY_NAME "Local\\ogl_beamformer_parameters" 4 5 #define OS_PATH_SEPARATOR_CHAR '\\' 6 #define OS_PATH_SEPARATOR "\\" 7 8 #include "util.h" 9 10 #define STD_INPUT_HANDLE -10 11 #define STD_OUTPUT_HANDLE -11 12 #define STD_ERROR_HANDLE -12 13 14 #define PAGE_READWRITE 0x04 15 #define MEM_COMMIT 0x1000 16 #define MEM_RESERVE 0x2000 17 18 #define GENERIC_WRITE 0x40000000 19 #define GENERIC_READ 0x80000000 20 21 #define FILE_SHARE_READ 0x00000001 22 #define FILE_MAP_ALL_ACCESS 0x000F001F 23 #define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 24 #define FILE_FLAG_OVERLAPPED 0x40000000 25 26 #define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 27 28 #define FILE_ACTION_MODIFIED 0x00000003 29 30 #define CREATE_ALWAYS 2 31 #define OPEN_EXISTING 3 32 33 #define THREAD_SET_LIMITED_INFORMATION 0x0400 34 35 /* NOTE: this is packed because the w32 api designers are dumb and ordered the members 36 * incorrectly. They worked around it be making the ft* members a struct {u32, u32} which 37 * is aligned on a 4-byte boundary. Then in their documentation they explicitly tell you not 38 * to cast to u64 because "it can cause alignment faults on 64-bit Windows" - go figure */ 39 typedef struct w32_file_info w32_file_info; 40 pack_struct(struct w32_file_info { 41 u32 dwFileAttributes; 42 u64 ftCreationTime; 43 u64 ftLastAccessTime; 44 u64 ftLastWriteTime; 45 u32 dwVolumeSerialNumber; 46 u32 nFileSizeHigh; 47 u32 nFileSizeLow; 48 u32 nNumberOfLinks; 49 u32 nFileIndexHigh; 50 u32 nFileIndexLow; 51 }); 52 53 typedef struct { 54 u32 next_entry_offset; 55 u32 action; 56 u32 filename_size; 57 u16 filename[]; 58 } w32_file_notify_info; 59 60 typedef struct { 61 uptr internal, internal_high; 62 union { 63 struct {u32 off, off_high;}; 64 iptr pointer; 65 }; 66 iptr event_handle; 67 } w32_overlapped; 68 69 typedef struct { 70 iptr io_completion_handle; 71 u64 timer_frequency; 72 } w32_context; 73 74 typedef enum { 75 W32_IO_FILE_WATCH, 76 W32_IO_PIPE, 77 } W32_IO_Event; 78 79 typedef struct { 80 u64 tag; 81 iptr context; 82 } w32_io_completion_event; 83 84 typedef struct { 85 iptr *semaphores; 86 u32 reserved_count; 87 } w32_shared_memory_context; 88 89 #define W32(r) __declspec(dllimport) r __stdcall 90 W32(b32) CloseHandle(iptr); 91 W32(b32) CopyFileA(c8 *, c8 *, b32); 92 W32(iptr) CreateFileA(c8 *, u32, u32, void *, u32, u32, void *); 93 W32(iptr) CreateFileMappingA(iptr, void *, u32, u32, u32, c8 *); 94 W32(iptr) CreateIoCompletionPort(iptr, iptr, uptr, u32); 95 W32(iptr) CreateSemaphoreA(iptr, i32, i32, c8 *); 96 W32(iptr) CreateThread(iptr, uz, iptr, iptr, u32, u32 *); 97 W32(b32) DeleteFileA(c8 *); 98 W32(void) ExitProcess(i32); 99 W32(b32) FreeLibrary(void *); 100 W32(i32) GetFileAttributesA(c8 *); 101 W32(b32) GetFileInformationByHandle(iptr, void *); 102 W32(i32) GetLastError(void); 103 W32(void *) GetModuleHandleA(c8 *); 104 W32(void *) GetProcAddress(void *, c8 *); 105 W32(b32) GetQueuedCompletionStatus(iptr, u32 *, uptr *, w32_overlapped **, u32); 106 W32(iptr) GetStdHandle(i32); 107 W32(void) GetSystemInfo(void *); 108 W32(void *) LoadLibraryA(c8 *); 109 W32(void *) MapViewOfFile(iptr, u32, u32, u32, u64); 110 W32(b32) QueryPerformanceCounter(u64 *); 111 W32(b32) QueryPerformanceFrequency(u64 *); 112 W32(b32) ReadDirectoryChangesW(iptr, u8 *, u32, b32, u32, u32 *, void *, void *); 113 W32(b32) ReadFile(iptr, u8 *, i32, i32 *, void *); 114 W32(b32) ReleaseSemaphore(iptr, i32, i32 *); 115 W32(i32) SetThreadDescription(iptr, u16 *); 116 W32(u32) WaitForSingleObject(iptr, u32); 117 W32(b32) WaitOnAddress(void *, void *, uz, u32); 118 W32(i32) WakeByAddressAll(void *); 119 W32(iptr) wglGetProcAddress(c8 *); 120 W32(b32) WriteFile(iptr, u8 *, i32, i32 *, void *); 121 W32(void *) VirtualAlloc(u8 *, iz, u32, u32); 122 123 #ifdef _DEBUG 124 function void * 125 os_get_module(char *name, Stream *e) 126 { 127 void *result = GetModuleHandleA(name); 128 if (!result && e) { 129 stream_append_s8s(e, s8("os_get_module(\""), c_str_to_s8(name), s8("\"): ")); 130 stream_append_i64(e, GetLastError()); 131 stream_append_byte(e, '\n'); 132 } 133 return result; 134 } 135 #endif 136 137 function OS_WRITE_FILE_FN(os_write_file) 138 { 139 i32 wlen = 0; 140 if (raw.len > 0 && raw.len <= U32_MAX) WriteFile(file, raw.data, (i32)raw.len, &wlen, 0); 141 return raw.len == wlen; 142 } 143 144 function no_return void 145 os_exit(i32 code) 146 { 147 ExitProcess(1); 148 unreachable(); 149 } 150 151 function no_return void 152 os_fatal(s8 msg) 153 { 154 os_write_file(GetStdHandle(STD_ERROR_HANDLE), msg); 155 os_exit(1); 156 unreachable(); 157 } 158 159 function u64 160 os_get_timer_frequency(void) 161 { 162 u64 result; 163 QueryPerformanceFrequency(&result); 164 return result; 165 } 166 167 function u64 168 os_get_timer_counter(void) 169 { 170 u64 result; 171 QueryPerformanceCounter(&result); 172 return result; 173 } 174 175 function iz 176 os_round_up_to_page_size(iz value) 177 { 178 struct { 179 u16 architecture; 180 u16 _pad1; 181 u32 page_size; 182 iz minimum_application_address; 183 iz maximum_application_address; 184 u64 active_processor_mask; 185 u32 number_of_processors; 186 u32 processor_type; 187 u32 allocation_granularity; 188 u16 processor_level; 189 u16 processor_revision; 190 } info; 191 GetSystemInfo(&info); 192 iz result = round_up_to(value, info.page_size); 193 return result; 194 } 195 196 function OS_ALLOC_ARENA_FN(os_alloc_arena) 197 { 198 Arena result = {0}; 199 capacity = os_round_up_to_page_size(capacity); 200 result.beg = VirtualAlloc(0, capacity, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); 201 if (!result.beg) 202 os_fatal(s8("os_alloc_arena: couldn't allocate memory\n")); 203 result.end = result.beg + capacity; 204 asan_poison_region(result.beg, result.end - result.beg); 205 return result; 206 } 207 208 function OS_READ_WHOLE_FILE_FN(os_read_whole_file) 209 { 210 s8 result = s8(""); 211 212 w32_file_info fileinfo; 213 iptr h = CreateFileA(file, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); 214 if (h >= 0 && GetFileInformationByHandle(h, &fileinfo)) { 215 iz filesize = (iz)fileinfo.nFileSizeHigh << 32; 216 filesize |= (iz)fileinfo.nFileSizeLow; 217 if (filesize <= U32_MAX) { 218 result = s8_alloc(arena, filesize); 219 i32 rlen; 220 if (!ReadFile(h, result.data, (i32)result.len, &rlen, 0) || rlen != result.len) 221 result = s8(""); 222 } 223 } 224 if (h >= 0) CloseHandle(h); 225 226 return result; 227 } 228 229 function OS_WRITE_NEW_FILE_FN(os_write_new_file) 230 { 231 b32 result = 0; 232 iptr h = CreateFileA(fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); 233 if (h >= 0) { 234 while (raw.len > 0) { 235 s8 chunk = raw; 236 chunk.len = MIN(chunk.len, (iz)GB(2)); 237 result = os_write_file(h, chunk); 238 if (!result) break; 239 raw = s8_cut_head(raw, chunk.len); 240 } 241 CloseHandle(h); 242 } 243 return result; 244 } 245 246 function b32 247 os_file_exists(char *path) 248 { 249 b32 result = GetFileAttributesA(path) != -1; 250 return result; 251 } 252 253 function SharedMemoryRegion 254 os_create_shared_memory_area(Arena *arena, char *name, u32 lock_count, iz requested_capacity) 255 { 256 iz capacity = os_round_up_to_page_size(requested_capacity); 257 assert(capacity <= (iz)U32_MAX); 258 SharedMemoryRegion result = {0}; 259 iptr h = CreateFileMappingA(-1, 0, PAGE_READWRITE, 0, (u32)capacity, name); 260 if (h != INVALID_FILE) { 261 void *new = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, (u32)capacity); 262 if (new) { 263 w32_shared_memory_context *ctx = push_struct(arena, typeof(*ctx)); 264 ctx->semaphores = push_array(arena, typeof(*ctx->semaphores), lock_count); 265 ctx->reserved_count = lock_count; 266 result.os_context = (iptr)ctx; 267 result.region = new; 268 269 Stream sb = arena_stream(*arena); 270 stream_append_s8s(&sb, c_str_to_s8(name), s8("_lock_")); 271 for (u32 i = 0; i < lock_count; i++) { 272 Stream lb = sb; 273 stream_append_u64(&lb, i); 274 stream_append_byte(&lb, 0); 275 ctx->semaphores[i] = CreateSemaphoreA(0, 1, 1, (c8 *)lb.data); 276 if (ctx->semaphores[i] == INVALID_FILE) 277 os_fatal(s8("os_create_shared_memory_area: failed to create semaphore\n")); 278 279 /* NOTE(rnp): hacky garbage because CreateSemaphore will just open an existing 280 * semaphore without any indication. Sometimes the other side of the shared memory 281 * will provide incorrect parameters or will otherwise fail and its faster to 282 * restart this program than to get that application to release the semaphores */ 283 /* TODO(rnp): figure out something more robust */ 284 ReleaseSemaphore(ctx->semaphores[i], 1, 0); 285 } 286 } 287 } 288 return result; 289 } 290 291 function b32 292 os_copy_file(char *name, char *new) 293 { 294 return CopyFileA(name, new, 0); 295 } 296 297 function void * 298 os_load_library(char *name, char *temp_name, Stream *e) 299 { 300 if (temp_name && os_copy_file(name, temp_name)) 301 name = temp_name; 302 303 void *result = LoadLibraryA(name); 304 if (!result && e) { 305 stream_append_s8s(e, s8("os_load_library(\""), c_str_to_s8(name), s8("\"): ")); 306 stream_append_i64(e, GetLastError()); 307 stream_append_byte(e, '\n'); 308 } 309 310 if (temp_name) 311 DeleteFileA(temp_name); 312 313 return result; 314 } 315 316 function void * 317 os_lookup_dynamic_symbol(void *h, char *name, Stream *e) 318 { 319 void *result = 0; 320 if (h) { 321 result = GetProcAddress(h, name); 322 if (!result && e) { 323 stream_append_s8s(e, s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name), 324 s8("\"): ")); 325 stream_append_i64(e, GetLastError()); 326 stream_append_byte(e, '\n'); 327 } 328 } 329 return result; 330 } 331 332 function void 333 os_unload_library(void *h) 334 { 335 FreeLibrary(h); 336 } 337 338 function OS_ADD_FILE_WATCH_FN(os_add_file_watch) 339 { 340 s8 directory = path; 341 directory.len = s8_scan_backwards(path, OS_PATH_SEPARATOR_CHAR); 342 assert(directory.len > 0); 343 344 u64 hash = s8_hash(directory); 345 FileWatchContext *fwctx = &os->file_watch_context; 346 FileWatchDirectory *dir = lookup_file_watch_directory(fwctx, hash); 347 if (!dir) { 348 assert(path.data[directory.len] == OS_PATH_SEPARATOR_CHAR); 349 350 dir = da_push(a, fwctx); 351 dir->hash = hash; 352 dir->name = push_s8(a, directory); 353 dir->handle = CreateFileA((c8 *)dir->name.data, GENERIC_READ, FILE_SHARE_READ, 0, 354 OPEN_EXISTING, 355 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, 0); 356 357 w32_context *ctx = (w32_context *)os->context; 358 w32_io_completion_event *event = push_struct(a, typeof(*event)); 359 event->tag = W32_IO_FILE_WATCH; 360 event->context = (iptr)dir; 361 CreateIoCompletionPort(dir->handle, ctx->io_completion_handle, (uptr)event, 0); 362 363 dir->buffer = sub_arena(a, 4096 + sizeof(w32_overlapped), 64); 364 w32_overlapped *overlapped = (w32_overlapped *)(dir->buffer.beg + 4096); 365 zero_struct(overlapped); 366 367 ReadDirectoryChangesW(dir->handle, dir->buffer.beg, 4096, 0, 368 FILE_NOTIFY_CHANGE_LAST_WRITE, 0, overlapped, 0); 369 } 370 371 FileWatch *fw = da_push(a, dir); 372 fw->user_data = user_data; 373 fw->callback = callback; 374 fw->hash = s8_hash(s8_cut_head(path, dir->name.len + 1)); 375 } 376 377 function iptr 378 os_create_thread(Arena arena, iptr user_context, s8 name, os_thread_entry_point_fn *fn) 379 { 380 iptr result = CreateThread(0, 0, (iptr)fn, user_context, 0, 0); 381 SetThreadDescription(result, s8_to_s16(&arena, name).data); 382 return result; 383 } 384 385 function OS_WAIT_ON_VALUE_FN(os_wait_on_value) 386 { 387 return WaitOnAddress(value, ¤t, sizeof(*value), timeout_ms); 388 } 389 390 function OS_WAKE_WAITERS_FN(os_wake_waiters) 391 { 392 if (sync) { 393 atomic_store_u32(sync, 0); 394 WakeByAddressAll(sync); 395 } 396 } 397 398 function OS_SHARED_MEMORY_LOCK_REGION_FN(os_shared_memory_region_lock) 399 { 400 w32_shared_memory_context *ctx = (typeof(ctx))sm->os_context; 401 b32 result = !WaitForSingleObject(ctx->semaphores[lock_index], timeout_ms); 402 if (result) atomic_store_u32(locks + lock_index, 1); 403 return result; 404 } 405 406 function OS_SHARED_MEMORY_UNLOCK_REGION_FN(os_shared_memory_region_unlock) 407 { 408 w32_shared_memory_context *ctx = (typeof(ctx))sm->os_context; 409 assert(atomic_load_u32(locks + lock_index)); 410 os_wake_waiters(locks + lock_index); 411 ReleaseSemaphore(ctx->semaphores[lock_index], 1, 0); 412 } 413 414 function void 415 os_init(OS *os, Arena *program_memory) 416 { 417 w32_context *ctx = push_struct(program_memory, typeof(*ctx)); 418 ctx->io_completion_handle = CreateIoCompletionPort(INVALID_FILE, 0, 0, 0); 419 ctx->timer_frequency = os_get_timer_frequency(); 420 421 os->error_handle = GetStdHandle(STD_ERROR_HANDLE); 422 os->context = (iptr)ctx; 423 }