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


      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 > 0) {
     48 		iz r = write((i32)file, raw.data, (uz)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 = (u64)time.tv_sec * 1000000000ULL + (u64)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, (uz)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 	asan_poison_region(result.beg, result.end - result.beg);
    101 	return result;
    102 }
    103 
    104 function OS_READ_WHOLE_FILE_FN(os_read_whole_file)
    105 {
    106 	s8 result = s8("");
    107 
    108 	struct stat sb;
    109 	i32 fd = open(file, O_RDONLY);
    110 	if (fd >= 0 && fstat(fd, &sb) >= 0) {
    111 		result = s8_alloc(arena, sb.st_size);
    112 		iz rlen = read(fd, result.data, (uz)result.len);
    113 		if (rlen != result.len)
    114 			result = s8("");
    115 	}
    116 	if (fd >= 0) close(fd);
    117 
    118 	return result;
    119 }
    120 
    121 function OS_WRITE_NEW_FILE_FN(os_write_new_file)
    122 {
    123 	b32 result = 0;
    124 	i32 fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0600);
    125 	if (fd != INVALID_FILE) {
    126 		result = os_write_file(fd, raw);
    127 		close(fd);
    128 	}
    129 	return result;
    130 }
    131 
    132 function b32
    133 os_file_exists(char *path)
    134 {
    135 	struct stat st;
    136 	b32 result = stat(path, &st) == 0;
    137 	return result;
    138 }
    139 
    140 function SharedMemoryRegion
    141 os_create_shared_memory_area(Arena *arena, char *name, u32 lock_count, iz requested_capacity)
    142 {
    143 	iz capacity = os_round_up_to_page_size(requested_capacity);
    144 	SharedMemoryRegion result = {0};
    145 	i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
    146 	if (fd > 0 && ftruncate(fd, capacity) != -1) {
    147 		void *new = mmap(0, (uz)capacity, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    148 		if (new != MAP_FAILED) result.region = new;
    149 	}
    150 	if (fd > 0) close(fd);
    151 	return result;
    152 }
    153 
    154 /* NOTE: complete garbage because there is no standarized copyfile() in POSix */
    155 function b32
    156 os_copy_file(char *name, char *new)
    157 {
    158 	b32 result = 0;
    159 	struct stat sb;
    160 	if (stat(name, &sb) == 0) {
    161 		i32 fd_old = open(name, O_RDONLY);
    162 		i32 fd_new = open(new,  O_WRONLY|O_CREAT, sb.st_mode);
    163 		if (fd_old >= 0 && fd_new >= 0) {
    164 			u8 buf[4096];
    165 			iz copied = 0;
    166 			while (copied != sb.st_size) {
    167 				iz r = read(fd_old, buf, countof(buf));
    168 				if (r < 0) break;
    169 				iz w = write(fd_new, buf, (uz)r);
    170 				if (w < 0) break;
    171 				copied += w;
    172 			}
    173 			result = copied == sb.st_size;
    174 		}
    175 		if (fd_old != -1) close(fd_old);
    176 		if (fd_new != -1) close(fd_new);
    177 	}
    178 	return result;
    179 }
    180 
    181 function void *
    182 os_load_library(char *name, char *temp_name, Stream *e)
    183 {
    184 	if (temp_name) {
    185 		if (os_copy_file(name, temp_name))
    186 			name = temp_name;
    187 	}
    188 
    189 	void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL);
    190 	if (!result && e) {
    191 		stream_append_s8s(e, s8("os_load_library(\""), c_str_to_s8(name), s8("\"): "),
    192 		                  c_str_to_s8(dlerror()), s8("\n"));
    193 	}
    194 
    195 	if (temp_name)
    196 		unlink(temp_name);
    197 
    198 	return result;
    199 }
    200 
    201 function void *
    202 os_lookup_dynamic_symbol(void *h, char *name, Stream *e)
    203 {
    204 	void *result = 0;
    205 	if (h) {
    206 		result = dlsym(h, name);
    207 		if (!result && e) {
    208 			stream_append_s8s(e, s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name),
    209 			                  s8("\"): "), c_str_to_s8(dlerror()), s8("\n"));
    210 		}
    211 	}
    212 	return result;
    213 }
    214 
    215 function void
    216 os_unload_library(void *h)
    217 {
    218 	/* NOTE: glibc is buggy gnuware so we need to check this */
    219 	if (h)
    220 		dlclose(h);
    221 }
    222 
    223 function OS_ADD_FILE_WATCH_FN(os_add_file_watch)
    224 {
    225 	s8 directory  = path;
    226 	directory.len = s8_scan_backwards(path, '/');
    227 	ASSERT(directory.len > 0);
    228 
    229 	u64 hash = s8_hash(directory);
    230 	FileWatchContext *fwctx = &os->file_watch_context;
    231 	FileWatchDirectory *dir = lookup_file_watch_directory(fwctx, hash);
    232 	if (!dir) {
    233 		ASSERT(path.data[directory.len] == '/');
    234 		dir = da_push(a, fwctx);
    235 		dir->hash   = hash;
    236 		dir->name   = push_s8_zero(a, directory);
    237 		u32 mask    = IN_MOVED_TO|IN_CLOSE_WRITE;
    238 		dir->handle = inotify_add_watch((i32)fwctx->handle, (c8 *)dir->name.data, mask);
    239 	}
    240 
    241 	FileWatch *fw = da_push(a, dir);
    242 	fw->user_data = user_data;
    243 	fw->callback  = callback;
    244 	fw->hash      = s8_hash(s8_cut_head(path, dir->name.len + 1));
    245 }
    246 
    247 i32 pthread_setname_np(pthread_t, char *);
    248 function iptr
    249 os_create_thread(Arena arena, iptr user_context, s8 name, os_thread_entry_point_fn *fn)
    250 {
    251 	pthread_t result;
    252 	pthread_create(&result, 0, (void *)fn, (void *)user_context);
    253 	pthread_setname_np(result, (char *)name.data);
    254 	return (iptr)result;
    255 }
    256 
    257 function OS_WAIT_ON_VALUE_FN(os_wait_on_value)
    258 {
    259 	struct timespec *timeout = 0, timeout_value;
    260 	if (timeout_ms != (u32)-1) {
    261 		timeout_value.tv_sec  = timeout_ms / 1000;
    262 		timeout_value.tv_nsec = (timeout_ms % 1000) * 1000000;
    263 		timeout = &timeout_value;
    264 	}
    265 	return syscall(SYS_futex, value, FUTEX_WAIT, current, timeout, 0, 0) == 0;
    266 }
    267 
    268 function OS_WAKE_WAITERS_FN(os_wake_waiters)
    269 {
    270 	if (sync) {
    271 		atomic_store_u32(sync, 0);
    272 		syscall(SYS_futex, sync, FUTEX_WAKE, I32_MAX, 0, 0, 0);
    273 	}
    274 }
    275 
    276 function OS_SHARED_MEMORY_LOCK_REGION_FN(os_shared_memory_region_lock)
    277 {
    278 	b32 result = 0;
    279 	for (;;) {
    280 		i32 current = 0;
    281 		if (atomic_cas_u32(locks + lock_index, &current, 1)) {
    282 			result = 1;
    283 			break;
    284 		}
    285 		if (!timeout_ms || !os_wait_on_value(locks + lock_index, current, timeout_ms))
    286 			break;
    287 	}
    288 	return result;
    289 }
    290 
    291 function OS_SHARED_MEMORY_UNLOCK_REGION_FN(os_shared_memory_region_unlock)
    292 {
    293 	i32 *lock = locks + lock_index;
    294 	assert(atomic_load_u32(lock));
    295 	atomic_store_u32(lock, 0);
    296 	os_wake_waiters(lock);
    297 }
    298 
    299 function void
    300 os_init(OS *os, Arena *program_memory)
    301 {
    302 	os->file_watch_context.handle = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
    303 	os->error_handle              = STDERR_FILENO;
    304 }