mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-29 02:40:05 +00:00
+68
-3
@@ -18,6 +18,7 @@ enum TargetOsKind : u16 {
|
||||
TargetOs_essence,
|
||||
TargetOs_freebsd,
|
||||
TargetOs_openbsd,
|
||||
TargetOs_haiku,
|
||||
|
||||
TargetOs_wasi,
|
||||
TargetOs_js,
|
||||
@@ -78,6 +79,7 @@ gb_global String target_os_names[TargetOs_COUNT] = {
|
||||
str_lit("essence"),
|
||||
str_lit("freebsd"),
|
||||
str_lit("openbsd"),
|
||||
str_lit("haiku"),
|
||||
|
||||
str_lit("wasi"),
|
||||
str_lit("js"),
|
||||
@@ -542,6 +544,13 @@ gb_global TargetMetrics target_openbsd_amd64 = {
|
||||
str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
|
||||
};
|
||||
|
||||
gb_global TargetMetrics target_haiku_amd64 = {
|
||||
TargetOs_haiku,
|
||||
TargetArch_amd64,
|
||||
8, 8, 8, 16,
|
||||
str_lit("x86_64-unknown-haiku"),
|
||||
};
|
||||
|
||||
gb_global TargetMetrics target_essence_amd64 = {
|
||||
TargetOs_essence,
|
||||
TargetArch_amd64,
|
||||
@@ -641,6 +650,7 @@ gb_global NamedTargetMetrics named_targets[] = {
|
||||
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
|
||||
|
||||
{ str_lit("openbsd_amd64"), &target_openbsd_amd64 },
|
||||
{ str_lit("haiku_amd64"), &target_haiku_amd64 },
|
||||
|
||||
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
|
||||
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
|
||||
@@ -872,6 +882,58 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
return path;
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
|
||||
#include <FindDirectory.h>
|
||||
|
||||
gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_);
|
||||
|
||||
gb_internal String internal_odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
isize len, i;
|
||||
u8 *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
auto path_buf = array_make<char>(heap_allocator(), 300);
|
||||
defer (array_free(&path_buf));
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
u32 sz = path_buf.count;
|
||||
int res = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, nullptr, &path_buf[0], sz);
|
||||
if(res == B_OK) {
|
||||
len = sz;
|
||||
break;
|
||||
} else {
|
||||
array_resize(&path_buf, sz + 1);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_lock(&string_buffer_mutex);
|
||||
defer (mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
text = gb_alloc_array(permanent_allocator(), u8, len + 1);
|
||||
gb_memmove(text, &path_buf[0], len);
|
||||
|
||||
path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
|
||||
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
@@ -888,6 +950,7 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
}
|
||||
|
||||
auto path_buf = array_make<char>(heap_allocator(), 300);
|
||||
defer (array_free(&path_buf));
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
@@ -920,9 +983,6 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
|
||||
// array_free(&path_buf);
|
||||
|
||||
return path;
|
||||
}
|
||||
#else
|
||||
@@ -1301,6 +1361,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
|
||||
metrics = &target_freebsd_amd64;
|
||||
#elif defined(GB_SYSTEM_OPENBSD)
|
||||
metrics = &target_openbsd_amd64;
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
metrics = &target_haiku_amd64;
|
||||
#elif defined(GB_CPU_ARM)
|
||||
metrics = &target_linux_arm64;
|
||||
#else
|
||||
@@ -1405,6 +1467,9 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
|
||||
case TargetOs_openbsd:
|
||||
bc->link_flags = str_lit("-arch x86-64 ");
|
||||
break;
|
||||
case TargetOs_haiku:
|
||||
bc->link_flags = str_lit("-arch x86-64 ");
|
||||
break;
|
||||
}
|
||||
} else if (bc->metrics.arch == TargetArch_i386) {
|
||||
switch (bc->metrics.os) {
|
||||
|
||||
@@ -4928,6 +4928,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
case TargetOs_essence:
|
||||
case TargetOs_freebsd:
|
||||
case TargetOs_openbsd:
|
||||
case TargetOs_haiku:
|
||||
switch (build_context.metrics.arch) {
|
||||
case TargetArch_i386:
|
||||
case TargetArch_amd64:
|
||||
|
||||
@@ -1010,6 +1010,7 @@ gb_internal void init_universal(void) {
|
||||
{"Linux", TargetOs_linux},
|
||||
{"Essence", TargetOs_essence},
|
||||
{"FreeBSD", TargetOs_freebsd},
|
||||
{"Haiku", TargetOs_haiku},
|
||||
{"OpenBSD", TargetOs_openbsd},
|
||||
{"WASI", TargetOs_wasi},
|
||||
{"JS", TargetOs_js},
|
||||
|
||||
+47
-2
@@ -83,6 +83,10 @@ extern "C" {
|
||||
#ifndef GB_SYSTEM_OPENBSD
|
||||
#define GB_SYSTEM_OPENBSD 1
|
||||
#endif
|
||||
#elif defined(__HAIKU__) || defined(__haiku__)
|
||||
#ifndef GB_SYSTEM_HAIKU
|
||||
#define GB_SYSTEM_HAIKU 1
|
||||
#endif
|
||||
#else
|
||||
#error This UNIX operating system is not supported
|
||||
#endif
|
||||
@@ -206,7 +210,7 @@ extern "C" {
|
||||
#endif
|
||||
#include <stdlib.h> // NOTE(bill): malloc on linux
|
||||
#include <sys/mman.h>
|
||||
#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||
#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__HAIKU__)
|
||||
#include <sys/sendfile.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
@@ -247,6 +251,13 @@ extern "C" {
|
||||
#include <pthread_np.h>
|
||||
#define lseek64 lseek
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_HAIKU)
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <kernel/OS.h>
|
||||
#define lseek64 lseek
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_UNIX)
|
||||
#include <semaphore.h>
|
||||
@@ -801,6 +812,13 @@ typedef struct gbAffinity {
|
||||
isize thread_count;
|
||||
isize threads_per_core;
|
||||
} gbAffinity;
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
typedef struct gbAffinity {
|
||||
b32 is_accurate;
|
||||
isize core_count;
|
||||
isize thread_count;
|
||||
isize threads_per_core;
|
||||
} gbAffinity;
|
||||
#else
|
||||
#error TODO(bill): Unknown system
|
||||
#endif
|
||||
@@ -2984,6 +3002,8 @@ gb_inline u32 gb_thread_current_id(void) {
|
||||
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
|
||||
#elif defined(GB_SYSTEM_LINUX)
|
||||
thread_id = gettid();
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
thread_id = find_thread(NULL);
|
||||
#else
|
||||
#error Unsupported architecture for gb_thread_current_id()
|
||||
#endif
|
||||
@@ -3184,7 +3204,9 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
|
||||
//info.affinity_tag = cast(integer_t)index;
|
||||
//result = thread_policy_set(thread, THREAD_AFFINITY_POLICY, cast(thread_policy_t)&info, THREAD_AFFINITY_POLICY_COUNT);
|
||||
|
||||
#if !defined(GB_SYSTEM_HAIKU)
|
||||
result = pthread_setaffinity_np(thread, sizeof(cpuset_t), &mn);
|
||||
#endif
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
@@ -3236,6 +3258,29 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
|
||||
GB_ASSERT(0 <= core && core < a->core_count);
|
||||
return a->threads_per_core;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
#include <unistd.h>
|
||||
|
||||
void gb_affinity_init(gbAffinity *a) {
|
||||
a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
a->threads_per_core = 1;
|
||||
a->is_accurate = a->core_count > 0;
|
||||
a->core_count = a->is_accurate ? a->core_count : 1;
|
||||
a->thread_count = a->core_count;
|
||||
}
|
||||
|
||||
void gb_affinity_destroy(gbAffinity *a) {
|
||||
gb_unused(a);
|
||||
}
|
||||
|
||||
b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
|
||||
GB_ASSERT(0 <= core && core < a->core_count);
|
||||
return a->threads_per_core;
|
||||
@@ -5457,7 +5502,7 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
|
||||
}
|
||||
}
|
||||
|
||||
gb_free(buf);
|
||||
gb_mfree(buf);
|
||||
close(new_fd);
|
||||
close(existing_fd);
|
||||
|
||||
|
||||
+2
-2
@@ -474,8 +474,8 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
|
||||
}
|
||||
|
||||
} else if (build_context.metrics.os != TargetOs_openbsd) {
|
||||
// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
|
||||
} else if (build_context.metrics.os != TargetOs_openbsd && build_context.metrics.os != TargetOs_haiku) {
|
||||
// OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
|
||||
link_settings = gb_string_appendc(link_settings, "-no-pie ");
|
||||
}
|
||||
|
||||
|
||||
@@ -2564,8 +2564,8 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
|
||||
switch (build_context.reloc_mode) {
|
||||
case RelocMode_Default:
|
||||
if (build_context.metrics.os == TargetOs_openbsd) {
|
||||
// Always use PIC for OpenBSD: it defaults to PIE
|
||||
if (build_context.metrics.os == TargetOs_openbsd || build_context.metrics.os == TargetOs_haiku) {
|
||||
// Always use PIC for OpenBSD and Haiku: they default to PIE
|
||||
reloc_mode = LLVMRelocPIC;
|
||||
}
|
||||
break;
|
||||
|
||||
+461
-461
@@ -1,461 +1,461 @@
|
||||
/*
|
||||
Path handling utilities.
|
||||
*/
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
gb_internal String remove_extension_from_path(String const &s) {
|
||||
if (s.len != 0 && s.text[s.len-1] == '.') {
|
||||
return s;
|
||||
}
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '.') {
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
gb_internal String remove_directory_from_path(String const &s) {
|
||||
isize len = 0;
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
return substring(s, s.len-len, s.len);
|
||||
}
|
||||
|
||||
|
||||
// NOTE(Mark Naughton): getcwd as String
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal String get_current_directory(void) {
|
||||
char cwd[256];
|
||||
getcwd(cwd, 256);
|
||||
|
||||
return make_string_c(cwd);
|
||||
}
|
||||
|
||||
#else
|
||||
gb_internal String get_current_directory(void) {
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
wchar_t cwd[256];
|
||||
GetCurrentDirectoryW(256, cwd);
|
||||
|
||||
String16 wstr = make_string16_c(cwd);
|
||||
|
||||
return string16_to_string(a, wstr);
|
||||
}
|
||||
#endif
|
||||
|
||||
gb_internal bool path_is_directory(String path);
|
||||
|
||||
gb_internal String directory_from_path(String const &s) {
|
||||
if (path_is_directory(s)) {
|
||||
return s;
|
||||
}
|
||||
|
||||
isize i = s.len-1;
|
||||
for (; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= 0) {
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
return substring(s, 0, 0);
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
String16 wstr = string_to_string16(a, path);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
i32 attribs = GetFileAttributesW(wstr.text);
|
||||
if (attribs < 0) return false;
|
||||
|
||||
return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
}
|
||||
|
||||
#else
|
||||
gb_internal bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
char *copy = cast(char *)copy_string(a, path).text;
|
||||
defer (gb_free(a, copy));
|
||||
|
||||
struct stat s;
|
||||
if (stat(copy, &s) == 0) {
|
||||
return (s.st_mode & S_IFDIR) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
gb_internal String path_to_full_path(gbAllocator a, String path) {
|
||||
gbAllocator ha = heap_allocator();
|
||||
char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
|
||||
defer (gb_free(ha, path_c));
|
||||
|
||||
char *fullpath = gb_path_get_full_name(a, path_c);
|
||||
String res = string_trim_whitespace(make_string_c(fullpath));
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
for (isize i = 0; i < res.len; i++) {
|
||||
if (res.text[i] == '\\') {
|
||||
res.text[i] = '/';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return copy_string(a, res);
|
||||
}
|
||||
|
||||
struct Path {
|
||||
String basename;
|
||||
String name;
|
||||
String ext;
|
||||
};
|
||||
|
||||
// NOTE(Jeroen): Naively turns a Path into a string.
|
||||
gb_internal String path_to_string(gbAllocator a, Path path) {
|
||||
if (path.basename.len + path.name.len + path.ext.len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
|
||||
isize len = path.basename.len + 1 + path.name.len + 1;
|
||||
if (path.ext.len > 0) {
|
||||
len += path.ext.len + 1;
|
||||
}
|
||||
|
||||
u8 *str = gb_alloc_array(a, u8, len);
|
||||
|
||||
isize i = 0;
|
||||
gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
|
||||
|
||||
gb_memmove(str+i, "/", 1); i += 1;
|
||||
|
||||
gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
|
||||
if (path.ext.len > 0) {
|
||||
gb_memmove(str+i, ".", 1); i += 1;
|
||||
gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
|
||||
}
|
||||
str[i] = 0;
|
||||
|
||||
String res = make_string(str, i);
|
||||
res = string_trim_whitespace(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
|
||||
gb_internal String path_to_full_path(gbAllocator a, Path path) {
|
||||
String temp = path_to_string(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), temp.text));
|
||||
|
||||
return path_to_full_path(a, temp);
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
|
||||
// and then breaks it into its components to make a Path.
|
||||
gb_internal Path path_from_string(gbAllocator a, String const &path) {
|
||||
Path res = {};
|
||||
|
||||
if (path.len == 0) return res;
|
||||
|
||||
String fullpath = path_to_full_path(a, path);
|
||||
defer (gb_free(heap_allocator(), fullpath.text));
|
||||
|
||||
res.basename = directory_from_path(fullpath);
|
||||
res.basename = copy_string(a, res.basename);
|
||||
|
||||
if (path_is_directory(fullpath)) {
|
||||
// It's a directory. We don't need to tinker with the name and extension.
|
||||
// It could have a superfluous trailing `/`. Remove it if so.
|
||||
if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
|
||||
res.basename.len--;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Note(Dragos): Is the copy_string required if it's a substring?
|
||||
isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
|
||||
res.name = substring(fullpath, name_start, fullpath.len);
|
||||
res.name = remove_extension_from_path(res.name);
|
||||
res.name = copy_string(a, res.name);
|
||||
|
||||
res.ext = path_extension(fullpath, false); // false says not to include the dot.
|
||||
res.ext = copy_string(a, res.ext);
|
||||
return res;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Takes a path String and returns the last path element.
|
||||
gb_internal String last_path_element(String const &path) {
|
||||
isize count = 0;
|
||||
u8 * start = (u8 *)(&path.text[path.len - 1]);
|
||||
for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
|
||||
count++;
|
||||
start--;
|
||||
}
|
||||
if (count > 0) {
|
||||
start++; // Advance past the `/` and return the substring.
|
||||
String res = make_string(start, count);
|
||||
return res;
|
||||
}
|
||||
// Must be a root path like `/` or `C:/`, return empty String.
|
||||
return STR_LIT("");
|
||||
}
|
||||
|
||||
gb_internal bool path_is_directory(Path path) {
|
||||
String path_string = path_to_full_path(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), path_string.text));
|
||||
|
||||
return path_is_directory(path_string);
|
||||
}
|
||||
|
||||
struct FileInfo {
|
||||
String name;
|
||||
String fullpath;
|
||||
i64 size;
|
||||
bool is_dir;
|
||||
};
|
||||
|
||||
enum ReadDirectoryError {
|
||||
ReadDirectory_None,
|
||||
|
||||
ReadDirectory_InvalidPath,
|
||||
ReadDirectory_NotExists,
|
||||
ReadDirectory_Permission,
|
||||
ReadDirectory_NotDir,
|
||||
ReadDirectory_Empty,
|
||||
ReadDirectory_Unknown,
|
||||
|
||||
ReadDirectory_COUNT,
|
||||
};
|
||||
|
||||
gb_internal i64 get_file_size(String path) {
|
||||
char *c_str = alloc_cstring(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), c_str));
|
||||
|
||||
gbFile f = {};
|
||||
gbFileError err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
if (err != gbFileError_None) {
|
||||
return -1;
|
||||
}
|
||||
return gb_file_size(&f);
|
||||
}
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
|
||||
while (path.len > 0) {
|
||||
Rune end = path[path.len-1];
|
||||
if (end == '/') {
|
||||
path.len -= 1;
|
||||
} else if (end == '\\') {
|
||||
path.len -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (path.len == 0) {
|
||||
return ReadDirectory_InvalidPath;
|
||||
}
|
||||
{
|
||||
char *c_str = alloc_cstring(temporary_allocator(), path);
|
||||
gbFile f = {};
|
||||
gbFileError file_err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
switch (file_err) {
|
||||
case gbFileError_Invalid: return ReadDirectory_InvalidPath;
|
||||
case gbFileError_NotExists: return ReadDirectory_NotExists;
|
||||
// case gbFileError_Permission: return ReadDirectory_Permission;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path_is_directory(path)) {
|
||||
return ReadDirectory_NotDir;
|
||||
}
|
||||
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
char *new_path = gb_alloc_array(a, char, path.len+3);
|
||||
defer (gb_free(a, new_path));
|
||||
|
||||
gb_memmove(new_path, path.text, path.len);
|
||||
gb_memmove(new_path+path.len, "/*", 2);
|
||||
new_path[path.len+2] = 0;
|
||||
|
||||
String np = make_string(cast(u8 *)new_path, path.len+2);
|
||||
String16 wstr = string_to_string16(a, np);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
WIN32_FIND_DATAW file_data = {};
|
||||
HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
|
||||
if (find_file == INVALID_HANDLE_VALUE) {
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
defer (FindClose(find_file));
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
do {
|
||||
wchar_t *filename_w = file_data.cFileName;
|
||||
u64 size = cast(u64)file_data.nFileSizeLow;
|
||||
size |= (cast(u64)file_data.nFileSizeHigh) << 32;
|
||||
String name = string16_to_string(a, make_string16_c(filename_w));
|
||||
if (name == "." || name == "..") {
|
||||
gb_free(a, name.text);
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = cast(i64)size;
|
||||
info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
array_add(fi, info);
|
||||
} while (FindNextFileW(find_file, &file_data));
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
char *c_path = alloc_cstring(a, path);
|
||||
defer (gb_free(a, c_path));
|
||||
|
||||
DIR *dir = opendir(c_path);
|
||||
if (!dir) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
return ReadDirectory_NotExists;
|
||||
case EACCES:
|
||||
return ReadDirectory_Permission;
|
||||
case ENOTDIR:
|
||||
return ReadDirectory_NotDir;
|
||||
default:
|
||||
// ENOMEM: out of memory
|
||||
// EMFILE: per-process limit on open fds reached
|
||||
// ENFILE: system-wide limit on total open files reached
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
GB_PANIC("unreachable");
|
||||
}
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
for (;;) {
|
||||
struct dirent *entry = readdir(dir);
|
||||
if (entry == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
String name = make_string_c(entry->d_name);
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
filepath.text[filepath.len] = 0;
|
||||
|
||||
|
||||
struct stat dir_stat = {};
|
||||
|
||||
if (stat((char *)filepath.text, &dir_stat)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(dir_stat.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i64 size = dir_stat.st_size;
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = size;
|
||||
array_add(fi, info);
|
||||
}
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
#error Implement read_directory
|
||||
#endif
|
||||
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal bool write_directory(String path) {
|
||||
char const *pathname = (char *) path.text;
|
||||
|
||||
if (access(pathname, W_OK) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
gb_internal bool write_directory(String path) {
|
||||
String16 wstr = string_to_string16(heap_allocator(), path);
|
||||
LPCWSTR wdirectory_name = wstr.text;
|
||||
|
||||
HANDLE directory = CreateFileW(wdirectory_name,
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
if (directory == INVALID_HANDLE_VALUE) {
|
||||
DWORD error_code = GetLastError();
|
||||
if (error_code == ERROR_ACCESS_DENIED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(directory);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
Path handling utilities.
|
||||
*/
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
gb_internal String remove_extension_from_path(String const &s) {
|
||||
if (s.len != 0 && s.text[s.len-1] == '.') {
|
||||
return s;
|
||||
}
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '.') {
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
gb_internal String remove_directory_from_path(String const &s) {
|
||||
isize len = 0;
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
return substring(s, s.len-len, s.len);
|
||||
}
|
||||
|
||||
|
||||
// NOTE(Mark Naughton): getcwd as String
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal String get_current_directory(void) {
|
||||
char cwd[256];
|
||||
getcwd(cwd, 256);
|
||||
|
||||
return make_string_c(cwd);
|
||||
}
|
||||
|
||||
#else
|
||||
gb_internal String get_current_directory(void) {
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
wchar_t cwd[256];
|
||||
GetCurrentDirectoryW(256, cwd);
|
||||
|
||||
String16 wstr = make_string16_c(cwd);
|
||||
|
||||
return string16_to_string(a, wstr);
|
||||
}
|
||||
#endif
|
||||
|
||||
gb_internal bool path_is_directory(String path);
|
||||
|
||||
gb_internal String directory_from_path(String const &s) {
|
||||
if (path_is_directory(s)) {
|
||||
return s;
|
||||
}
|
||||
|
||||
isize i = s.len-1;
|
||||
for (; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= 0) {
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
return substring(s, 0, 0);
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
String16 wstr = string_to_string16(a, path);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
i32 attribs = GetFileAttributesW(wstr.text);
|
||||
if (attribs < 0) return false;
|
||||
|
||||
return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
}
|
||||
|
||||
#else
|
||||
gb_internal bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
char *copy = cast(char *)copy_string(a, path).text;
|
||||
defer (gb_free(a, copy));
|
||||
|
||||
struct stat s;
|
||||
if (stat(copy, &s) == 0) {
|
||||
return (s.st_mode & S_IFDIR) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
gb_internal String path_to_full_path(gbAllocator a, String path) {
|
||||
gbAllocator ha = heap_allocator();
|
||||
char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
|
||||
defer (gb_free(ha, path_c));
|
||||
|
||||
char *fullpath = gb_path_get_full_name(a, path_c);
|
||||
String res = string_trim_whitespace(make_string_c(fullpath));
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
for (isize i = 0; i < res.len; i++) {
|
||||
if (res.text[i] == '\\') {
|
||||
res.text[i] = '/';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return copy_string(a, res);
|
||||
}
|
||||
|
||||
struct Path {
|
||||
String basename;
|
||||
String name;
|
||||
String ext;
|
||||
};
|
||||
|
||||
// NOTE(Jeroen): Naively turns a Path into a string.
|
||||
gb_internal String path_to_string(gbAllocator a, Path path) {
|
||||
if (path.basename.len + path.name.len + path.ext.len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
|
||||
isize len = path.basename.len + 1 + path.name.len + 1;
|
||||
if (path.ext.len > 0) {
|
||||
len += path.ext.len + 1;
|
||||
}
|
||||
|
||||
u8 *str = gb_alloc_array(a, u8, len);
|
||||
|
||||
isize i = 0;
|
||||
gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
|
||||
|
||||
gb_memmove(str+i, "/", 1); i += 1;
|
||||
|
||||
gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
|
||||
if (path.ext.len > 0) {
|
||||
gb_memmove(str+i, ".", 1); i += 1;
|
||||
gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
|
||||
}
|
||||
str[i] = 0;
|
||||
|
||||
String res = make_string(str, i);
|
||||
res = string_trim_whitespace(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
|
||||
gb_internal String path_to_full_path(gbAllocator a, Path path) {
|
||||
String temp = path_to_string(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), temp.text));
|
||||
|
||||
return path_to_full_path(a, temp);
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
|
||||
// and then breaks it into its components to make a Path.
|
||||
gb_internal Path path_from_string(gbAllocator a, String const &path) {
|
||||
Path res = {};
|
||||
|
||||
if (path.len == 0) return res;
|
||||
|
||||
String fullpath = path_to_full_path(a, path);
|
||||
defer (gb_free(heap_allocator(), fullpath.text));
|
||||
|
||||
res.basename = directory_from_path(fullpath);
|
||||
res.basename = copy_string(a, res.basename);
|
||||
|
||||
if (path_is_directory(fullpath)) {
|
||||
// It's a directory. We don't need to tinker with the name and extension.
|
||||
// It could have a superfluous trailing `/`. Remove it if so.
|
||||
if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
|
||||
res.basename.len--;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Note(Dragos): Is the copy_string required if it's a substring?
|
||||
isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
|
||||
res.name = substring(fullpath, name_start, fullpath.len);
|
||||
res.name = remove_extension_from_path(res.name);
|
||||
res.name = copy_string(a, res.name);
|
||||
|
||||
res.ext = path_extension(fullpath, false); // false says not to include the dot.
|
||||
res.ext = copy_string(a, res.ext);
|
||||
return res;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Takes a path String and returns the last path element.
|
||||
gb_internal String last_path_element(String const &path) {
|
||||
isize count = 0;
|
||||
u8 * start = (u8 *)(&path.text[path.len - 1]);
|
||||
for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
|
||||
count++;
|
||||
start--;
|
||||
}
|
||||
if (count > 0) {
|
||||
start++; // Advance past the `/` and return the substring.
|
||||
String res = make_string(start, count);
|
||||
return res;
|
||||
}
|
||||
// Must be a root path like `/` or `C:/`, return empty String.
|
||||
return STR_LIT("");
|
||||
}
|
||||
|
||||
gb_internal bool path_is_directory(Path path) {
|
||||
String path_string = path_to_full_path(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), path_string.text));
|
||||
|
||||
return path_is_directory(path_string);
|
||||
}
|
||||
|
||||
struct FileInfo {
|
||||
String name;
|
||||
String fullpath;
|
||||
i64 size;
|
||||
bool is_dir;
|
||||
};
|
||||
|
||||
enum ReadDirectoryError {
|
||||
ReadDirectory_None,
|
||||
|
||||
ReadDirectory_InvalidPath,
|
||||
ReadDirectory_NotExists,
|
||||
ReadDirectory_Permission,
|
||||
ReadDirectory_NotDir,
|
||||
ReadDirectory_Empty,
|
||||
ReadDirectory_Unknown,
|
||||
|
||||
ReadDirectory_COUNT,
|
||||
};
|
||||
|
||||
gb_internal i64 get_file_size(String path) {
|
||||
char *c_str = alloc_cstring(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), c_str));
|
||||
|
||||
gbFile f = {};
|
||||
gbFileError err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
if (err != gbFileError_None) {
|
||||
return -1;
|
||||
}
|
||||
return gb_file_size(&f);
|
||||
}
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
|
||||
while (path.len > 0) {
|
||||
Rune end = path[path.len-1];
|
||||
if (end == '/') {
|
||||
path.len -= 1;
|
||||
} else if (end == '\\') {
|
||||
path.len -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (path.len == 0) {
|
||||
return ReadDirectory_InvalidPath;
|
||||
}
|
||||
{
|
||||
char *c_str = alloc_cstring(temporary_allocator(), path);
|
||||
gbFile f = {};
|
||||
gbFileError file_err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
switch (file_err) {
|
||||
case gbFileError_Invalid: return ReadDirectory_InvalidPath;
|
||||
case gbFileError_NotExists: return ReadDirectory_NotExists;
|
||||
// case gbFileError_Permission: return ReadDirectory_Permission;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path_is_directory(path)) {
|
||||
return ReadDirectory_NotDir;
|
||||
}
|
||||
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
char *new_path = gb_alloc_array(a, char, path.len+3);
|
||||
defer (gb_free(a, new_path));
|
||||
|
||||
gb_memmove(new_path, path.text, path.len);
|
||||
gb_memmove(new_path+path.len, "/*", 2);
|
||||
new_path[path.len+2] = 0;
|
||||
|
||||
String np = make_string(cast(u8 *)new_path, path.len+2);
|
||||
String16 wstr = string_to_string16(a, np);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
WIN32_FIND_DATAW file_data = {};
|
||||
HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
|
||||
if (find_file == INVALID_HANDLE_VALUE) {
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
defer (FindClose(find_file));
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
do {
|
||||
wchar_t *filename_w = file_data.cFileName;
|
||||
u64 size = cast(u64)file_data.nFileSizeLow;
|
||||
size |= (cast(u64)file_data.nFileSizeHigh) << 32;
|
||||
String name = string16_to_string(a, make_string16_c(filename_w));
|
||||
if (name == "." || name == "..") {
|
||||
gb_free(a, name.text);
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = cast(i64)size;
|
||||
info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
array_add(fi, info);
|
||||
} while (FindNextFileW(find_file, &file_data));
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_HAIKU)
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
char *c_path = alloc_cstring(a, path);
|
||||
defer (gb_free(a, c_path));
|
||||
|
||||
DIR *dir = opendir(c_path);
|
||||
if (!dir) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
return ReadDirectory_NotExists;
|
||||
case EACCES:
|
||||
return ReadDirectory_Permission;
|
||||
case ENOTDIR:
|
||||
return ReadDirectory_NotDir;
|
||||
default:
|
||||
// ENOMEM: out of memory
|
||||
// EMFILE: per-process limit on open fds reached
|
||||
// ENFILE: system-wide limit on total open files reached
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
GB_PANIC("unreachable");
|
||||
}
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
for (;;) {
|
||||
struct dirent *entry = readdir(dir);
|
||||
if (entry == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
String name = make_string_c(entry->d_name);
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
filepath.text[filepath.len] = 0;
|
||||
|
||||
|
||||
struct stat dir_stat = {};
|
||||
|
||||
if (stat((char *)filepath.text, &dir_stat)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(dir_stat.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i64 size = dir_stat.st_size;
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = size;
|
||||
array_add(fi, info);
|
||||
}
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
#error Implement read_directory
|
||||
#endif
|
||||
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal bool write_directory(String path) {
|
||||
char const *pathname = (char *) path.text;
|
||||
|
||||
if (access(pathname, W_OK) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
gb_internal bool write_directory(String path) {
|
||||
String16 wstr = string_to_string16(heap_allocator(), path);
|
||||
LPCWSTR wdirectory_name = wstr.text;
|
||||
|
||||
HANDLE directory = CreateFileW(wdirectory_name,
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
if (directory == INVALID_HANDLE_VALUE) {
|
||||
DWORD error_code = GetLastError();
|
||||
if (error_code == ERROR_ACCESS_DENIED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(directory);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
+173
-1
@@ -492,6 +492,8 @@ gb_internal u32 thread_current_id(void) {
|
||||
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
|
||||
#elif defined(GB_SYSTEM_LINUX)
|
||||
thread_id = gettid();
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
thread_id = find_thread(NULL);
|
||||
#else
|
||||
#error Unsupported architecture for thread_current_id()
|
||||
#endif
|
||||
@@ -831,8 +833,178 @@ gb_internal void futex_wait(Futex *f, Footex val) {
|
||||
WaitOnAddress(f, (void *)&val, sizeof(val), INFINITE);
|
||||
} while (f->load() == val);
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
|
||||
// Futex implementation taken from https://tavianator.com/2023/futex.html
|
||||
|
||||
#include <pthread.h>
|
||||
#include <atomic>
|
||||
|
||||
struct _Spinlock {
|
||||
std::atomic_flag state;
|
||||
|
||||
void init() {
|
||||
state.clear();
|
||||
}
|
||||
|
||||
void lock() {
|
||||
while (state.test_and_set(std::memory_order_acquire)) {
|
||||
#if defined(GB_CPU_X86)
|
||||
_mm_pause();
|
||||
#else
|
||||
(void)0; // spin...
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
state.clear(std::memory_order_release);
|
||||
}
|
||||
};
|
||||
|
||||
struct Futex_Waitq;
|
||||
|
||||
struct Futex_Waiter {
|
||||
_Spinlock lock;
|
||||
pthread_t thread;
|
||||
Futex *futex;
|
||||
Futex_Waitq *waitq;
|
||||
Futex_Waiter *prev, *next;
|
||||
};
|
||||
|
||||
struct Futex_Waitq {
|
||||
_Spinlock lock;
|
||||
Futex_Waiter list;
|
||||
|
||||
void init() {
|
||||
auto head = &list;
|
||||
head->prev = head->next = head;
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: This approach may scale badly in the future,
|
||||
// possible solution - hash map (leads to deadlocks now).
|
||||
|
||||
Futex_Waitq g_waitq = {
|
||||
.lock = ATOMIC_FLAG_INIT,
|
||||
.list = {
|
||||
.prev = &g_waitq.list,
|
||||
.next = &g_waitq.list,
|
||||
},
|
||||
};
|
||||
|
||||
Futex_Waitq *get_waitq(Futex *f) {
|
||||
// Future hash map method...
|
||||
return &g_waitq;
|
||||
}
|
||||
|
||||
void futex_signal(Futex *f) {
|
||||
auto waitq = get_waitq(f);
|
||||
|
||||
waitq->lock.lock();
|
||||
|
||||
auto head = &waitq->list;
|
||||
for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
|
||||
if (waiter->futex != f) {
|
||||
continue;
|
||||
}
|
||||
waitq->lock.unlock();
|
||||
pthread_kill(waiter->thread, SIGCONT);
|
||||
return;
|
||||
}
|
||||
|
||||
waitq->lock.unlock();
|
||||
}
|
||||
|
||||
void futex_broadcast(Futex *f) {
|
||||
auto waitq = get_waitq(f);
|
||||
|
||||
waitq->lock.lock();
|
||||
|
||||
auto head = &waitq->list;
|
||||
for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
|
||||
if (waiter->futex != f) {
|
||||
continue;
|
||||
}
|
||||
if (waiter->next == head) {
|
||||
waitq->lock.unlock();
|
||||
pthread_kill(waiter->thread, SIGCONT);
|
||||
return;
|
||||
} else {
|
||||
pthread_kill(waiter->thread, SIGCONT);
|
||||
}
|
||||
}
|
||||
|
||||
waitq->lock.unlock();
|
||||
}
|
||||
|
||||
void futex_wait(Futex *f, Footex val) {
|
||||
Futex_Waiter waiter;
|
||||
waiter.thread = pthread_self();
|
||||
waiter.futex = f;
|
||||
|
||||
auto waitq = get_waitq(f);
|
||||
while (waitq->lock.state.test_and_set(std::memory_order_acquire)) {
|
||||
if (f->load(std::memory_order_relaxed) != val) {
|
||||
return;
|
||||
}
|
||||
#if defined(GB_CPU_X86)
|
||||
_mm_pause();
|
||||
#else
|
||||
(void)0; // spin...
|
||||
#endif
|
||||
}
|
||||
|
||||
waiter.waitq = waitq;
|
||||
waiter.lock.init();
|
||||
waiter.lock.lock();
|
||||
|
||||
auto head = &waitq->list;
|
||||
waiter.prev = head->prev;
|
||||
waiter.next = head;
|
||||
waiter.prev->next = &waiter;
|
||||
waiter.next->prev = &waiter;
|
||||
|
||||
waiter.prev->next = &waiter;
|
||||
waiter.next->prev = &waiter;
|
||||
|
||||
sigset_t old_mask, mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGCONT);
|
||||
pthread_sigmask(SIG_BLOCK, &mask, &old_mask);
|
||||
|
||||
if (f->load(std::memory_order_relaxed) == val) {
|
||||
waiter.lock.unlock();
|
||||
waitq->lock.unlock();
|
||||
|
||||
int sig;
|
||||
sigwait(&mask, &sig);
|
||||
|
||||
waitq->lock.lock();
|
||||
waiter.lock.lock();
|
||||
|
||||
while (waitq != waiter.waitq) {
|
||||
auto req = waiter.waitq;
|
||||
waiter.lock.unlock();
|
||||
waitq->lock.unlock();
|
||||
waitq = req;
|
||||
waitq->lock.lock();
|
||||
waiter.lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
waiter.prev->next = waiter.next;
|
||||
waiter.next->prev = waiter.prev;
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &old_mask, NULL);
|
||||
|
||||
waiter.lock.unlock();
|
||||
waitq->lock.unlock();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -825,6 +825,7 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
|
||||
case TargetOs_essence:
|
||||
case TargetOs_freebsd:
|
||||
case TargetOs_openbsd:
|
||||
case TargetOs_haiku:
|
||||
debug_format = TB_DEBUGFMT_DWARF;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user