base_linux.c (7954B)
1 /* See LICENSE for license details. */ 2 3 /* NOTE(rnp): provides the platform layer for everything in this repo. */ 4 5 #define OS_SHARED_MEMORY_NAME "/ogl_beamformer_shared_memory" 6 7 #define OS_PATH_SEPARATOR_CHAR '/' 8 #define OS_PATH_SEPARATOR "/" 9 10 #include "base_platform.h" 11 12 #include "util.h" 13 14 #include <errno.h> 15 #include <fcntl.h> 16 #include <linux/futex.h> 17 #include <poll.h> 18 #include <pthread.h> 19 #include <sys/auxv.h> 20 #include <sys/inotify.h> 21 #include <sys/mman.h> 22 #include <sys/stat.h> 23 #include <sys/syscall.h> 24 #include <sys/sysinfo.h> 25 #include <unistd.h> 26 27 typedef struct OSLinuxEntity OSLinuxEntity; 28 typedef struct { 29 void *handle; 30 OSLinuxEntity *prev, *next; 31 } OSLinuxWindow; 32 33 typedef enum { 34 OSLinuxFileWatchKind_Platform, 35 OSLinuxFileWatchKind_User, 36 } OSLinuxFileWatchKind; 37 38 typedef struct OSLinuxFileWatchDirectory OSLinuxFileWatchDirectory; 39 typedef struct OSLinuxFileWatch OSLinuxFileWatch; 40 struct OSLinuxFileWatch { 41 OSLinuxFileWatchKind kind; 42 u64 hash; 43 u64 update_time; 44 void *user_context; 45 46 OSLinuxFileWatchDirectory *parent; 47 OSLinuxFileWatch *prev, *next; 48 }; 49 50 struct OSLinuxFileWatchDirectory { 51 u64 hash; 52 i64 handle; 53 str8 name; 54 55 OSLinuxFileWatch *first_child; 56 OSLinuxFileWatch *last_child; 57 OSLinuxFileWatchDirectory *prev, *next; 58 }; 59 60 typedef enum { 61 OSLinuxEntityKind_Window, 62 OSLinuxEntityKind_FileWatch, 63 OSLinuxEntityKind_FileWatchDirectory, 64 } OSLinuxEntityKind; 65 66 struct OSLinuxEntity { 67 OSLinuxEntityKind kind; 68 union { 69 OSLinuxFileWatch file_watch; 70 OSLinuxFileWatchDirectory file_watch_directory; 71 OSLinuxWindow window; 72 } as; 73 OSLinuxEntity *next; 74 }; 75 76 typedef struct { 77 OSSystemInfo system_info; 78 79 Arena *arena; 80 i32 arena_lock; 81 82 i32 inotify_handle; 83 84 struct { 85 OSLinuxFileWatchDirectory *first; 86 OSLinuxFileWatchDirectory *last; 87 } file_watch_directories; 88 89 struct { 90 OSLinuxEntity *first; 91 OSLinuxEntity *last; 92 } windows; 93 94 OSLinuxEntity *entity_freelist; 95 } OSLinux_Context; 96 global OSLinux_Context os_linux_context; 97 98 function b32 99 os_write_file(i32 file, void *data, i64 length) 100 { 101 i64 offset = 0; 102 while (offset < length) { 103 i64 r = write(file, (u8 *)data + offset, (u64)(length - offset)); 104 if (r < 0 && errno != EINTR) break; 105 if (r >= 0) offset += r; 106 } 107 return offset == length; 108 } 109 110 BASE_EXPORT no_return void 111 os_exit(i32 code) 112 { 113 _exit(code); 114 unreachable(); 115 } 116 117 function u64 118 os_timer_frequency(void) 119 { 120 return 1000000000ULL; 121 } 122 123 BASE_EXPORT u64 124 os_timer_count(void) 125 { 126 struct timespec time = {0}; 127 clock_gettime(CLOCK_MONOTONIC, &time); 128 u64 result = (u64)time.tv_sec * 1000000000ULL + (u64)time.tv_nsec; 129 return result; 130 } 131 132 function u64 133 os_number_of_processors(void) 134 { 135 u64 set[128 / sizeof(u64)] = {0}; 136 syscall(SYS_sched_getaffinity, 0, sizeof(set), set); 137 138 u64 result = 0; 139 for EachElement(set, it) 140 result += popcount_u64(set[it]); 141 return result > 0 ? result : 1; 142 } 143 144 function void 145 os_system_info_init(void) 146 { 147 os_linux_context.system_info.timer_frequency = os_timer_frequency(); 148 os_linux_context.system_info.logical_processor_count = os_number_of_processors(); 149 os_linux_context.system_info.page_size = ARCH_X64? KB(4) : getauxval(AT_PAGESZ); 150 os_linux_context.system_info.path_separator_byte = '/'; 151 } 152 153 BASE_EXPORT OSSystemInfo * 154 os_system_info(void) 155 { 156 #if BASE_PLATFORM_NO_MAIN 157 if unlikely(os_linux_context.system_info.path_separator_byte == 0) 158 os_system_info_init(); 159 #endif 160 return &os_linux_context.system_info; 161 } 162 163 BASE_EXPORT void * 164 os_memory_reserve(u64 size) 165 { 166 void *result = mmap(0, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); 167 if (result == MAP_FAILED) 168 result = 0; 169 return result; 170 } 171 172 BASE_EXPORT void 173 os_memory_release(void *base, u64 size) 174 { 175 munmap(base, size); 176 } 177 178 BASE_EXPORT b32 179 os_memory_commit(void *base, u64 size) 180 { 181 mprotect(base, size, PROT_READ|PROT_WRITE); 182 return 1; 183 } 184 185 BASE_EXPORT void 186 os_memory_uncommit(void *base, u64 size) 187 { 188 madvise(base, size, MADV_DONTNEED); 189 mprotect(base, size, PROT_NONE); 190 } 191 192 BASE_EXPORT void 193 os_memory_seal(void *base, u64 size) 194 { 195 mprotect(base, size, PROT_READ); 196 } 197 198 BASE_EXPORT str8 199 os_read_entire_file(Arena *arena, const char *file) 200 { 201 str8 result = {0}; 202 struct stat sb; 203 i32 fd = open(file, O_RDONLY); 204 if (fd >= 0 && fstat(fd, &sb) >= 0) { 205 result.data = push_array(arena, u8, sb.st_size); 206 do { 207 i64 rlen = read(fd, result.data + result.length, (u64)(sb.st_size - result.length)); 208 if (rlen > 0) result.length += rlen; 209 } while (result.length != sb.st_size && errno != EINTR); 210 if (result.length != sb.st_size) { 211 arena_pop(arena, sb.st_size); 212 zero_struct(&result); 213 } 214 } 215 if (fd >= 0) close(fd); 216 217 return result; 218 } 219 220 function b32 221 os_write_new_file(char *fname, str8 raw) 222 { 223 b32 result = 0; 224 i32 fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0600); 225 if (fd != INVALID_FILE) { 226 result = os_write_file(fd, raw.data, raw.length); 227 close(fd); 228 } 229 return result; 230 } 231 232 function b32 233 os_file_exists(char *path) 234 { 235 struct stat st; 236 b32 result = stat(path, &st) == 0; 237 return result; 238 } 239 240 /* NOTE: complete garbage because there is no standarized copyfile() in POSix */ 241 function b32 242 os_copy_file(char *name, char *new) 243 { 244 b32 result = 0; 245 struct stat sb; 246 if (stat(name, &sb) == 0) { 247 i32 fd_old = open(name, O_RDONLY); 248 i32 fd_new = open(new, O_WRONLY|O_CREAT, sb.st_mode); 249 if (fd_old >= 0 && fd_new >= 0) { 250 u8 buf[4096]; 251 i64 copied = 0; 252 while (copied != sb.st_size) { 253 i64 r = read(fd_old, buf, countof(buf)); 254 if (r < 0) break; 255 i64 w = write(fd_new, buf, (u64)r); 256 if (w < 0) break; 257 copied += w; 258 } 259 result = copied == sb.st_size; 260 } 261 if (fd_old != -1) close(fd_old); 262 if (fd_new != -1) close(fd_new); 263 } 264 return result; 265 } 266 267 function OSLinuxEntity * 268 os_entity_allocate(OSLinuxEntityKind kind) 269 { 270 OSLinuxEntity *result = 0; 271 DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock)) 272 { 273 result = SLLPopFreelist(os_linux_context.entity_freelist); 274 if (!result) result = push_struct_no_zero(os_linux_context.arena, OSLinuxEntity); 275 } 276 277 zero_struct(result); 278 result->kind = kind; 279 return result; 280 } 281 282 BASE_EXPORT OSThread 283 os_create_thread(const char *name, void *user_context, os_thread_entry_point_fn *fn) 284 { 285 pthread_t thread; 286 pthread_create(&thread, 0, (void *)fn, (void *)user_context); 287 288 if (name) { 289 char buffer[16]; 290 str8 name_str = str8_from_c_str((char *)name); 291 u64 length = (u64)Clamp(name_str.length, 0, countof(buffer) - 1); 292 memory_copy(buffer, (char *)name, length); 293 buffer[length] = 0; 294 pthread_setname_np(thread, buffer); 295 } 296 297 OSThread result = {(u64)thread}; 298 return result; 299 } 300 301 BASE_EXPORT OSBarrier 302 os_barrier_alloc(u32 count) 303 { 304 OSBarrier result = {0}; 305 DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock)) 306 { 307 pthread_barrier_t *barrier = push_struct(os_linux_context.arena, pthread_barrier_t); 308 pthread_barrier_init(barrier, 0, count); 309 result.value[0] = (u64)barrier; 310 } 311 return result; 312 } 313 314 BASE_EXPORT void 315 os_barrier_enter(OSBarrier barrier) 316 { 317 pthread_barrier_t *b = (pthread_barrier_t *)barrier.value[0]; 318 if (b) pthread_barrier_wait(b); 319 } 320 321 BASE_EXPORT b32 322 os_wait_on_address(i32 *value, i32 current, u32 timeout_ms) 323 { 324 struct timespec *timeout = 0, timeout_value; 325 if (timeout_ms != (u32)-1) { 326 timeout_value.tv_sec = timeout_ms / 1000; 327 timeout_value.tv_nsec = (timeout_ms % 1000) * 1000000; 328 timeout = &timeout_value; 329 } 330 return syscall(SYS_futex, value, FUTEX_WAIT, current, timeout, 0, 0) == 0; 331 } 332 333 BASE_EXPORT void 334 os_wake_all_waiters(i32 *sync) 335 { 336 if (sync) { 337 atomic_store_u32(sync, 0); 338 syscall(SYS_futex, sync, FUTEX_WAKE, I32_MAX, 0, 0, 0); 339 } 340 } 341 342 #if !BASE_PLATFORM_NO_MAIN 343 BASE_IMPORT void entry_point(i32 argc, char *argv[]); 344 345 extern i32 346 main(i32 argc, char *argv[]) 347 { 348 os_system_info_init(); 349 os_linux_context.arena = arena_create(.name = "Platform Arena"); 350 351 entry_point(argc, argv); 352 353 return 0; 354 } 355 #endif