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