os_win32.c (13481B)
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 u32 reserved1; 62 u32 reserved2; 63 u64 Reserved3[2]; 64 u32 reserved4; 65 u32 reserved5; 66 } w32_synchronization_barrier; 67 68 typedef struct { 69 u16 architecture; 70 u16 _pad1; 71 u32 page_size; 72 iz minimum_application_address; 73 iz maximum_application_address; 74 u64 active_processor_mask; 75 u32 number_of_processors; 76 u32 processor_type; 77 u32 allocation_granularity; 78 u16 processor_level; 79 u16 processor_revision; 80 } w32_system_info; 81 82 typedef struct { 83 uptr internal, internal_high; 84 union { 85 struct {u32 off, off_high;}; 86 iptr pointer; 87 }; 88 iptr event_handle; 89 } w32_overlapped; 90 91 typedef enum { 92 W32_IO_FILE_WATCH, 93 W32_IO_PIPE, 94 } W32_IO_Event; 95 96 typedef struct { 97 u64 tag; 98 iptr context; 99 } w32_io_completion_event; 100 101 typedef struct { 102 iptr *semaphores; 103 u32 reserved_count; 104 } w32_shared_memory_context; 105 106 #define W32(r) __declspec(dllimport) r __stdcall 107 W32(b32) CloseHandle(iptr); 108 W32(b32) CopyFileA(c8 *, c8 *, b32); 109 W32(iptr) CreateFileA(c8 *, u32, u32, void *, u32, u32, void *); 110 W32(iptr) CreateFileMappingA(iptr, void *, u32, u32, u32, c8 *); 111 W32(iptr) CreateIoCompletionPort(iptr, iptr, uptr, u32); 112 W32(iptr) CreateSemaphoreA(iptr, i32, i32, c8 *); 113 W32(iptr) CreateThread(iptr, uz, iptr, iptr, u32, u32 *); 114 W32(b32) DeleteFileA(c8 *); 115 W32(b32) EnterSynchronizationBarrier(w32_synchronization_barrier *, u32); 116 W32(void) ExitProcess(i32); 117 W32(b32) FreeLibrary(void *); 118 W32(i32) GetFileAttributesA(c8 *); 119 W32(b32) GetFileInformationByHandle(iptr, void *); 120 W32(i32) GetLastError(void); 121 W32(void *) GetModuleHandleA(c8 *); 122 W32(void *) GetProcAddress(void *, c8 *); 123 W32(b32) GetQueuedCompletionStatus(iptr, u32 *, uptr *, w32_overlapped **, u32); 124 W32(iptr) GetStdHandle(i32); 125 W32(void) GetSystemInfo(w32_system_info *); 126 W32(b32) InitializeSynchronizationBarrier(w32_synchronization_barrier *, i32, i32); 127 W32(void *) LoadLibraryA(c8 *); 128 W32(void *) MapViewOfFile(iptr, u32, u32, u32, u64); 129 W32(b32) QueryPerformanceCounter(u64 *); 130 W32(b32) QueryPerformanceFrequency(u64 *); 131 W32(b32) ReadDirectoryChangesW(iptr, u8 *, u32, b32, u32, u32 *, void *, void *); 132 W32(b32) ReadFile(iptr, u8 *, i32, i32 *, void *); 133 W32(b32) ReleaseSemaphore(iptr, i32, i32 *); 134 W32(i32) SetThreadDescription(iptr, u16 *); 135 W32(u32) WaitForSingleObject(iptr, u32); 136 W32(b32) WaitOnAddress(void *, void *, uz, u32); 137 W32(i32) WakeByAddressAll(void *); 138 W32(iptr) wglGetProcAddress(c8 *); 139 W32(b32) WriteFile(iptr, u8 *, i32, i32 *, void *); 140 W32(void *) VirtualAlloc(u8 *, iz, u32, u32); 141 142 typedef struct { 143 Arena arena; 144 i32 arena_lock; 145 iptr error_handle; 146 iptr io_completion_handle; 147 u64 timer_frequency; 148 OS_SystemInfo system_info; 149 } OS_W32Context; 150 global OS_W32Context os_w32_context; 151 152 #ifdef _DEBUG 153 function void * 154 os_get_module(char *name, Stream *e) 155 { 156 void *result = GetModuleHandleA(name); 157 if (!result && e) { 158 stream_append_s8s(e, s8("os_get_module(\""), c_str_to_s8(name), s8("\"): ")); 159 stream_append_i64(e, GetLastError()); 160 stream_append_byte(e, '\n'); 161 } 162 return result; 163 } 164 #endif 165 166 function OS_WRITE_FILE_FN(os_write_file) 167 { 168 i32 wlen = 0; 169 if (raw.len > 0 && raw.len <= U32_MAX) WriteFile(file, raw.data, (i32)raw.len, &wlen, 0); 170 return raw.len == wlen; 171 } 172 173 function no_return void 174 os_exit(i32 code) 175 { 176 ExitProcess(1); 177 unreachable(); 178 } 179 180 function no_return void 181 os_fatal(s8 msg) 182 { 183 os_write_file(GetStdHandle(STD_ERROR_HANDLE), msg); 184 os_exit(1); 185 unreachable(); 186 } 187 188 function iptr 189 os_error_handle(void) 190 { 191 return os_w32_context.error_handle; 192 } 193 194 function s8 195 os_path_separator(void) 196 { 197 return s8("\\"); 198 } 199 200 function u64 201 os_get_timer_frequency(void) 202 { 203 u64 result = os_w32_context.timer_frequency; 204 return result; 205 } 206 207 function u64 208 os_get_timer_counter(void) 209 { 210 u64 result; 211 QueryPerformanceCounter(&result); 212 return result; 213 } 214 215 function void 216 os_common_init(void) 217 { 218 w32_system_info info = {0}; 219 GetSystemInfo(&info); 220 221 os_w32_context.system_info.page_size = info.page_size; 222 os_w32_context.system_info.logical_processor_count = info.number_of_processors; 223 224 QueryPerformanceFrequency(&os_w32_context.timer_frequency); 225 } 226 227 function iz 228 os_round_up_to_page_size(iz value) 229 { 230 iz result = round_up_to(value, os_w32_context.system_info.page_size); 231 return result; 232 } 233 234 function OS_ALLOC_ARENA_FN(os_alloc_arena) 235 { 236 Arena result = {0}; 237 capacity = os_round_up_to_page_size(capacity); 238 result.beg = VirtualAlloc(0, capacity, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); 239 if (!result.beg) 240 os_fatal(s8("os_alloc_arena: couldn't allocate memory\n")); 241 result.end = result.beg + capacity; 242 asan_poison_region(result.beg, result.end - result.beg); 243 return result; 244 } 245 246 function OS_READ_WHOLE_FILE_FN(os_read_whole_file) 247 { 248 s8 result = s8(""); 249 250 w32_file_info fileinfo; 251 iptr h = CreateFileA(file, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); 252 if (h >= 0 && GetFileInformationByHandle(h, &fileinfo)) { 253 iz filesize = (iz)fileinfo.nFileSizeHigh << 32; 254 filesize |= (iz)fileinfo.nFileSizeLow; 255 if (filesize <= U32_MAX) { 256 result = s8_alloc(arena, filesize); 257 i32 rlen; 258 if (!ReadFile(h, result.data, (i32)result.len, &rlen, 0) || rlen != result.len) 259 result = s8(""); 260 } 261 } 262 if (h >= 0) CloseHandle(h); 263 264 return result; 265 } 266 267 function OS_WRITE_NEW_FILE_FN(os_write_new_file) 268 { 269 b32 result = 0; 270 iptr h = CreateFileA(fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); 271 if (h >= 0) { 272 while (raw.len > 0) { 273 s8 chunk = raw; 274 chunk.len = MIN(chunk.len, (iz)GB(2)); 275 result = os_write_file(h, chunk); 276 if (!result) break; 277 raw = s8_cut_head(raw, chunk.len); 278 } 279 CloseHandle(h); 280 } 281 return result; 282 } 283 284 function b32 285 os_file_exists(char *path) 286 { 287 b32 result = GetFileAttributesA(path) != -1; 288 return result; 289 } 290 291 function SharedMemoryRegion 292 os_create_shared_memory_area(Arena *arena, char *name, u32 lock_count, iz requested_capacity) 293 { 294 iz capacity = os_round_up_to_page_size(requested_capacity); 295 assert(capacity <= (iz)U32_MAX); 296 SharedMemoryRegion result = {0}; 297 iptr h = CreateFileMappingA(-1, 0, PAGE_READWRITE, 0, (u32)capacity, name); 298 if (h != INVALID_FILE) { 299 void *new = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, (u32)capacity); 300 if (new) { 301 w32_shared_memory_context *ctx = push_struct(arena, typeof(*ctx)); 302 ctx->semaphores = push_array(arena, typeof(*ctx->semaphores), lock_count); 303 ctx->reserved_count = lock_count; 304 result.os_context = (iptr)ctx; 305 result.region = new; 306 307 Stream sb = arena_stream(*arena); 308 stream_append_s8s(&sb, c_str_to_s8(name), s8("_lock_")); 309 for (u32 i = 0; i < lock_count; i++) { 310 Stream lb = sb; 311 stream_append_u64(&lb, i); 312 stream_append_byte(&lb, 0); 313 ctx->semaphores[i] = CreateSemaphoreA(0, 1, 1, (c8 *)lb.data); 314 if (ctx->semaphores[i] == INVALID_FILE) 315 os_fatal(s8("os_create_shared_memory_area: failed to create semaphore\n")); 316 317 /* NOTE(rnp): hacky garbage because CreateSemaphore will just open an existing 318 * semaphore without any indication. Sometimes the other side of the shared memory 319 * will provide incorrect parameters or will otherwise fail and its faster to 320 * restart this program than to get that application to release the semaphores */ 321 /* TODO(rnp): figure out something more robust */ 322 ReleaseSemaphore(ctx->semaphores[i], 1, 0); 323 } 324 } 325 } 326 return result; 327 } 328 329 function b32 330 os_copy_file(char *name, char *new) 331 { 332 return CopyFileA(name, new, 0); 333 } 334 335 function void * 336 os_load_library(char *name, char *temp_name, Stream *e) 337 { 338 if (temp_name && os_copy_file(name, temp_name)) 339 name = temp_name; 340 341 void *result = LoadLibraryA(name); 342 if (!result && e) { 343 stream_append_s8s(e, s8("os_load_library(\""), c_str_to_s8(name), s8("\"): ")); 344 stream_append_i64(e, GetLastError()); 345 stream_append_byte(e, '\n'); 346 } 347 348 if (temp_name) 349 DeleteFileA(temp_name); 350 351 return result; 352 } 353 354 function void * 355 os_lookup_dynamic_symbol(void *h, char *name, Stream *e) 356 { 357 void *result = 0; 358 if (h) { 359 result = GetProcAddress(h, name); 360 if (!result && e) { 361 stream_append_s8s(e, s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name), 362 s8("\"): ")); 363 stream_append_i64(e, GetLastError()); 364 stream_append_byte(e, '\n'); 365 } 366 } 367 return result; 368 } 369 370 function void 371 os_unload_library(void *h) 372 { 373 FreeLibrary(h); 374 } 375 376 function OS_ADD_FILE_WATCH_FN(os_add_file_watch) 377 { 378 s8 directory = path; 379 directory.len = s8_scan_backwards(path, '\\'); 380 assert(directory.len > 0); 381 382 u64 hash = u64_hash_from_s8(directory); 383 FileWatchDirectory *dir = lookup_file_watch_directory(fwctx, hash); 384 if (!dir) { 385 assert(path.data[directory.len] == '\\'); 386 387 dir = da_push(a, fwctx); 388 dir->hash = hash; 389 dir->name = push_s8(a, directory); 390 dir->handle = CreateFileA((c8 *)dir->name.data, GENERIC_READ, FILE_SHARE_READ, 0, 391 OPEN_EXISTING, 392 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, 0); 393 394 w32_io_completion_event *event = push_struct(a, typeof(*event)); 395 event->tag = W32_IO_FILE_WATCH; 396 event->context = (iptr)dir; 397 CreateIoCompletionPort(dir->handle, os_w32_context.io_completion_handle, (uptr)event, 0); 398 399 dir->buffer = sub_arena(a, 4096 + sizeof(w32_overlapped), 64); 400 w32_overlapped *overlapped = (w32_overlapped *)(dir->buffer.beg + 4096); 401 zero_struct(overlapped); 402 403 ReadDirectoryChangesW(dir->handle, dir->buffer.beg, 4096, 0, 404 FILE_NOTIFY_CHANGE_LAST_WRITE, 0, overlapped, 0); 405 } 406 407 FileWatch *fw = da_push(a, dir); 408 fw->user_data = user_data; 409 fw->callback = callback; 410 fw->hash = u64_hash_from_s8(s8_cut_head(path, dir->name.len + 1)); 411 } 412 413 function OS_WAIT_ON_VALUE_FN(os_wait_on_value) 414 { 415 return WaitOnAddress(value, ¤t, sizeof(*value), timeout_ms); 416 } 417 418 function OS_WAKE_WAITERS_FN(os_wake_waiters) 419 { 420 if (sync) { 421 atomic_store_u32(sync, 0); 422 WakeByAddressAll(sync); 423 } 424 } 425 426 function b32 427 os_take_lock(i32 *lock, i32 timeout_ms) 428 { 429 b32 result = 0; 430 for (;;) { 431 i32 current = 0; 432 if (atomic_cas_u32(lock, ¤t, 1)) 433 result = 1; 434 if (result || !timeout_ms || (!os_wait_on_value(lock, current, (u32)timeout_ms) && timeout_ms != -1)) 435 break; 436 } 437 return result; 438 } 439 440 function void 441 os_release_lock(i32 *lock) 442 { 443 assert(atomic_load_u32(lock)); 444 atomic_store_u32(lock, 0); 445 os_wake_waiters(lock); 446 } 447 448 function OS_SHARED_MEMORY_LOCK_REGION_FN(os_shared_memory_region_lock) 449 { 450 w32_shared_memory_context *ctx = (typeof(ctx))sm->os_context; 451 b32 result = !WaitForSingleObject(ctx->semaphores[lock_index], timeout_ms); 452 if (result) atomic_store_u32(locks + lock_index, 1); 453 return result; 454 } 455 456 function OS_SHARED_MEMORY_UNLOCK_REGION_FN(os_shared_memory_region_unlock) 457 { 458 w32_shared_memory_context *ctx = (typeof(ctx))sm->os_context; 459 os_release_lock(locks + lock_index); 460 ReleaseSemaphore(ctx->semaphores[lock_index], 1, 0); 461 } 462 463 function OS_SystemInfo * 464 os_get_system_info(void) 465 { 466 return &os_w32_context.system_info; 467 } 468 469 function Barrier 470 os_barrier_alloc(u32 count) 471 { 472 Barrier result = {0}; 473 DeferLoop(os_take_lock(&os_w32_context.arena_lock, -1), 474 os_release_lock(&os_w32_context.arena_lock)) 475 { 476 w32_synchronization_barrier *barrier = push_struct(&os_w32_context.arena, w32_synchronization_barrier); 477 InitializeSynchronizationBarrier(barrier, (i32)count, -1); 478 result.value[0] = (u64)barrier; 479 } 480 return result; 481 } 482 483 function void 484 os_barrier_wait(Barrier barrier) 485 { 486 w32_synchronization_barrier *b = (w32_synchronization_barrier *)barrier.value[0]; 487 if (b) EnterSynchronizationBarrier(b, 0); 488 } 489 490 function iptr 491 os_create_thread(iptr user_context, os_thread_entry_point_fn *fn) 492 { 493 iptr result = CreateThread(0, 0, (iptr)fn, user_context, 0, 0); 494 return result; 495 } 496 497 function void 498 os_set_thread_name(iptr thread, s8 name) 499 { 500 DeferLoop(os_take_lock(&os_w32_context.arena_lock, -1), 501 os_release_lock(&os_w32_context.arena_lock)) 502 { 503 Arena arena = os_w32_context.arena; 504 SetThreadDescription(thread, s8_to_s16(&arena, name).data); 505 } 506 }