ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

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, &current, 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, &current, 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 }