Files
metadesk/source/os/linux/os_linux.c
T
ed 0ab226f739 adding prefixes to source
They'll be removed on demand in libgen repo
2025-02-12 14:40:11 -05:00

1093 lines
30 KiB
C

#ifdef INTELLISENSE_DIRECTIVES
# include "os_linux.h"
# include "os/os.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Globals
MD_API_C md_global MD_OS_LNX_State md_os_lnx_state = {0};
MD_API_C md_thread_static MD_OS_LNX_SafeCallChain* md_os_lnx_safe_call_chain = 0;
////////////////////////////////
//~ rjf: Entities
MD_OS_LNX_Entity*
md_os_lnx_entity_alloc(MD_OS_LNX_EntityKind kind)
{
MD_OS_LNX_Entity* entity = 0;
md_defer_loop(pthread_mutex_lock (&md_os_lnx_state.entity_mutex), pthread_mutex_unlock(&md_os_lnx_state.entity_mutex))
{
entity = md_os_lnx_state.entity_free;
if (entity) {
md_sll_stack_pop(md_os_lnx_state.entity_free);
}
else {
entity = md_push_array__no_zero(md_os_lnx_state.entity_arena, MD_OS_LNX_Entity, 1);
}
}
md_memory_zero_struct(entity);
entity->kind = kind;
return entity;
}
void
md_os_lnx_entity_release(MD_OS_LNX_Entity *entity) {
md_defer_loop(pthread_mutex_lock(&md_os_lnx_state.entity_mutex), pthread_mutex_unlock(&md_os_lnx_state.entity_mutex))
{
md_sll_stack_push(md_os_lnx_state.entity_free, entity);
}
}
////////////////////////////////
//~ rjf: Thread Entry Point
void*
md_os_lnx_thread_entry_point(void *ptr)
{
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity *)ptr;
MD_OS_ThreadFunctionType* func = entity->thread.func;
void* thread_ptr = entity->thread.ptr;
MD_TCTX md_tctx_;
md_tctx_init_and_equip(&md_tctx_);
func(thread_ptr);
md_tctx_release();
return 0;
}
////////////////////////////////
//~ rjf: @md_os_hooks System/Process Info (Implemented Per-OS)
MD_OS_SystemInfo*
md_os_get_system_info(void) {
return &md_os_lnx_state.system_info;
}
MD_OS_ProcessInfo*
md_os_get_process_info(void) {
return &md_os_lnx_state.process_info;
}
////////////////////////////////
//~ rjf: @md_os_hooks Thread Info (Implemented Per-OS)
void
md_os_set_thread_name(MD_String8 name)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
{
MD_String8 name_copy = md_push_str8_copy(scratch.arena, name);
pthread_t current_thread = pthread_self();
pthread_setname_np(current_thread, (char *)name_copy.str);
}
scratch_end(scratch);
}
////////////////////////////////
//~ rjf: @md_os_hooks Aborting (Implemented Per-OS)
void md_os_abort(MD_S32 exit_code) { exit(exit_code); }
////////////////////////////////
//~ rjf: @md_os_hooks File System (Implemented Per-OS)
//- rjf: files
MD_OS_Handle
md_os_file_open(MD_OS_AccessFlags flags, MD_String8 path)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
{
MD_String8 path_copy = md_push_str8_copy(scratch.arena, path);
int lnx_flags = 0;
if (flags & (MD_OS_AccessFlag_Read | MD_OS_AccessFlag_Write)) { lnx_flags = O_RDWR; }
else if (flags & MD_OS_AccessFlag_Write) { lnx_flags = O_WRONLY; }
else if (flags & MD_OS_AccessFlag_Read) { lnx_flags = O_RDONLY; }
if (flags & MD_OS_AccessFlag_Append) { lnx_flags |= O_APPEND; }
int fd = open((char *)path_copy.str, lnx_flags);
MD_OS_Handle handle = {0};
if (fd != -1) {
handle.u64[0] = fd;
}
}
scratch_end(scratch);
return handle;
}
void
md_os_file_close(MD_OS_Handle file) {
if (md_os_handle_match(file, md_os_handle_zero())) { return; }
int fd = (int)file.u64[0];
close(fd);
}
MD_U64
md_os_file_read(MD_OS_Handle file, MD_Rng1U64 rng, void* out_data)
{
if (md_os_handle_match(file, md_os_handle_zero())) { return 0; }
int fd = (int)file.u64[0];
if (rng.md_min != 0) {
lseek(fd, rng.md_min, SEEK_SET);
}
MD_U64 total_num_bytes_to_read = md_dim_1u64(rng);
MD_U64 total_num_bytes_read = 0;
MD_U64 total_num_bytes_left_to_read = total_num_bytes_to_read;
for (;total_num_bytes_left_to_read > 0;)
{
int read_result = read(fd, (MD_U8 *)out_data + total_num_bytes_read, total_num_bytes_left_to_read);
if (read_result >= 0) {
total_num_bytes_read += read_result;
total_num_bytes_left_to_read -= read_result;
}
else if (errno != EINTR) {
break;
}
}
return total_num_bytes_read;
}
MD_U64
md_os_file_write(MD_OS_Handle file, MD_Rng1U64 rng, void *data)
{
if(md_os_handle_match(file, md_os_handle_zero())) { return 0; }
int fd = (int)file.u64[0];
if (rng.md_min != 0) {
lseek(fd, rng.md_min, SEEK_SET);
}
MD_U64 total_num_bytes_to_write = md_dim_1u64(rng);
MD_U64 total_num_bytes_written = 0;
MD_U64 total_num_bytes_left_to_write = total_num_bytes_to_write;
for (;total_num_bytes_left_to_write > 0;)
{
int write_result = write(fd, (MD_U8*)data + total_num_bytes_written, total_num_bytes_left_to_write);
if (write_result >= 0) {
total_num_bytes_written += write_result;
total_num_bytes_left_to_write -= write_result;
}
else if (errno != EINTR) {
break;
}
}
return total_num_bytes_written;
}
MD_B32
md_os_file_set_times(MD_OS_Handle file, MD_DateTime date_time)
{
if(md_os_handle_match(file, md_os_handle_zero())) { return 0; }
int fd = (int)file.u64[0];
timespec time = md_os_lnx_timespec_from_date_time(date_time);
timespec times[2] = {time, time};
int futimens_result = futimens(fd, times);
MD_B32 good = (futimens_result != -1);
return good;
}
MD_FileProperties
md_os_properties_from_file(MD_OS_Handle file)
{
if(md_os_handle_match(file, md_os_handle_zero())) { return (MD_FileProperties){0}; }
int fd = (int)file.u64[0];
struct stat fd_stat = {0};
int fstat_result = fstat(fd, &fd_stat);
MD_FileProperties props = {0};
if (fstat_result != -1) {
props = md_os_lnx_file_properties_from_stat(&fd_stat);
}
return props;
}
MD_OS_FileID
md_os_id_from_file(MD_OS_Handle file)
{
if (md_os_handle_match(file, md_os_handle_zero())) { return (MD_OS_FileID){0}; }
int fd = (int)file.u64[0];
struct stat fd_stat = {0};
int fstat_result = fstat(fd, &fd_stat);
MD_OS_FileID id = {0};
if(fstat_result != -1) {
id.v[0] = fd_stat.st_dev;
id.v[1] = fd_stat.st_ino;
}
return id;
}
MD_B32
md_os_delete_file_at_path(MD_String8 path)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_B32 result = 0;
MD_String8 path_copy = md_push_str8_copy(scratch.arena, path);
if (remove((char*)path_copy.str) != -1) {
result = 1;
}
scratch_end(scratch);
return result;
}
MD_B32
md_os_copy_file_path(MD_String8 dst, MD_String8 src)
{
MD_B32 result = 0;
MD_OS_Handle src_h = md_os_file_open(MD_OS_AccessFlag_Read, src);
MD_OS_Handle dst_h = md_os_file_open(MD_OS_AccessFlag_Write, dst);
if ( !md_os_handle_match(src_h, md_os_handle_zero()) &&
!md_os_handle_match(dst_h, md_os_handle_zero()) )
{
MD_FileProperties src_props = md_os_properties_from_file(src_h);
MD_U64 size = src_props.size;
MD_U64 total_bytes_copied = 0;
MD_U64 bytes_left_to_copy = size;
for (;bytes_left_to_copy > 0;)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_U64 buffer_size = Min(bytes_left_to_copy, MD_MB(8));
MD_U8 *buffer = md_push_array__no_zero(scratch.arena, MD_U8, buffer_size);
MD_U64 bytes_read = md_os_file_read (src_h, md_r1u64(total_bytes_copied, total_bytes_copied+buffer_size), buffer);
MD_U64 bytes_written = md_os_file_write(dst_h, md_r1u64(total_bytes_copied, total_bytes_copied+bytes_read), buffer);
MD_U64 bytes_copied = Min(bytes_read, bytes_written);
bytes_left_to_copy -= bytes_copied;
total_bytes_copied += bytes_copied;
scratch_end(scratch);
if(bytes_copied == 0) {
break;
}
}
}
md_os_file_close(src_h);
md_os_file_close(dst_h);
return result;
}
MD_String8
md_os_full_path_from_path__ainfo(MD_AllocatorInfo ainfo, MD_String8 path)
{
char buffer[PATH_MAX] = {0};
MD_TempArena scratch = md_scratch_begin(ainfo); {
MD_String8 path_copy = md_push_str8_copy(scratch.arena, path);
realpath((char *)path_copy.str, buffer);
}
scratch_end(scratch);
MD_String8 result = md_alloc_str8_copy(ainfo, md_str8_cstring(buffer));
return result;
}
MD_B32
md_os_file_path_exists(MD_String8 path)
{
MD_B32 result = 0;
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String8 path_copy = md_push_str8_copy(scratch.arena, path);
int access_result = access((char *)path_copy.str, F_OK);
if (access_result == 0) {
result = 1;
}
scratch_end(scratch);
return result;
}
md_internal MD_FileProperties
md_os_properties_from_file_path(MD_String8 path)
{
MD_FileProperties props = {0};
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String8 path_copy = md_push_str8_copy(scratch.arena, path);
struct stat f_stat = {0};
int stat_result = stat((char *)path_copy.str, &f_stat);
if (stat_result != -1) {
props = md_os_lnx_file_properties_from_stat(&f_stat);
}
scratch_end(scratch);
return props;
}
//- rjf: file maps
MD_OS_Handle
md_os_file_map_open(MD_OS_AccessFlags flags, MD_OS_Handle file) {
MD_OS_Handle map = file;
return map;
}
void
md_os_file_map_close(MD_OS_Handle map) {
// NOTE(rjf): nothing to do; `map` handles are the same as `file` handles in
// the linux implementation (on Windows they require separate handles)
}
void*
md_os_file_map_view_open(MD_OS_Handle map, MD_OS_AccessFlags flags, MD_Rng1U64 range)
{
if (md_os_handle_match(map, md_os_handle_zero())) { return 0; }
int prot_flags = 0;
if (flags & MD_OS_AccessFlag_Write) { prot_flags |= PROT_WRITE; }
if (flags & MD_OS_AccessFlag_Read) { prot_flags |= PROT_READ; }
int fd = (int)map.u64[0];
int map_flags = MAP_PRIVATE;
void* base = mmap(0, md_dim_1u64(range), prot_flags, map_flags, fd, range.md_min);
return base;
}
void
md_os_file_map_view_close(MD_OS_Handle map, void* ptr, MD_Rng1U64 range) {
munmap(ptr, md_dim_1u64(range));
}
//- rjf: directory iteration
MD_OS_FileIter*
md_os_file_iter_begin__ainfo(MD_AllocatorInfo ainfo, MD_String8 path, MD_OS_FileIterFlags flags)
{
MD_OS_FileIter*
base_iter = md_alloc_array(ainfo, MD_OS_FileIter, 1);
base_iter->flags = flags;
MD_OS_LNX_FileIter* iter = (MD_OS_LNX_FileIter*)base_iter->memory;
{
MD_String8 path_copy = md_str8_copy(arena, path);
iter->dir = opendir((char*)path_copy.str);
iter->path = path_copy;
}
return base_iter;
}
MD_B32
md_os_file_iter_next(MD_Arena* arena, MD_OS_FileIter* iter, MD_OS_FileInfo* info_out)
{
MD_B32 good = 0;
MD_OS_LNX_FileIter* lnx_iter = (MD_OS_LNX_FileIter*)iter->memory;
for(;;)
{
// rjf: get next entry
lnx_iter->dp = readdir(lnx_iter->dir);
good = (lnx_iter->dp != 0);
// rjf: unpack entry info
struct stat st = {0};
int stat_result = 0;
if(good)
{
MD_TempArena scratch = md_scratch_begin(&arena, 1);
MD_String8 full_path = md_str8f(scratch.arena, "%S/%s", lnx_iter->path, lnx_iter->dp->d_name);
stat_result = stat((char *)full_path.str, &st);
scratch_end(scratch);
}
// rjf: determine if filtered
MD_B32 filtered = 0;
if(good)
{
filtered = ((st.st_mode == S_IFDIR && iter->flags & MD_OS_FileIterFlag_SkipFolders) ||
(st.st_mode == S_IFREG && iter->flags & MD_OS_FileIterFlag_SkipFiles) ||
(lnx_iter->dp->d_name[0] == '.' && lnx_iter->dp->d_name[1] == 0) ||
(lnx_iter->dp->d_name[0] == '.' && lnx_iter->dp->d_name[1] == '.' && lnx_iter->dp->d_name[2] == 0));
}
// rjf: output & exit, if good & unfiltered
if (good && !filtered)
{
info_out->name = md_str8_copy(arena, md_str8_cstring(lnx_iter->dp->d_name));
if (stat_result != -1) {
info_out->props = md_os_lnx_file_properties_from_stat(&st);
}
break;
}
// rjf: exit if not good
if (!good)
{
break;
}
}
return good;
}
void
md_os_file_iter_end(MD_OS_FileIter* iter) {
MD_OS_LNX_FileIter* lnx_iter = (MD_OS_LNX_FileIter*)iter->memory;
closedir(lnx_iter->dir);
}
//- rjf: directory creation
MD_B32
md_os_make_directory(MD_String8 path)
{
MD_B32 result = 0;
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String8 path_copy = md_push_str8_copy(scratch.arena, path);
if (mkdir((char*)path_copy.str, 0777) != -1) {
result = 1;
}
scratch_end(scratch);
return result;
}
////////////////////////////////
//~ rjf: @md_os_hooks Shared Memory (Implemented Per-OS)
MD_OS_Handle
md_os_shared_memory_alloc(MD_U64 size, MD_String8 name)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String8 name_copy = md_push_str8_copy(scratch.arena, name);
int id = shm_open((char *)name_copy.str, O_RDWR, 0);
ftruncate(id, size);
scratch_end(scratch);
MD_OS_Handle result = {(MD_U64)id};
return result;
}
MD_OS_Handle
md_os_shared_memory_open(MD_String8 name)
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_String8 name_copy = md_push_str8_copy(scratch.arena, name);
int id = shm_open((char *)name_copy.str, O_RDWR, 0);
scratch_end(scratch);
MD_OS_Handle result = {(MD_U64)id};
return result;
}
void
md_os_shared_memory_close(MD_OS_Handle handle) {
if (md_os_handle_match(handle, md_os_handle_zero())) { return; }
int id = (int)handle.u64[0];
close(id);
}
void*
md_os_shared_memory_view_open(MD_OS_Handle handle, MD_Rng1U64 range) {
if (md_os_handle_match(handle, md_os_handle_zero())) { return 0; }
int id = (int)handle.u64[0];
void* base = mmap(0, md_dim_1u64(range), PROT_READ|PROT_WRITE, MAP_SHARED, id, range.md_min);
return base;
}
void
md_os_shared_memory_view_close(MD_OS_Handle handle, void* ptr, MD_Rng1U64 range) {
if (md_os_handle_match(handle, md_os_handle_zero())) { return; }
munmap(ptr, md_dim_1u64(range));
}
////////////////////////////////
//~ rjf: @md_os_hooks Time (Implemented Per-OS)
MD_U64
md_os_now_microseconds(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
MD_U64 result = t.tv_sec * md_million(1) + (t.tv_nsec / thousand(1));
return result;
}
MD_U32
md_os_now_unix(void) {
time_t t = time(0);
return (MD_U32)t;
}
MD_DateTime
md_os_now_universal_time(void)
{
time_t t = 0;
time(&t);
struct tm universal_tm = {0};
gmtime_r(&t, &universal_tm);
MD_DateTime result = md_os_lnx_date_time_from_tm(universal_tm, 0);
return result;
}
MD_DateTime
md_os_universal_time_from_local(MD_DateTime* date_time)
{
// rjf: local MD_DateTime -> universal time_t
tm local_tm = md_os_lnx_tm_from_date_time(*date_time);
local_tm.tm_isdst = -1;
time_t universal_t = mktime(&local_tm);
// rjf: universal time_t -> MD_DateTime
tm universal_tm = {0};
gmtime_r(&universal_t, &universal_tm);
MD_DateTime result = md_os_lnx_date_time_from_tm(universal_tm, 0);
return result;
}
MD_DateTime
md_os_local_time_from_universal(MD_DateTime* date_time)
{
// rjf: universal MD_DateTime -> local time_t
tm universal_tm = md_os_lnx_tm_from_date_time(*date_time);
universal_tm.tm_isdst = -1;
time_t universal_t = timegm(&universal_tm);
tm local_tm = {0};
localtime_r(&universal_t, &local_tm);
// rjf: local tm -> MD_DateTime
MD_DateTime result = md_os_lnx_date_time_from_tm(local_tm, 0);
return result;
}
void
md_os_sleep_milliseconds(MD_U32 msec) {
usleep(msec * thousand(1));
}
////////////////////////////////
//~ rjf: @md_os_hooks Child Processes (Implemented Per-OS)
MD_OS_Handle
md_os_process_launch(MD_OS_ProcessLaunchParams* params)
{
NotImplemented;
}
MD_B32
md_os_process_join(MD_OS_Handle handle, MD_U64 endt_us)
{
NotImplemented;
}
void
md_os_process_detach(MD_OS_Handle handle)
{
NotImplemented;
}
////////////////////////////////
//~ rjf: @md_os_hooks Threads (Implemented Per-OS)
MD_OS_Handle
md_os_thread_launch(MD_OS_ThreadFunctionType* func, void* ptr, void* params)
{
MD_OS_LNX_Entity* entity = md_os_lnx_entity_alloc(MD_OS_LNX_EntityKind_Thread);
entity->thread.func = func;
entity->thread.ptr = ptr;
{
pthread_attr_t attr;
pthread_attr_init(&attr);
int pthread_result = pthread_create(&entity->thread.handle, &attr, md_os_lnx_thread_entry_point, entity);
pthread_attr_destroy(&attr);
if (pthread_result == -1)
{
md_os_lnx_entity_release(entity);
entity = 0;
}
}
MD_OS_Handle handle = {(MD_U64)entity};
return handle;
}
MD_B32
md_os_thread_join(MD_OS_Handle handle, MD_U64 endt_us)
{
if (md_os_handle_match(handle, md_os_handle_zero())) { return 0; }
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity*)handle.u64[0];
int join_result = pthread_join(entity->thread.handle, 0);
MD_B32 result = (join_result == 0);
md_os_lnx_entity_release(entity);
return result;
}
void
md_os_thread_detach(MD_OS_Handle handle) {
if(md_os_handle_match(handle, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity*)handle.u64[0];
md_os_lnx_entity_release(entity);
}
////////////////////////////////
//~ rjf: @md_os_hooks Synchronization Primitives (Implemented Per-OS)
//- rjf: mutexes
MD_OS_Handle
md_os_mutex_alloc(void)
{
MD_OS_LNX_Entity *entity = md_os_lnx_entity_alloc(MD_OS_LNX_EntityKind_Mutex);
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
int init_result = pthread_mutex_init(&entity->mutex_handle, &attr);
pthread_mutexattr_destroy(&attr);
if (init_result == -1) {
md_os_lnx_entity_release(entity);
entity = 0;
}
MD_OS_Handle handle = {(MD_U64)entity};
return handle;
}
void
md_os_mutex_release(MD_OS_Handle mutex) {
if (md_os_handle_match(mutex, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity *)mutex.u64[0];
pthread_mutex_destroy(&entity->mutex_handle);
md_os_lnx_entity_release(entity);
}
void
md_os_mutex_take(MD_OS_Handle mutex) {
if (md_os_handle_match(mutex, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity *)mutex.u64[0];
pthread_mutex_lock(&entity->mutex_handle);
}
void
md_os_mutex_drop(MD_OS_Handle mutex) {
if (md_os_handle_match(mutex, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity *)mutex.u64[0];
pthread_mutex_unlock(&entity->mutex_handle);
}
//- rjf: reader/writer mutexes
MD_OS_Handle
md_os_rw_mutex_alloc(void)
{
MD_OS_LNX_Entity* entity = md_os_lnx_entity_alloc(MD_OS_LNX_EntityKind_RWMutex);
int init_result = pthread_rwlock_init(&entity->rwmutex_handle, 0);
if (init_result == -1) {
md_os_lnx_entity_release(entity);
entity = 0;
}
MD_OS_Handle handle = {(MD_U64)entity};
return handle;
}
void
md_os_rw_mutex_release(MD_OS_Handle rw_mutex) {
if (md_os_handle_match(rw_mutex, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity*)rw_mutex.u64[0];
pthread_rwlock_destroy(&entity->rwmutex_handle);
md_os_lnx_entity_release(entity);
}
void
md_os_rw_mutex_take_r(MD_OS_Handle rw_mutex) {
if (md_os_handle_match(rw_mutex, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity*)rw_mutex.u64[0];
pthread_rwlock_rdlock(&entity->rwmutex_handle);
}
void
md_os_rw_mutex_drop_r(MD_OS_Handle rw_mutex) {
if (md_os_handle_match(rw_mutex, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity*)rw_mutex.u64[0];
pthread_rwlock_unlock(&entity->rwmutex_handle);
}
void
md_os_rw_mutex_take_w(MD_OS_Handle rw_mutex) {
if (md_os_handle_match(rw_mutex, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity*)rw_mutex.u64[0];
pthread_rwlock_wrlock(&entity->rwmutex_handle);
}
void
md_os_rw_mutex_drop_w(MD_OS_Handle rw_mutex) {
if (md_os_handle_match(rw_mutex, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity*)rw_mutex.u64[0];
pthread_rwlock_unlock(&entity->rwmutex_handle);
}
//- rjf: condition variables
MD_OS_Handle
md_os_condition_variable_alloc(void)
{
MD_OS_LNX_Entity* entity = md_os_lnx_entity_alloc(MD_OS_LNX_EntityKind_ConditionVariable);
int init_result = pthread_cond_init(&entity->cv.cond_handle, 0);
if (init_result == -1) {
md_os_lnx_entity_release(entity);
entity = 0;
}
int init2_result = 0;
if (entity) {
init2_result = pthread_mutex_init(&entity->cv.rwlock_mutex_handle, 0);
}
if (init2_result == -1) {
pthread_cond_destroy(&entity->cv.cond_handle);
md_os_lnx_entity_release(entity);
entity = 0;
}
MD_OS_Handle handle = {(MD_U64)entity};
return handle;
}
void
md_os_condition_variable_release(MD_OS_Handle cv) {
if (md_os_handle_match(cv, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* entity = (MD_OS_LNX_Entity *)cv.u64[0];
pthread_cond_destroy(&entity->cv.cond_handle);
pthread_mutex_destroy(&entity->cv.rwlock_mutex_handle);
md_os_lnx_entity_release(entity);
}
MD_B32
md_os_condition_variable_wait(MD_OS_Handle cv, MD_OS_Handle mutex, MD_U64 endt_us)
{
if (md_os_handle_match(cv, md_os_handle_zero())) { return 0; }
if (md_os_handle_match(mutex, md_os_handle_zero())) { return 0; }
MD_OS_LNX_Entity* cv_entity = (MD_OS_LNX_Entity*)cv.u64[0];
MD_OS_LNX_Entity* mutex_entity = (MD_OS_LNX_Entity*)mutex.u64[0];
struct timespec endt_timespec;
endt_timespec.tv_sec = endt_us / md_million(1);
endt_timespec.tv_nsec = thousand(1) * (endt_us - (endt_us / md_million(1)) * md_million(1));
int wait_result = pthread_cond_timedwait(&cv_entity->cv.cond_handle, &mutex_entity->mutex_handle, &endt_timespec);
MD_B32 result = (wait_result != ETIMEDOUT);
return result;
}
MD_B32
md_os_condition_variable_wait_rw_r(MD_OS_Handle cv, MD_OS_Handle mutex_rw, MD_U64 endt_us)
{
// TODO(rjf): because pthread does not supply cv/rw natively, I had to hack
// this together, but this would probably just be a lot better if we just
// implemented the primitives ourselves with e.g. futexes
//
if(md_os_handle_match(cv, md_os_handle_zero())) { return 0; }
if(md_os_handle_match(mutex_rw, md_os_handle_zero())) { return 0; }
MD_OS_LNX_Entity* cv_entity = (MD_OS_LNX_Entity*)cv.u64[0];
MD_OS_LNX_Entity* rw_mutex_entity = (MD_OS_LNX_Entity*)mutex_rw.u64[0];
struct timespec endt_timespec;
endt_timespec.tv_sec = endt_us / md_million(1);
endt_timespec.tv_nsec = thousand(1) * (endt_us - (endt_us / md_million(1)) * md_million(1));
MD_B32 result = 0;
for(;;)
{
pthread_mutex_lock(&cv_entity->cv.rwlock_mutex_handle);
int wait_result = pthread_cond_timedwait(&cv_entity->cv.cond_handle, &cv_entity->cv.rwlock_mutex_handle, &endt_timespec);
if (wait_result != ETIMEDOUT)
{
pthread_rwlock_rdlock(&rw_mutex_entity->rwmutex_handle);
pthread_mutex_unlock(&cv_entity->cv.rwlock_mutex_handle);
result = 1;
break;
}
pthread_mutex_unlock(&cv_entity->cv.rwlock_mutex_handle);
if (wait_result == ETIMEDOUT)
{
break;
}
}
return result;
}
MD_B32
md_os_condition_variable_wait_rw_w(MD_OS_Handle cv, MD_OS_Handle mutex_rw, MD_U64 endt_us)
{
// TODO(rjf): because pthread does not supply cv/rw natively, I had to hack
// this together, but this would probably just be a lot better if we just
// implemented the primitives ourselves with e.g. futexes
//
if (md_os_handle_match(cv, md_os_handle_zero())) { return 0; }
if (md_os_handle_match(mutex_rw, md_os_handle_zero())) { return 0; }
MD_OS_LNX_Entity* cv_entity = (MD_OS_LNX_Entity*)cv.u64[0];
MD_OS_LNX_Entity* rw_mutex_entity = (MD_OS_LNX_Entity*)mutex_rw.u64[0];
struct timespec endt_timespec;
endt_timespec.tv_sec = endt_us / md_million(1);
endt_timespec.tv_nsec = thousand(1) * (endt_us - (endt_us / md_million(1)) * md_million(1));
MD_B32 result = 0;
for(;;)
{
pthread_mutex_lock(&cv_entity->cv.rwlock_mutex_handle);
int wait_result = pthread_cond_timedwait(&cv_entity->cv.cond_handle, &cv_entity->cv.rwlock_mutex_handle, &endt_timespec);
if (wait_result != ETIMEDOUT)
{
pthread_rwlock_wrlock(&rw_mutex_entity->rwmutex_handle);
pthread_mutex_unlock(&cv_entity->cv.rwlock_mutex_handle);
result = 1;
break;
}
pthread_mutex_unlock(&cv_entity->cv.rwlock_mutex_handle);
if (wait_result == ETIMEDOUT)
{
break;
}
}
return result;
}
void
md_os_condition_variable_signal(MD_OS_Handle cv) {
if (md_os_handle_match(cv, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* cv_entity = (MD_OS_LNX_Entity *)cv.u64[0];
pthread_cond_signal(&cv_entity->cv.cond_handle);
}
void
md_os_condition_variable_broadcast(MD_OS_Handle cv) {
if (md_os_handle_match(cv, md_os_handle_zero())) { return; }
MD_OS_LNX_Entity* cv_entity = (MD_OS_LNX_Entity *)cv.u64[0];
pthread_cond_broadcast(&cv_entity->cv.cond_handle);
}
//- rjf: cross-process semaphores
MD_OS_Handle
md_os_semaphore_alloc(MD_U32 initial_count, MD_U32 max_count, MD_String8 name)
{
NotImplemented;
}
void
md_os_semaphore_release(MD_OS_Handle semaphore)
{
NotImplemented;
}
MD_OS_Handle
md_os_semaphore_open(MD_String8 name)
{
NotImplemented;
}
void
md_os_semaphore_close(MD_OS_Handle semaphore)
{
NotImplemented;
}
MD_B32
md_os_semaphore_take(MD_OS_Handle semaphore, MD_U64 endt_us)
{
NotImplemented;
}
void
md_os_semaphore_drop(MD_OS_Handle semaphore)
{
NotImplemented;
}
////////////////////////////////
//~ rjf: @md_os_hooks Dynamically-Loaded Libraries (Implemented Per-OS)
MD_OS_Handle
md_os_library_open(MD_String8 path) {
MD_TempArena scratch = md_scratch_begin(0, 0);
char* path_cstr = (char *)md_push_str8_copy(scratch.arena, path).str;
void* so = dlopen(path_cstr, RTLD_LAZY);
MD_OS_Handle lib = { (MD_U64)so };
scratch_end(scratch);
return lib;
}
MD_VoidProc*
md_os_library_load_proc(MD_OS_Handle lib, MD_String8 name) {
MD_TempArena scratch = md_scratch_begin(0, 0);
void* so = (void*)lib.u64;
char* name_cstr = (char*)md_push_str8_copy(scratch.arena, name).str;
MD_VoidProc* proc = (MD_VoidProc*)dlsym(so, name_cstr);
scratch_end(scratch);
return proc;
}
void
md_os_library_close(MD_OS_Handle lib) {
void* so = (void*)lib.u64;
dlclose(so);
}
////////////////////////////////
//~ rjf: @md_os_hooks Safe Calls (Implemented Per-OS)
void
md_os_safe_call(MD_OS_ThreadFunctionType* func, MD_OS_ThreadFunctionType* fail_handler, void* ptr)
{
// rjf: push handler to chain
MD_OS_LNX_SafeCallChain chain = {0};
SLLStackPush(md_os_lnx_safe_call_chain, &chain);
chain.fail_handler = fail_handler;
chain.ptr = ptr;
// rjf: set up sig handler info
struct sigaction new_act = {0};
new_act.sa_handler = md_os_lnx_safe_call_sig_handler;
int signals_to_handle[] = {
SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP,
};
struct sigaction og_act[md_array_count(signals_to_handle)] = {0};
// rjf: attach handler info for all signals
for(MD_U32 i = 0; i < md_array_count(signals_to_handle); i += 1) {
sigaction(signals_to_handle[i], &new_act, &og_act[i]);
}
// rjf: call function
func(ptr);
// rjf: reset handler info for all signals
for (MD_U32 i = 0; i < md_array_count(signals_to_handle); i += 1) {
sigaction(signals_to_handle[i], &og_act[i], 0);
}
}
////////////////////////////////
//~ rjf: @md_os_hooks GUIDs (Implemented Per-OS)
MD_OS_Guid
md_os_make_guid(void)
{
MD_U8 random_bytes[16] = {0};
md_static_assert(sizeof(random_bytes) == sizeof(MD_OS_Guid), md_os_lnx_guid_size_check);
getrandom(random_bytes, sizeof(random_bytes), 0);
MD_OS_Guid guid = {0};
md_memory_copy(&guid, random_bytes, sizeof(random_bytes));
guid.data3 &= 0x0fff;
guid.data3 |= (4 << 12);
guid.data4[0] &= 0x3f;
guid.data4[0] |= 0x80;
return guid;
}
////////////////////////////////
//~ rjf: @md_os_hooks Entry Points (Implemented Per-OS)
#if MD_BUILD_ENTRY_DEFINING_UNIT || 1
int
main(int argc, char **argv)
{
//- rjf: set up OS layer
{
//- rjf: get statically-allocated system/process info
{
MD_OS_SystemInfo *info = &md_os_lnx_state.system_info;
info->logical_processor_count = (MD_U32)get_nprocs();
info->page_size = (MD_U64)getpagesize();
info->large_page_size = MD_MB(2);
info->allocation_granularity = info->page_size;
}
{
MD_OS_ProcessInfo *info = &md_os_lnx_state.process_info;
info->pid = (MD_U32)getpid();
}
//- rjf: set up thread context
md_local_persist MD_TCTX md_tctx;
md_tctx_init_and_equip(&md_tctx);
//- rjf: set up dynamically allocated state
md_os_lnx_state.arena = md_arena_alloc();
md_os_lnx_state.entity_arena = md_arena_alloc();
pthread_mutex_init(&md_os_lnx_state.entity_mutex, 0);
//- rjf: grab dynamically allocated system info
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_OS_SystemInfo* info = &md_os_lnx_state.system_info;
// rjf: get machine name
MD_B32 got_final_result = 0;
MD_U8* buffer = 0;
int size = 0;
for (MD_S64 cap = 4096, r = 0; r < 4; cap *= 2, r += 1)
{
scratch_end(scratch);
buffer = md_push_array__no_zero(scratch.arena, MD_U8, cap);
size = gethostname((char*)buffer, cap);
if (size < cap)
{
got_final_result = 1;
break;
}
}
// rjf: save name to info
if (got_final_result && size > 0)
{
info->machine_name.size = size;
info->machine_name.str = md_push_array__no_zero(md_os_lnx_state.arena, MD_U8, info->machine_name.size + 1);
MemoryCopy(info->machine_name.str, buffer, info->machine_name.size);
info->machine_name.str[info->machine_name.size] = 0;
}
scratch_end(scratch);
}
//- rjf: grab dynamically allocated process info
{
MD_TempArena scratch = md_scratch_begin(0, 0);
MD_OS_ProcessInfo* info = &md_os_lnx_state.process_info;
// rjf: grab binary path
{
// rjf: get self string
MD_B32 got_final_result = 0;
MD_U8* buffer = 0;
int size = 0;
for (MD_S64 cap = PATH_MAX, r = 0; r < 4; cap *= 2, r += 1)
{
scratch_end(scratch);
buffer = md_push_array__no_zero(scratch.arena, MD_U8, cap);
size = readlink("/proc/self/exe", (char*)buffer, cap);
if (size < cap)
{
got_final_result = 1;
break;
}
}
// rjf: save
if (got_final_result && size > 0)
{
MD_String8 full_name = md_str8(buffer, size);
MD_String8 name_chopped = md_str8_chop_last_slash(full_name);
info->binary_path = md_push_str8_copy(md_os_lnx_state.arena, name_chopped);
}
}
// rjf: grab initial directory
{
info->initial_path = md_os_get_current_path(md_os_lnx_state.arena);
}
// rjf: grab home directory
{
char *home = getenv("HOME");
info->user_program_data_path = md_str8_cstring(home);
}
scratch_end(scratch);
}
}
//- rjf: call into "real" entry point
md_main_thread_base_entry_point(md_entry_point, argv, (MD_U64)argc);
}
// MD_BUILD_ENTRY_POINT
#endif