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