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, ¤t, 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 }