os_linux.c (8305B)
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 <sys/sysinfo.h> 23 #include <unistd.h> 24 25 typedef struct { 26 Arena arena; 27 i32 arena_lock; 28 i32 inotify_handle; 29 OS_SystemInfo system_info; 30 } OS_LinuxContext; 31 global OS_LinuxContext os_linux_context; 32 33 #ifdef _DEBUG 34 function void * 35 os_get_module(char *name, Stream *e) 36 { 37 void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD); 38 if (!result && e) { 39 stream_append_s8s(e, s8("os_get_module(\""), c_str_to_s8(name), s8("\"): "), 40 c_str_to_s8(dlerror()), s8("\n")); 41 } 42 return result; 43 } 44 #endif 45 46 function OS_WRITE_FILE_FN(os_write_file) 47 { 48 while (raw.len > 0) { 49 iz r = write((i32)file, raw.data, (uz)raw.len); 50 if (r < 0) return 0; 51 raw = s8_cut_head(raw, r); 52 } 53 return 1; 54 } 55 56 function void __attribute__((noreturn)) 57 os_exit(i32 code) 58 { 59 _exit(code); 60 unreachable(); 61 } 62 63 function void __attribute__((noreturn)) 64 os_fatal(s8 msg) 65 { 66 os_write_file(STDERR_FILENO, msg); 67 os_exit(1); 68 unreachable(); 69 } 70 71 function iptr 72 os_error_handle(void) 73 { 74 return STDERR_FILENO; 75 } 76 77 function s8 78 os_path_separator(void) 79 { 80 return s8("/"); 81 } 82 83 function u64 84 os_get_timer_frequency(void) 85 { 86 return 1000000000ULL; 87 } 88 89 function u64 90 os_get_timer_counter(void) 91 { 92 struct timespec time = {0}; 93 clock_gettime(CLOCK_MONOTONIC, &time); 94 u64 result = (u64)time.tv_sec * 1000000000ULL + (u64)time.tv_nsec; 95 return result; 96 } 97 98 function void 99 os_common_init(void) 100 { 101 os_linux_context.system_info.logical_processor_count = (u32)get_nprocs(); 102 os_linux_context.system_info.page_size = (u32)getpagesize(); 103 } 104 105 function iz 106 os_round_up_to_page_size(iz value) 107 { 108 iz result = round_up_to(value, os_linux_context.system_info.page_size); 109 return result; 110 } 111 112 function OS_ALLOC_ARENA_FN(os_alloc_arena) 113 { 114 Arena result = {0}; 115 capacity = os_round_up_to_page_size(capacity); 116 result.beg = mmap(0, (uz)capacity, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); 117 if (result.beg == MAP_FAILED) 118 os_fatal(s8("os_alloc_arena: couldn't allocate memory\n")); 119 result.end = result.beg + capacity; 120 asan_poison_region(result.beg, result.end - result.beg); 121 return result; 122 } 123 124 function OS_READ_WHOLE_FILE_FN(os_read_whole_file) 125 { 126 s8 result = s8(""); 127 128 struct stat sb; 129 i32 fd = open(file, O_RDONLY); 130 if (fd >= 0 && fstat(fd, &sb) >= 0) { 131 result = s8_alloc(arena, sb.st_size); 132 iz rlen = read(fd, result.data, (uz)result.len); 133 if (rlen != result.len) 134 result = s8(""); 135 } 136 if (fd >= 0) close(fd); 137 138 return result; 139 } 140 141 function OS_WRITE_NEW_FILE_FN(os_write_new_file) 142 { 143 b32 result = 0; 144 i32 fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0600); 145 if (fd != INVALID_FILE) { 146 result = os_write_file(fd, raw); 147 close(fd); 148 } 149 return result; 150 } 151 152 function b32 153 os_file_exists(char *path) 154 { 155 struct stat st; 156 b32 result = stat(path, &st) == 0; 157 return result; 158 } 159 160 function SharedMemoryRegion 161 os_create_shared_memory_area(Arena *arena, char *name, u32 lock_count, iz requested_capacity) 162 { 163 iz capacity = os_round_up_to_page_size(requested_capacity); 164 SharedMemoryRegion result = {0}; 165 i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); 166 if (fd > 0 && ftruncate(fd, capacity) != -1) { 167 void *new = mmap(0, (uz)capacity, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 168 if (new != MAP_FAILED) result.region = new; 169 } 170 if (fd > 0) close(fd); 171 return result; 172 } 173 174 /* NOTE: complete garbage because there is no standarized copyfile() in POSix */ 175 function b32 176 os_copy_file(char *name, char *new) 177 { 178 b32 result = 0; 179 struct stat sb; 180 if (stat(name, &sb) == 0) { 181 i32 fd_old = open(name, O_RDONLY); 182 i32 fd_new = open(new, O_WRONLY|O_CREAT, sb.st_mode); 183 if (fd_old >= 0 && fd_new >= 0) { 184 u8 buf[4096]; 185 iz copied = 0; 186 while (copied != sb.st_size) { 187 iz r = read(fd_old, buf, countof(buf)); 188 if (r < 0) break; 189 iz w = write(fd_new, buf, (uz)r); 190 if (w < 0) break; 191 copied += w; 192 } 193 result = copied == sb.st_size; 194 } 195 if (fd_old != -1) close(fd_old); 196 if (fd_new != -1) close(fd_new); 197 } 198 return result; 199 } 200 201 function void * 202 os_load_library(char *name, char *temp_name, Stream *e) 203 { 204 if (temp_name) { 205 if (os_copy_file(name, temp_name)) 206 name = temp_name; 207 } 208 209 void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL); 210 if (!result && e) { 211 stream_append_s8s(e, s8("os_load_library(\""), c_str_to_s8(name), s8("\"): "), 212 c_str_to_s8(dlerror()), s8("\n")); 213 } 214 215 if (temp_name) 216 unlink(temp_name); 217 218 return result; 219 } 220 221 function void * 222 os_lookup_dynamic_symbol(void *h, char *name, Stream *e) 223 { 224 void *result = 0; 225 if (h) { 226 result = dlsym(h, name); 227 if (!result && e) { 228 stream_append_s8s(e, s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name), 229 s8("\"): "), c_str_to_s8(dlerror()), s8("\n")); 230 } 231 } 232 return result; 233 } 234 235 function void 236 os_unload_library(void *h) 237 { 238 /* NOTE: glibc is buggy gnuware so we need to check this */ 239 if (h) 240 dlclose(h); 241 } 242 243 function OS_ADD_FILE_WATCH_FN(os_add_file_watch) 244 { 245 s8 directory = path; 246 directory.len = s8_scan_backwards(path, '/'); 247 assert(directory.len > 0); 248 249 u64 hash = u64_hash_from_s8(directory); 250 FileWatchDirectory *dir = lookup_file_watch_directory(fwctx, hash); 251 if (!dir) { 252 assert(path.data[directory.len] == '/'); 253 dir = da_push(a, fwctx); 254 dir->hash = hash; 255 dir->name = push_s8(a, directory); 256 u32 mask = IN_MOVED_TO|IN_CLOSE_WRITE; 257 dir->handle = inotify_add_watch(os_linux_context.inotify_handle, (c8 *)dir->name.data, mask); 258 } 259 260 FileWatch *fw = da_push(a, dir); 261 fw->user_data = user_data; 262 fw->callback = callback; 263 fw->hash = u64_hash_from_s8(s8_cut_head(path, dir->name.len + 1)); 264 } 265 266 function OS_WAIT_ON_VALUE_FN(os_wait_on_value) 267 { 268 struct timespec *timeout = 0, timeout_value; 269 if (timeout_ms != (u32)-1) { 270 timeout_value.tv_sec = timeout_ms / 1000; 271 timeout_value.tv_nsec = (timeout_ms % 1000) * 1000000; 272 timeout = &timeout_value; 273 } 274 return syscall(SYS_futex, value, FUTEX_WAIT, current, timeout, 0, 0) == 0; 275 } 276 277 function OS_WAKE_WAITERS_FN(os_wake_waiters) 278 { 279 if (sync) { 280 atomic_store_u32(sync, 0); 281 syscall(SYS_futex, sync, FUTEX_WAKE, I32_MAX, 0, 0, 0); 282 } 283 } 284 285 function b32 286 os_take_lock(i32 *lock, i32 timeout_ms) 287 { 288 b32 result = 0; 289 for (;;) { 290 i32 current = 0; 291 if (atomic_cas_u32(lock, ¤t, 1)) 292 result = 1; 293 if (result || !timeout_ms || (!os_wait_on_value(lock, current, (u32)timeout_ms) && timeout_ms != -1)) 294 break; 295 } 296 return result; 297 } 298 299 function void 300 os_release_lock(i32 *lock) 301 { 302 assert(atomic_load_u32(lock)); 303 atomic_store_u32(lock, 0); 304 os_wake_waiters(lock); 305 } 306 307 function OS_SHARED_MEMORY_LOCK_REGION_FN(os_shared_memory_region_lock) 308 { 309 b32 result = os_take_lock(locks + lock_index, (i32)timeout_ms); 310 return result; 311 } 312 313 function OS_SHARED_MEMORY_UNLOCK_REGION_FN(os_shared_memory_region_unlock) 314 { 315 os_release_lock(locks + lock_index); 316 } 317 318 function OS_SystemInfo * 319 os_get_system_info(void) 320 { 321 return &os_linux_context.system_info; 322 } 323 324 function Barrier 325 os_barrier_alloc(u32 count) 326 { 327 Barrier result = {0}; 328 DeferLoop(os_take_lock(&os_linux_context.arena_lock, -1), 329 os_release_lock(&os_linux_context.arena_lock)) 330 { 331 pthread_barrier_t *barrier = push_struct(&os_linux_context.arena, pthread_barrier_t); 332 pthread_barrier_init(barrier, 0, count); 333 result.value[0] = (u64)barrier; 334 } 335 return result; 336 } 337 338 function void 339 os_barrier_wait(Barrier barrier) 340 { 341 pthread_barrier_t *b = (pthread_barrier_t *)barrier.value[0]; 342 if (b) pthread_barrier_wait(b); 343 } 344 345 function iptr 346 os_create_thread(iptr user_context, os_thread_entry_point_fn *fn) 347 { 348 pthread_t result; 349 pthread_create(&result, 0, (void *)fn, (void *)user_context); 350 return (iptr)result; 351 } 352 353 function void 354 os_set_thread_name(iptr thread, s8 name) 355 { 356 char buffer[16]; 357 u64 length = (u64)CLAMP(name.len, 0, countof(buffer) - 1); 358 mem_copy(buffer, name.data, length); 359 buffer[length] = 0; 360 pthread_setname_np((pthread_t)thread, buffer); 361 }