ogl_beamforming

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

os_linux.c (8305B)


      1 /* See LICENSE for license details. */
      2 
      3 /* NOTE(rnp): provides the platform layer for the beamformer. This code must
      4  * be provided by any platform the beamformer is ported to. */
      5 
      6 #define OS_SHARED_MEMORY_NAME "/ogl_beamformer_shared_memory"
      7 
      8 #define OS_PATH_SEPARATOR_CHAR '/'
      9 #define OS_PATH_SEPARATOR      "/"
     10 
     11 #include "util.h"
     12 
     13 #include <dlfcn.h>
     14 #include <fcntl.h>
     15 #include <linux/futex.h>
     16 #include <poll.h>
     17 #include <pthread.h>
     18 #include <sys/inotify.h>
     19 #include <sys/mman.h>
     20 #include <sys/stat.h>
     21 #include <sys/syscall.h>
     22 #include <sys/sysinfo.h>
     23 #include <unistd.h>
     24 
     25 typedef struct {
     26 	Arena         arena;
     27 	i32           arena_lock;
     28 	i32           inotify_handle;
     29 	OS_SystemInfo system_info;
     30 } OS_LinuxContext;
     31 global OS_LinuxContext os_linux_context;
     32 
     33 #ifdef _DEBUG
     34 function void *
     35 os_get_module(char *name, Stream *e)
     36 {
     37 	void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD);
     38 	if (!result && e) {
     39 		stream_append_s8s(e, s8("os_get_module(\""), c_str_to_s8(name), s8("\"): "),
     40 		                  c_str_to_s8(dlerror()), s8("\n"));
     41 	}
     42 	return result;
     43 }
     44 #endif
     45 
     46 function OS_WRITE_FILE_FN(os_write_file)
     47 {
     48 	while (raw.len > 0) {
     49 		iz r = write((i32)file, raw.data, (uz)raw.len);
     50 		if (r < 0) return 0;
     51 		raw = s8_cut_head(raw, r);
     52 	}
     53 	return 1;
     54 }
     55 
     56 function void __attribute__((noreturn))
     57 os_exit(i32 code)
     58 {
     59 	_exit(code);
     60 	unreachable();
     61 }
     62 
     63 function void __attribute__((noreturn))
     64 os_fatal(s8 msg)
     65 {
     66 	os_write_file(STDERR_FILENO, msg);
     67 	os_exit(1);
     68 	unreachable();
     69 }
     70 
     71 function iptr
     72 os_error_handle(void)
     73 {
     74 	return STDERR_FILENO;
     75 }
     76 
     77 function s8
     78 os_path_separator(void)
     79 {
     80 	return s8("/");
     81 }
     82 
     83 function u64
     84 os_get_timer_frequency(void)
     85 {
     86 	return 1000000000ULL;
     87 }
     88 
     89 function u64
     90 os_get_timer_counter(void)
     91 {
     92 	struct timespec time = {0};
     93 	clock_gettime(CLOCK_MONOTONIC, &time);
     94 	u64 result = (u64)time.tv_sec * 1000000000ULL + (u64)time.tv_nsec;
     95 	return result;
     96 }
     97 
     98 function void
     99 os_common_init(void)
    100 {
    101 	os_linux_context.system_info.logical_processor_count = (u32)get_nprocs();
    102 	os_linux_context.system_info.page_size               = (u32)getpagesize();
    103 }
    104 
    105 function iz
    106 os_round_up_to_page_size(iz value)
    107 {
    108 	iz result = round_up_to(value, os_linux_context.system_info.page_size);
    109 	return result;
    110 }
    111 
    112 function OS_ALLOC_ARENA_FN(os_alloc_arena)
    113 {
    114 	Arena result = {0};
    115 	capacity   = os_round_up_to_page_size(capacity);
    116 	result.beg = mmap(0, (uz)capacity, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
    117 	if (result.beg == MAP_FAILED)
    118 		os_fatal(s8("os_alloc_arena: couldn't allocate memory\n"));
    119 	result.end = result.beg + capacity;
    120 	asan_poison_region(result.beg, result.end - result.beg);
    121 	return result;
    122 }
    123 
    124 function OS_READ_WHOLE_FILE_FN(os_read_whole_file)
    125 {
    126 	s8 result = s8("");
    127 
    128 	struct stat sb;
    129 	i32 fd = open(file, O_RDONLY);
    130 	if (fd >= 0 && fstat(fd, &sb) >= 0) {
    131 		result = s8_alloc(arena, sb.st_size);
    132 		iz rlen = read(fd, result.data, (uz)result.len);
    133 		if (rlen != result.len)
    134 			result = s8("");
    135 	}
    136 	if (fd >= 0) close(fd);
    137 
    138 	return result;
    139 }
    140 
    141 function OS_WRITE_NEW_FILE_FN(os_write_new_file)
    142 {
    143 	b32 result = 0;
    144 	i32 fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0600);
    145 	if (fd != INVALID_FILE) {
    146 		result = os_write_file(fd, raw);
    147 		close(fd);
    148 	}
    149 	return result;
    150 }
    151 
    152 function b32
    153 os_file_exists(char *path)
    154 {
    155 	struct stat st;
    156 	b32 result = stat(path, &st) == 0;
    157 	return result;
    158 }
    159 
    160 function SharedMemoryRegion
    161 os_create_shared_memory_area(Arena *arena, char *name, u32 lock_count, iz requested_capacity)
    162 {
    163 	iz capacity = os_round_up_to_page_size(requested_capacity);
    164 	SharedMemoryRegion result = {0};
    165 	i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
    166 	if (fd > 0 && ftruncate(fd, capacity) != -1) {
    167 		void *new = mmap(0, (uz)capacity, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    168 		if (new != MAP_FAILED) result.region = new;
    169 	}
    170 	if (fd > 0) close(fd);
    171 	return result;
    172 }
    173 
    174 /* NOTE: complete garbage because there is no standarized copyfile() in POSix */
    175 function b32
    176 os_copy_file(char *name, char *new)
    177 {
    178 	b32 result = 0;
    179 	struct stat sb;
    180 	if (stat(name, &sb) == 0) {
    181 		i32 fd_old = open(name, O_RDONLY);
    182 		i32 fd_new = open(new,  O_WRONLY|O_CREAT, sb.st_mode);
    183 		if (fd_old >= 0 && fd_new >= 0) {
    184 			u8 buf[4096];
    185 			iz copied = 0;
    186 			while (copied != sb.st_size) {
    187 				iz r = read(fd_old, buf, countof(buf));
    188 				if (r < 0) break;
    189 				iz w = write(fd_new, buf, (uz)r);
    190 				if (w < 0) break;
    191 				copied += w;
    192 			}
    193 			result = copied == sb.st_size;
    194 		}
    195 		if (fd_old != -1) close(fd_old);
    196 		if (fd_new != -1) close(fd_new);
    197 	}
    198 	return result;
    199 }
    200 
    201 function void *
    202 os_load_library(char *name, char *temp_name, Stream *e)
    203 {
    204 	if (temp_name) {
    205 		if (os_copy_file(name, temp_name))
    206 			name = temp_name;
    207 	}
    208 
    209 	void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL);
    210 	if (!result && e) {
    211 		stream_append_s8s(e, s8("os_load_library(\""), c_str_to_s8(name), s8("\"): "),
    212 		                  c_str_to_s8(dlerror()), s8("\n"));
    213 	}
    214 
    215 	if (temp_name)
    216 		unlink(temp_name);
    217 
    218 	return result;
    219 }
    220 
    221 function void *
    222 os_lookup_dynamic_symbol(void *h, char *name, Stream *e)
    223 {
    224 	void *result = 0;
    225 	if (h) {
    226 		result = dlsym(h, name);
    227 		if (!result && e) {
    228 			stream_append_s8s(e, s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name),
    229 			                  s8("\"): "), c_str_to_s8(dlerror()), s8("\n"));
    230 		}
    231 	}
    232 	return result;
    233 }
    234 
    235 function void
    236 os_unload_library(void *h)
    237 {
    238 	/* NOTE: glibc is buggy gnuware so we need to check this */
    239 	if (h)
    240 		dlclose(h);
    241 }
    242 
    243 function OS_ADD_FILE_WATCH_FN(os_add_file_watch)
    244 {
    245 	s8 directory  = path;
    246 	directory.len = s8_scan_backwards(path, '/');
    247 	assert(directory.len > 0);
    248 
    249 	u64 hash = u64_hash_from_s8(directory);
    250 	FileWatchDirectory *dir = lookup_file_watch_directory(fwctx, hash);
    251 	if (!dir) {
    252 		assert(path.data[directory.len] == '/');
    253 		dir = da_push(a, fwctx);
    254 		dir->hash   = hash;
    255 		dir->name   = push_s8(a, directory);
    256 		u32 mask    = IN_MOVED_TO|IN_CLOSE_WRITE;
    257 		dir->handle = inotify_add_watch(os_linux_context.inotify_handle, (c8 *)dir->name.data, mask);
    258 	}
    259 
    260 	FileWatch *fw = da_push(a, dir);
    261 	fw->user_data = user_data;
    262 	fw->callback  = callback;
    263 	fw->hash      = u64_hash_from_s8(s8_cut_head(path, dir->name.len + 1));
    264 }
    265 
    266 function OS_WAIT_ON_VALUE_FN(os_wait_on_value)
    267 {
    268 	struct timespec *timeout = 0, timeout_value;
    269 	if (timeout_ms != (u32)-1) {
    270 		timeout_value.tv_sec  = timeout_ms / 1000;
    271 		timeout_value.tv_nsec = (timeout_ms % 1000) * 1000000;
    272 		timeout = &timeout_value;
    273 	}
    274 	return syscall(SYS_futex, value, FUTEX_WAIT, current, timeout, 0, 0) == 0;
    275 }
    276 
    277 function OS_WAKE_WAITERS_FN(os_wake_waiters)
    278 {
    279 	if (sync) {
    280 		atomic_store_u32(sync, 0);
    281 		syscall(SYS_futex, sync, FUTEX_WAKE, I32_MAX, 0, 0, 0);
    282 	}
    283 }
    284 
    285 function b32
    286 os_take_lock(i32 *lock, i32 timeout_ms)
    287 {
    288 	b32 result = 0;
    289 	for (;;) {
    290 		i32 current = 0;
    291 		if (atomic_cas_u32(lock, &current, 1))
    292 			result = 1;
    293 		if (result || !timeout_ms || (!os_wait_on_value(lock, current, (u32)timeout_ms) && timeout_ms != -1))
    294 			break;
    295 	}
    296 	return result;
    297 }
    298 
    299 function void
    300 os_release_lock(i32 *lock)
    301 {
    302 	assert(atomic_load_u32(lock));
    303 	atomic_store_u32(lock, 0);
    304 	os_wake_waiters(lock);
    305 }
    306 
    307 function OS_SHARED_MEMORY_LOCK_REGION_FN(os_shared_memory_region_lock)
    308 {
    309 	b32 result = os_take_lock(locks + lock_index, (i32)timeout_ms);
    310 	return result;
    311 }
    312 
    313 function OS_SHARED_MEMORY_UNLOCK_REGION_FN(os_shared_memory_region_unlock)
    314 {
    315 	os_release_lock(locks + lock_index);
    316 }
    317 
    318 function OS_SystemInfo *
    319 os_get_system_info(void)
    320 {
    321 	return &os_linux_context.system_info;
    322 }
    323 
    324 function Barrier
    325 os_barrier_alloc(u32 count)
    326 {
    327 	Barrier result = {0};
    328 	DeferLoop(os_take_lock(&os_linux_context.arena_lock, -1),
    329 	          os_release_lock(&os_linux_context.arena_lock))
    330 	{
    331 		pthread_barrier_t *barrier = push_struct(&os_linux_context.arena, pthread_barrier_t);
    332 		pthread_barrier_init(barrier, 0, count);
    333 		result.value[0] = (u64)barrier;
    334 	}
    335 	return result;
    336 }
    337 
    338 function void
    339 os_barrier_wait(Barrier barrier)
    340 {
    341 	pthread_barrier_t *b = (pthread_barrier_t *)barrier.value[0];
    342 	if (b) pthread_barrier_wait(b);
    343 }
    344 
    345 function iptr
    346 os_create_thread(iptr user_context, os_thread_entry_point_fn *fn)
    347 {
    348 	pthread_t result;
    349 	pthread_create(&result, 0, (void *)fn, (void *)user_context);
    350 	return (iptr)result;
    351 }
    352 
    353 function void
    354 os_set_thread_name(iptr thread, s8 name)
    355 {
    356 	char buffer[16];
    357 	u64  length = (u64)CLAMP(name.len, 0, countof(buffer) - 1);
    358 	mem_copy(buffer, name.data, length);
    359 	buffer[length] = 0;
    360 	pthread_setname_np((pthread_t)thread, buffer);
    361 }