base_linux.c (7727B)
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 str8 186 os_read_entire_file(Arena *arena, const char *file) 187 { 188 str8 result = {0}; 189 struct stat sb; 190 i32 fd = open(file, O_RDONLY); 191 if (fd >= 0 && fstat(fd, &sb) >= 0) { 192 result.data = push_array(arena, u8, sb.st_size); 193 do { 194 i64 rlen = read(fd, result.data + result.length, (u64)(sb.st_size - result.length)); 195 if (rlen > 0) result.length += rlen; 196 } while (result.length != sb.st_size && errno != EINTR); 197 if (result.length != sb.st_size) { 198 arena_pop(arena, sb.st_size); 199 zero_struct(&result); 200 } 201 } 202 if (fd >= 0) close(fd); 203 204 return result; 205 } 206 207 function b32 208 os_write_new_file(char *fname, str8 raw) 209 { 210 b32 result = 0; 211 i32 fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0600); 212 if (fd != INVALID_FILE) { 213 result = os_write_file(fd, raw.data, raw.length); 214 close(fd); 215 } 216 return result; 217 } 218 219 function b32 220 os_file_exists(char *path) 221 { 222 struct stat st; 223 b32 result = stat(path, &st) == 0; 224 return result; 225 } 226 227 /* NOTE: complete garbage because there is no standarized copyfile() in POSix */ 228 function b32 229 os_copy_file(char *name, char *new) 230 { 231 b32 result = 0; 232 struct stat sb; 233 if (stat(name, &sb) == 0) { 234 i32 fd_old = open(name, O_RDONLY); 235 i32 fd_new = open(new, O_WRONLY|O_CREAT, sb.st_mode); 236 if (fd_old >= 0 && fd_new >= 0) { 237 u8 buf[4096]; 238 i64 copied = 0; 239 while (copied != sb.st_size) { 240 i64 r = read(fd_old, buf, countof(buf)); 241 if (r < 0) break; 242 i64 w = write(fd_new, buf, (u64)r); 243 if (w < 0) break; 244 copied += w; 245 } 246 result = copied == sb.st_size; 247 } 248 if (fd_old != -1) close(fd_old); 249 if (fd_new != -1) close(fd_new); 250 } 251 return result; 252 } 253 254 function OSLinuxEntity * 255 os_entity_allocate(OSLinuxEntityKind kind) 256 { 257 OSLinuxEntity *result = 0; 258 DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock)) 259 { 260 result = SLLPopFreelist(os_linux_context.entity_freelist); 261 if (!result) result = push_struct_no_zero(os_linux_context.arena, OSLinuxEntity); 262 } 263 264 zero_struct(result); 265 result->kind = kind; 266 return result; 267 } 268 269 BASE_EXPORT OSThread 270 os_create_thread(const char *name, void *user_context, os_thread_entry_point_fn *fn) 271 { 272 pthread_t thread; 273 pthread_create(&thread, 0, (void *)fn, (void *)user_context); 274 275 if (name) { 276 char buffer[16]; 277 str8 name_str = str8_from_c_str((char *)name); 278 u64 length = (u64)Clamp(name_str.length, 0, countof(buffer) - 1); 279 memory_copy(buffer, (char *)name, length); 280 buffer[length] = 0; 281 pthread_setname_np(thread, buffer); 282 } 283 284 OSThread result = {(u64)thread}; 285 return result; 286 } 287 288 BASE_EXPORT OSBarrier 289 os_barrier_alloc(u32 count) 290 { 291 OSBarrier result = {0}; 292 DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock)) 293 { 294 pthread_barrier_t *barrier = push_struct(os_linux_context.arena, pthread_barrier_t); 295 pthread_barrier_init(barrier, 0, count); 296 result.value[0] = (u64)barrier; 297 } 298 return result; 299 } 300 301 BASE_EXPORT void 302 os_barrier_enter(OSBarrier barrier) 303 { 304 pthread_barrier_t *b = (pthread_barrier_t *)barrier.value[0]; 305 if (b) pthread_barrier_wait(b); 306 } 307 308 BASE_EXPORT b32 309 os_wait_on_address(i32 *value, i32 current, u32 timeout_ms) 310 { 311 struct timespec *timeout = 0, timeout_value; 312 if (timeout_ms != (u32)-1) { 313 timeout_value.tv_sec = timeout_ms / 1000; 314 timeout_value.tv_nsec = (timeout_ms % 1000) * 1000000; 315 timeout = &timeout_value; 316 } 317 return syscall(SYS_futex, value, FUTEX_WAIT, current, timeout, 0, 0) == 0; 318 } 319 320 BASE_EXPORT void 321 os_wake_all_waiters(i32 *sync) 322 { 323 if (sync) { 324 atomic_store_u32(sync, 0); 325 syscall(SYS_futex, sync, FUTEX_WAKE, I32_MAX, 0, 0, 0); 326 } 327 } 328 329 #if !BASE_PLATFORM_NO_MAIN 330 BASE_IMPORT void entry_point(i32 argc, char *argv[]); 331 332 extern i32 333 main(i32 argc, char *argv[]) 334 { 335 os_system_info_init(); 336 os_linux_context.arena = arena_create(.name = "Platform Arena"); 337 338 entry_point(argc, argv); 339 340 return 0; 341 } 342 #endif