Files
ed 3e064f6443 progress on sanity_tests.c, adding back md_str8_styllize
Eventually a heavy reduction of base & OS should be provided via libgen or done here to reduce the overall surface area of the library for the user.
Base and os were dragged from raddbg and the original metadesk didn't need as many definitions.
2025-02-13 09:53:46 -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 / md_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 * md_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 = md_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 = md_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 = md_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};
sll_stack_push(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);
memory_copy(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