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