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 (7285B)


      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 <unistd.h>
     23 
     24 typedef struct {
     25 	i32           inotify_handle;
     26 } OS_LinuxContext;
     27 global OS_LinuxContext os_linux_context;
     28 
     29 /* NOTE(rnp): hidden behind feature flags -> screw compiler/standards idiots */
     30 #ifndef CLOCK_MONOTONIC
     31   #define CLOCK_MONOTONIC 1
     32 #endif
     33 i32 ftruncate(i32, i64);
     34 i64 syscall(i64, ...);
     35 i32 clock_gettime(i32, struct timespec *);
     36 
     37 #ifdef _DEBUG
     38 function void *
     39 os_get_module(char *name, Stream *e)
     40 {
     41 	void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD);
     42 	if (!result && e) {
     43 		stream_append_s8s(e, s8("os_get_module(\""), c_str_to_s8(name), s8("\"): "),
     44 		                  c_str_to_s8(dlerror()), s8("\n"));
     45 	}
     46 	return result;
     47 }
     48 #endif
     49 
     50 function OS_WRITE_FILE_FN(os_write_file)
     51 {
     52 	while (raw.len > 0) {
     53 		iz r = write((i32)file, raw.data, (uz)raw.len);
     54 		if (r < 0) return 0;
     55 		raw = s8_cut_head(raw, r);
     56 	}
     57 	return 1;
     58 }
     59 
     60 function void __attribute__((noreturn))
     61 os_exit(i32 code)
     62 {
     63 	_exit(code);
     64 	unreachable();
     65 }
     66 
     67 function void __attribute__((noreturn))
     68 os_fatal(s8 msg)
     69 {
     70 	os_write_file(STDERR_FILENO, msg);
     71 	os_exit(1);
     72 	unreachable();
     73 }
     74 
     75 function iptr
     76 os_error_handle(void)
     77 {
     78 	return STDERR_FILENO;
     79 }
     80 
     81 function s8
     82 os_path_separator(void)
     83 {
     84 	return s8("/");
     85 }
     86 
     87 function u64
     88 os_get_timer_frequency(void)
     89 {
     90 	return 1000000000ULL;
     91 }
     92 
     93 function u64
     94 os_get_timer_counter(void)
     95 {
     96 	struct timespec time = {0};
     97 	clock_gettime(CLOCK_MONOTONIC, &time);
     98 	u64 result = (u64)time.tv_sec * 1000000000ULL + (u64)time.tv_nsec;
     99 	return result;
    100 }
    101 
    102 function iz
    103 os_round_up_to_page_size(iz value)
    104 {
    105 	iz result = round_up_to(value, sysconf(_SC_PAGESIZE));
    106 	return result;
    107 }
    108 
    109 function OS_ALLOC_ARENA_FN(os_alloc_arena)
    110 {
    111 	Arena result = {0};
    112 	capacity   = os_round_up_to_page_size(capacity);
    113 	result.beg = mmap(0, (uz)capacity, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
    114 	if (result.beg == MAP_FAILED)
    115 		os_fatal(s8("os_alloc_arena: couldn't allocate memory\n"));
    116 	result.end = result.beg + capacity;
    117 	asan_poison_region(result.beg, result.end - result.beg);
    118 	return result;
    119 }
    120 
    121 function OS_READ_WHOLE_FILE_FN(os_read_whole_file)
    122 {
    123 	s8 result = s8("");
    124 
    125 	struct stat sb;
    126 	i32 fd = open(file, O_RDONLY);
    127 	if (fd >= 0 && fstat(fd, &sb) >= 0) {
    128 		result = s8_alloc(arena, sb.st_size);
    129 		iz rlen = read(fd, result.data, (uz)result.len);
    130 		if (rlen != result.len)
    131 			result = s8("");
    132 	}
    133 	if (fd >= 0) close(fd);
    134 
    135 	return result;
    136 }
    137 
    138 function OS_WRITE_NEW_FILE_FN(os_write_new_file)
    139 {
    140 	b32 result = 0;
    141 	i32 fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0600);
    142 	if (fd != INVALID_FILE) {
    143 		result = os_write_file(fd, raw);
    144 		close(fd);
    145 	}
    146 	return result;
    147 }
    148 
    149 function b32
    150 os_file_exists(char *path)
    151 {
    152 	struct stat st;
    153 	b32 result = stat(path, &st) == 0;
    154 	return result;
    155 }
    156 
    157 function SharedMemoryRegion
    158 os_create_shared_memory_area(Arena *arena, char *name, u32 lock_count, iz requested_capacity)
    159 {
    160 	iz capacity = os_round_up_to_page_size(requested_capacity);
    161 	SharedMemoryRegion result = {0};
    162 	i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
    163 	if (fd > 0 && ftruncate(fd, capacity) != -1) {
    164 		void *new = mmap(0, (uz)capacity, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    165 		if (new != MAP_FAILED) result.region = new;
    166 	}
    167 	if (fd > 0) close(fd);
    168 	return result;
    169 }
    170 
    171 /* NOTE: complete garbage because there is no standarized copyfile() in POSix */
    172 function b32
    173 os_copy_file(char *name, char *new)
    174 {
    175 	b32 result = 0;
    176 	struct stat sb;
    177 	if (stat(name, &sb) == 0) {
    178 		i32 fd_old = open(name, O_RDONLY);
    179 		i32 fd_new = open(new,  O_WRONLY|O_CREAT, sb.st_mode);
    180 		if (fd_old >= 0 && fd_new >= 0) {
    181 			u8 buf[4096];
    182 			iz copied = 0;
    183 			while (copied != sb.st_size) {
    184 				iz r = read(fd_old, buf, countof(buf));
    185 				if (r < 0) break;
    186 				iz w = write(fd_new, buf, (uz)r);
    187 				if (w < 0) break;
    188 				copied += w;
    189 			}
    190 			result = copied == sb.st_size;
    191 		}
    192 		if (fd_old != -1) close(fd_old);
    193 		if (fd_new != -1) close(fd_new);
    194 	}
    195 	return result;
    196 }
    197 
    198 function void *
    199 os_load_library(char *name, char *temp_name, Stream *e)
    200 {
    201 	if (temp_name) {
    202 		if (os_copy_file(name, temp_name))
    203 			name = temp_name;
    204 	}
    205 
    206 	void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL);
    207 	if (!result && e) {
    208 		stream_append_s8s(e, s8("os_load_library(\""), c_str_to_s8(name), s8("\"): "),
    209 		                  c_str_to_s8(dlerror()), s8("\n"));
    210 	}
    211 
    212 	if (temp_name)
    213 		unlink(temp_name);
    214 
    215 	return result;
    216 }
    217 
    218 function void *
    219 os_lookup_dynamic_symbol(void *h, char *name, Stream *e)
    220 {
    221 	void *result = 0;
    222 	if (h) {
    223 		result = dlsym(h, name);
    224 		if (!result && e) {
    225 			stream_append_s8s(e, s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name),
    226 			                  s8("\"): "), c_str_to_s8(dlerror()), s8("\n"));
    227 		}
    228 	}
    229 	return result;
    230 }
    231 
    232 function void
    233 os_unload_library(void *h)
    234 {
    235 	/* NOTE: glibc is buggy gnuware so we need to check this */
    236 	if (h)
    237 		dlclose(h);
    238 }
    239 
    240 function OS_ADD_FILE_WATCH_FN(os_add_file_watch)
    241 {
    242 	s8 directory  = path;
    243 	directory.len = s8_scan_backwards(path, '/');
    244 	assert(directory.len > 0);
    245 
    246 	u64 hash = u64_hash_from_s8(directory);
    247 	FileWatchDirectory *dir = lookup_file_watch_directory(fwctx, hash);
    248 	if (!dir) {
    249 		assert(path.data[directory.len] == '/');
    250 		dir = da_push(a, fwctx);
    251 		dir->hash   = hash;
    252 		dir->name   = push_s8(a, directory);
    253 		u32 mask    = IN_MOVED_TO|IN_CLOSE_WRITE;
    254 		dir->handle = inotify_add_watch(os_linux_context.inotify_handle, (c8 *)dir->name.data, mask);
    255 	}
    256 
    257 	FileWatch *fw = da_push(a, dir);
    258 	fw->user_data = user_data;
    259 	fw->callback  = callback;
    260 	fw->hash      = u64_hash_from_s8(s8_cut_head(path, dir->name.len + 1));
    261 }
    262 
    263 i32 pthread_setname_np(pthread_t, char *);
    264 function iptr
    265 os_create_thread(Arena arena, iptr user_context, s8 name, os_thread_entry_point_fn *fn)
    266 {
    267 	pthread_t result;
    268 	pthread_create(&result, 0, (void *)fn, (void *)user_context);
    269 	pthread_setname_np(result, (char *)name.data);
    270 	return (iptr)result;
    271 }
    272 
    273 function OS_WAIT_ON_VALUE_FN(os_wait_on_value)
    274 {
    275 	struct timespec *timeout = 0, timeout_value;
    276 	if (timeout_ms != (u32)-1) {
    277 		timeout_value.tv_sec  = timeout_ms / 1000;
    278 		timeout_value.tv_nsec = (timeout_ms % 1000) * 1000000;
    279 		timeout = &timeout_value;
    280 	}
    281 	return syscall(SYS_futex, value, FUTEX_WAIT, current, timeout, 0, 0) == 0;
    282 }
    283 
    284 function OS_WAKE_WAITERS_FN(os_wake_waiters)
    285 {
    286 	if (sync) {
    287 		atomic_store_u32(sync, 0);
    288 		syscall(SYS_futex, sync, FUTEX_WAKE, I32_MAX, 0, 0, 0);
    289 	}
    290 }
    291 
    292 function OS_SHARED_MEMORY_LOCK_REGION_FN(os_shared_memory_region_lock)
    293 {
    294 	b32 result = 0;
    295 	for (;;) {
    296 		i32 current = 0;
    297 		if (atomic_cas_u32(locks + lock_index, &current, 1))
    298 			result = 1;
    299 		if (result || !timeout_ms || !os_wait_on_value(locks + lock_index, current, timeout_ms))
    300 			break;
    301 	}
    302 	return result;
    303 }
    304 
    305 function OS_SHARED_MEMORY_UNLOCK_REGION_FN(os_shared_memory_region_unlock)
    306 {
    307 	i32 *lock = locks + lock_index;
    308 	assert(atomic_load_u32(lock));
    309 	atomic_store_u32(lock, 0);
    310 	os_wake_waiters(lock);
    311 }