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