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


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