From 4558f3992a47b4597563152baf26f1d2b5684b4d Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Tue, 16 Apr 2024 14:27:29 +0200 Subject: [PATCH 01/36] Initial commit of NetBSD port --- base/runtime/entry_unix.odin | 2 +- base/runtime/heap_allocator_unix.odin | 2 +- base/runtime/os_specific_bsd.odin | 8 +- build_odin.sh | 4 + core/os/os_netbsd.odin | 755 ++++++++++++++++++++++++++ core/os/stat_unix.odin | 2 +- core/os/stream.odin | 4 +- core/sync/futex_netbsd.odin | 166 ++++++ core/sync/primitives_netbsd.odin | 9 + core/sys/unix/pthread_netbsd.odin | 103 ++++ core/sys/unix/pthread_unix.odin | 2 +- core/sys/unix/signal_netbsd.odin | 31 ++ core/sys/unix/time_unix.odin | 5 +- core/thread/thread_unix.odin | 2 +- core/time/time_unix.odin | 2 +- src/build_settings.cpp | 17 + src/checker.cpp | 1 + src/gb/gb.h | 43 +- src/path.cpp | 2 +- src/threading.cpp | 11 +- 20 files changed, 1154 insertions(+), 17 deletions(-) create mode 100644 core/os/os_netbsd.odin create mode 100644 core/sync/futex_netbsd.odin create mode 100644 core/sync/primitives_netbsd.odin create mode 100644 core/sys/unix/pthread_netbsd.odin create mode 100644 core/sys/unix/signal_netbsd.odin diff --git a/base/runtime/entry_unix.odin b/base/runtime/entry_unix.odin index e49698e6e..7d7252625 100644 --- a/base/runtime/entry_unix.odin +++ b/base/runtime/entry_unix.odin @@ -1,5 +1,5 @@ //+private -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku //+no-instrumentation package runtime diff --git a/base/runtime/heap_allocator_unix.odin b/base/runtime/heap_allocator_unix.odin index 2b6698885..a8a4e9169 100644 --- a/base/runtime/heap_allocator_unix.odin +++ b/base/runtime/heap_allocator_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku //+private package runtime diff --git a/base/runtime/os_specific_bsd.odin b/base/runtime/os_specific_bsd.odin index 9cd065ff6..46ce51166 100644 --- a/base/runtime/os_specific_bsd.odin +++ b/base/runtime/os_specific_bsd.odin @@ -1,4 +1,4 @@ -//+build freebsd, openbsd +//+build freebsd, openbsd, netbsd //+private package runtime @@ -9,7 +9,11 @@ foreign libc { @(link_name="write") _unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int --- - __error :: proc() -> ^i32 --- + when ODIN_OS == .NetBSD { + @(link_name="__errno") __error :: proc() -> ^i32 --- + } else { + __error :: proc() -> ^i32 --- + } } _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { diff --git a/build_odin.sh b/build_odin.sh index c53766290..df4451060 100755 --- a/build_odin.sh +++ b/build_odin.sh @@ -69,6 +69,10 @@ FreeBSD) CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" ;; +NetBSD) + CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" + LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" + ;; Linux) CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)" diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin new file mode 100644 index 000000000..c75715ac7 --- /dev/null +++ b/core/os/os_netbsd.odin @@ -0,0 +1,755 @@ +package os + +foreign import dl "system:dl" +foreign import libc "system:c" + +import "base:runtime" +import "core:strings" +import "core:sys/unix" +import "core:c" + +Handle :: distinct i32 +File_Time :: distinct u64 +Errno :: distinct i32 + +INVALID_HANDLE :: ~Handle(0) + +ERROR_NONE: Errno : 0 /* No error */ +EPERM: Errno : 1 /* Operation not permitted */ +ENOENT: Errno : 2 /* No such file or directory */ +EINTR: Errno : 4 /* Interrupted system call */ +ESRCH: Errno : 3 /* No such process */ +EIO: Errno : 5 /* Input/output error */ +ENXIO: Errno : 6 /* Device not configured */ +E2BIG: Errno : 7 /* Argument list too long */ +ENOEXEC: Errno : 8 /* Exec format error */ +EBADF: Errno : 9 /* Bad file descriptor */ +ECHILD: Errno : 10 /* No child processes */ +EDEADLK: Errno : 11 /* Resource deadlock avoided. 11 was EAGAIN */ +ENOMEM: Errno : 12 /* Cannot allocate memory */ +EACCES: Errno : 13 /* Permission denied */ +EFAULT: Errno : 14 /* Bad address */ +ENOTBLK: Errno : 15 /* Block device required */ +EBUSY: Errno : 16 /* Device busy */ +EEXIST: Errno : 17 /* File exists */ +EXDEV: Errno : 18 /* Cross-device link */ +ENODEV: Errno : 19 /* Operation not supported by device */ +ENOTDIR: Errno : 20 /* Not a directory */ +EISDIR: Errno : 21 /* Is a directory */ +EINVAL: Errno : 22 /* Invalid argument */ +ENFILE: Errno : 23 /* Too many open files in system */ +EMFILE: Errno : 24 /* Too many open files */ +ENOTTY: Errno : 25 /* Inappropriate ioctl for device */ +ETXTBSY: Errno : 26 /* Text file busy */ +EFBIG: Errno : 27 /* File too large */ +ENOSPC: Errno : 28 /* No space left on device */ +ESPIPE: Errno : 29 /* Illegal seek */ +EROFS: Errno : 30 /* Read-only file system */ +EMLINK: Errno : 31 /* Too many links */ +EPIPE: Errno : 32 /* Broken pipe */ + +/* math software */ +EDOM: Errno : 33 /* Numerical argument out of domain */ +ERANGE: Errno : 34 /* Result too large or too small */ + +/* non-blocking and interrupt i/o */ +EAGAIN: Errno : 35 /* Resource temporarily unavailable */ +EWOULDBLOCK: Errno : EAGAIN /* Operation would block */ +EINPROGRESS: Errno : 36 /* Operation now in progress */ +EALREADY: Errno : 37 /* Operation already in progress */ + +/* ipc/network software -- argument errors */ +ENOTSOCK: Errno : 38 /* Socket operation on non-socket */ +EDESTADDRREQ: Errno : 39 /* Destination address required */ +EMSGSIZE: Errno : 40 /* Message too long */ +EPROTOTYPE: Errno : 41 /* Protocol wrong type for socket */ +ENOPROTOOPT: Errno : 42 /* Protocol option not available */ +EPROTONOSUPPORT: Errno : 43 /* Protocol not supported */ +ESOCKTNOSUPPORT: Errno : 44 /* Socket type not supported */ +EOPNOTSUPP: Errno : 45 /* Operation not supported */ +EPFNOSUPPORT: Errno : 46 /* Protocol family not supported */ +EAFNOSUPPORT: Errno : 47 /* Address family not supported by protocol family */ +EADDRINUSE: Errno : 48 /* Address already in use */ +EADDRNOTAVAIL: Errno : 49 /* Can't assign requested address */ + +/* ipc/network software -- operational errors */ +ENETDOWN: Errno : 50 /* Network is down */ +ENETUNREACH: Errno : 51 /* Network is unreachable */ +ENETRESET: Errno : 52 /* Network dropped connection on reset */ +ECONNABORTED: Errno : 53 /* Software caused connection abort */ +ECONNRESET: Errno : 54 /* Connection reset by peer */ +ENOBUFS: Errno : 55 /* No buffer space available */ +EISCONN: Errno : 56 /* Socket is already connected */ +ENOTCONN: Errno : 57 /* Socket is not connected */ +ESHUTDOWN: Errno : 58 /* Can't send after socket shutdown */ +ETOOMANYREFS: Errno : 59 /* Too many references: can't splice */ +ETIMEDOUT: Errno : 60 /* Operation timed out */ +ECONNREFUSED: Errno : 61 /* Connection refused */ + +ELOOP: Errno : 62 /* Too many levels of symbolic links */ +ENAMETOOLONG: Errno : 63 /* File name too long */ + +/* should be rearranged */ +EHOSTDOWN: Errno : 64 /* Host is down */ +EHOSTUNREACH: Errno : 65 /* No route to host */ +ENOTEMPTY: Errno : 66 /* Directory not empty */ + +/* quotas & mush */ +EPROCLIM: Errno : 67 /* Too many processes */ +EUSERS: Errno : 68 /* Too many users */ +EDQUOT: Errno : 69 /* Disc quota exceeded */ + +/* Network File System */ +ESTALE: Errno : 70 /* Stale NFS file handle */ +EREMOTE: Errno : 71 /* Too many levels of remote in path */ +EBADRPC: Errno : 72 /* RPC struct is bad */ +ERPCMISMATCH: Errno : 73 /* RPC version wrong */ +EPROGUNAVAIL: Errno : 74 /* RPC prog. not avail */ +EPROGMISMATCH: Errno : 75 /* Program version wrong */ +EPROCUNAVAIL: Errno : 76 /* Bad procedure for program */ + +ENOLCK: Errno : 77 /* No locks available */ +ENOSYS: Errno : 78 /* Function not implemented */ + +EFTYPE: Errno : 79 /* Inappropriate file type or format */ +EAUTH: Errno : 80 /* Authentication error */ +ENEEDAUTH: Errno : 81 /* Need authenticator */ + +/* SystemV IPC */ +EIDRM: Errno : 82 /* Identifier removed */ +ENOMSG: Errno : 83 /* No message of desired type */ +EOVERFLOW: Errno : 84 /* Value too large to be stored in data type */ + +/* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */ +EILSEQ: Errno : 85 /* Illegal byte sequence */ + +/* From IEEE Std 1003.1-2001 */ +/* Base, Realtime, Threads or Thread Priority Scheduling option errors */ +ENOTSUP: Errno : 86 /* Not supported */ + +/* Realtime option errors */ +ECANCELED: Errno : 87 /* Operation canceled */ + +/* Realtime, XSI STREAMS option errors */ +EBADMSG: Errno : 88 /* Bad or Corrupt message */ + +/* XSI STREAMS option errors */ +ENODATA: Errno : 89 /* No message available */ +ENOSR: Errno : 90 /* No STREAM resources */ +ENOSTR: Errno : 91 /* Not a STREAM */ +ETIME: Errno : 92 /* STREAM ioctl timeout */ + +/* File system extended attribute errors */ +ENOATTR: Errno : 93 /* Attribute not found */ + +/* Realtime, XSI STREAMS option errors */ +EMULTIHOP: Errno : 94 /* Multihop attempted */ +ENOLINK: Errno : 95 /* Link has been severed */ +EPROTO: Errno : 96 /* Protocol error */ + +/* Robust mutexes */ +EOWNERDEAD: Errno : 97 /* Previous owner died */ +ENOTRECOVERABLE: Errno : 98 /* State not recoverable */ + +ELAST: Errno : 98 /* Must equal largest errno */ + +/* end of errno */ + +O_RDONLY :: 0x000000000 +O_WRONLY :: 0x000000001 +O_RDWR :: 0x000000002 +O_CREATE :: 0x000000200 +O_EXCL :: 0x000000800 +O_NOCTTY :: 0x000008000 +O_TRUNC :: 0x000000400 +O_NONBLOCK :: 0x000000004 +O_APPEND :: 0x000000008 +O_SYNC :: 0x000000080 +O_ASYNC :: 0x000000040 +O_CLOEXEC :: 0x000400000 + +RTLD_LAZY :: 0x001 +RTLD_NOW :: 0x002 +RTLD_GLOBAL :: 0x100 +RTLD_LOCAL :: 0x200 +RTLD_TRACE :: 0x200 +RTLD_NODELETE :: 0x01000 +RTLD_NOLOAD :: 0x02000 + +MAX_PATH :: 1024 +MAXNAMLEN :: 511 + +args := _alloc_command_line_arguments() + +Unix_File_Time :: struct { + seconds: time_t, + nanoseconds: c.long, +} + +dev_t :: u64 +ino_t :: u64 +nlink_t :: u64 +off_t :: i64 +mode_t :: u16 +pid_t :: u32 +uid_t :: u32 +gid_t :: u32 +blkcnt_t :: i64 +blksize_t :: i32 +fflags_t :: u32 +time_t :: i64 + +OS_Stat :: struct { + device_id: dev_t, + mode: mode_t, + _padding0: i16, + ino: ino_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + _padding1: i32, + rdev: dev_t, + + last_access: Unix_File_Time, + modified: Unix_File_Time, + status_change: Unix_File_Time, + birthtime: Unix_File_Time, + + size: off_t, + blocks: blkcnt_t, + block_size: blksize_t, + + flags: fflags_t, + gen: u32, + lspare: [2]u32, +} + +Dirent :: struct { + ino: ino_t, + reclen: u16, + namlen: u16, + type: u8, + name: [MAXNAMLEN + 1]byte, +} + +Dir :: distinct rawptr // DIR* + +// File type +S_IFMT :: 0o170000 // Type of file mask +S_IFIFO :: 0o010000 // Named pipe (fifo) +S_IFCHR :: 0o020000 // Character special +S_IFDIR :: 0o040000 // Directory +S_IFBLK :: 0o060000 // Block special +S_IFREG :: 0o100000 // Regular +S_IFLNK :: 0o120000 // Symbolic link +S_IFSOCK :: 0o140000 // Socket + +// File mode +// Read, write, execute/search by owner +S_IRWXU :: 0o0700 // RWX mask for owner +S_IRUSR :: 0o0400 // R for owner +S_IWUSR :: 0o0200 // W for owner +S_IXUSR :: 0o0100 // X for owner + +// Read, write, execute/search by group +S_IRWXG :: 0o0070 // RWX mask for group +S_IRGRP :: 0o0040 // R for group +S_IWGRP :: 0o0020 // W for group +S_IXGRP :: 0o0010 // X for group + +// Read, write, execute/search by others +S_IRWXO :: 0o0007 // RWX mask for other +S_IROTH :: 0o0004 // R for other +S_IWOTH :: 0o0002 // W for other +S_IXOTH :: 0o0001 // X for other + +S_ISUID :: 0o4000 // Set user id on execution +S_ISGID :: 0o2000 // Set group id on execution +S_ISVTX :: 0o1000 // Directory restrcted delete + +S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } +S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } +S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } +S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } +S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } +S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } +S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } + +F_OK :: 0 // Test for file existance +X_OK :: 1 // Test for execute permission +W_OK :: 2 // Test for write permission +R_OK :: 4 // Test for read permission + +foreign libc { + @(link_name="__errno") __errno_location :: proc() -> ^c.int --- + + @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- + @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- + @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- + @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- + @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 --- + @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int --- + @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int --- + @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- + @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int --- + @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- + @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int --- + @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- + @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- + @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int --- + @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- + @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- + @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- + + @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- + @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- + @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- + @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- + + @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr --- + @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr --- + @(link_name="free") _unix_free :: proc(ptr: rawptr) --- + @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- + + @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- + @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- + @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int --- + + @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- +} + +foreign dl { + @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr --- + @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- + @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int --- + @(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- +} + +// NOTE(phix): Perhaps share the following functions with FreeBSD if they turn out to be the same in the end. + +is_path_separator :: proc(r: rune) -> bool { + return r == '/' +} + +get_last_error :: proc "contextless" () -> int { + return int(__errno_location()^) +} + +open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + handle := _unix_open(cstr, c.int(flags), c.int(mode)) + if handle == -1 { + return INVALID_HANDLE, Errno(get_last_error()) + } + return handle, ERROR_NONE +} + +close :: proc(fd: Handle) -> Errno { + result := _unix_close(fd) + if result == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +// We set a max of 1GB to keep alignment and to be safe. +@(private) +MAX_RW :: 1 << 30 + +read :: proc(fd: Handle, data: []byte) -> (int, Errno) { + to_read := min(c.size_t(len(data)), MAX_RW) + bytes_read := _unix_read(fd, &data[0], to_read) + if bytes_read == -1 { + return -1, Errno(get_last_error()) + } + return int(bytes_read), ERROR_NONE +} + +write :: proc(fd: Handle, data: []byte) -> (int, Errno) { + if len(data) == 0 { + return 0, ERROR_NONE + } + + to_write := min(c.size_t(len(data)), MAX_RW) + bytes_written := _unix_write(fd, &data[0], to_write) + if bytes_written == -1 { + return -1, Errno(get_last_error()) + } + return int(bytes_written), ERROR_NONE +} + +seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { + res := _unix_seek(fd, offset, c.int(whence)) + if res == -1 { + return -1, Errno(get_last_error()) + } + return res, ERROR_NONE +} + +file_size :: proc(fd: Handle) -> (i64, Errno) { + s, err := fstat(fd) + if err != ERROR_NONE { + return -1, err + } + return s.size, ERROR_NONE +} + +rename :: proc(old_path, new_path: string) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) + new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) + res := _unix_rename(old_path_cstr, new_path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +remove :: proc(path: string) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_unlink(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_mkdir(path_cstr, mode) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +remove_directory :: proc(path: string) -> Errno { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_rmdir(path_cstr) + if res == -1 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +is_file_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_file_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISREG(s.mode) +} + +is_dir_handle :: proc(fd: Handle) -> bool { + s, err := _fstat(fd) + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { + s: OS_Stat + err: Errno + if follow_links { + s, err = _stat(path) + } else { + s, err = _lstat(path) + } + if err != ERROR_NONE { + return false + } + return S_ISDIR(s.mode) +} + +is_file :: proc {is_file_path, is_file_handle} +is_dir :: proc {is_dir_path, is_dir_handle} + +// NOTE(bill): Uses startup to initialize it + +stdin: Handle = 0 +stdout: Handle = 1 +stderr: Handle = 2 + +last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { + s, err := _fstat(fd) + if err != ERROR_NONE { + return 0, err + } + modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds + return File_Time(modified), ERROR_NONE +} + +last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { + s, err := _stat(name) + if err != ERROR_NONE { + return 0, err + } + modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds + return File_Time(modified), ERROR_NONE +} + +@private +_stat :: proc(path: string) -> (OS_Stat, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + s: OS_Stat = --- + result := _unix_lstat(cstr, &s) + if result == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_lstat :: proc(path: string) -> (OS_Stat, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + + // deliberately uninitialized + s: OS_Stat = --- + res := _unix_lstat(cstr, &s) + if res == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { + s: OS_Stat = --- + result := _unix_fstat(fd, &s) + if result == -1 { + return s, Errno(get_last_error()) + } + return s, ERROR_NONE +} + +@private +_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { + dirp := _unix_fdopendir(fd) + if dirp == cast(Dir)nil { + return nil, Errno(get_last_error()) + } + return dirp, ERROR_NONE +} + +@private +_closedir :: proc(dirp: Dir) -> Errno { + rc := _unix_closedir(dirp) + if rc != 0 { + return Errno(get_last_error()) + } + return ERROR_NONE +} + +@private +_rewinddir :: proc(dirp: Dir) { + _unix_rewinddir(dirp) +} + +@private +_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { + result: ^Dirent + rc := _unix_readdir_r(dirp, &entry, &result) + + if rc != 0 { + err = Errno(get_last_error()) + return + } + err = ERROR_NONE + + if result == nil { + end_of_stream = true + return + } + + return +} + +@private +_readlink :: proc(path: string) -> (string, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) + + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + + bufsz : uint = MAX_PATH + buf := make([]byte, MAX_PATH) + for { + rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) + if rc == -1 { + delete(buf) + return "", Errno(get_last_error()) + } else if rc == int(bufsz) { + bufsz += MAX_PATH + delete(buf) + buf = make([]byte, bufsz) + } else { + return strings.string_from_ptr(&buf[0], rc), ERROR_NONE + } + } + + return "", Errno{} +} + +absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { + return "", Errno(ENOSYS) +} + +absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { + rel := rel + if rel == "" { + rel = "." + } + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) + + rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) + + path_ptr := _unix_realpath(rel_cstr, nil) + if path_ptr == nil { + return "", Errno(get_last_error()) + } + defer _unix_free(path_ptr) + + path_cstr := transmute(cstring)path_ptr + path = strings.clone( string(path_cstr) ) + + return path, ERROR_NONE +} + +access :: proc(path: string, mask: int) -> (bool, Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + + cstr := strings.clone_to_cstring(path, context.temp_allocator) + result := _unix_access(cstr, c.int(mask)) + if result == -1 { + return false, Errno(get_last_error()) + } + return true, ERROR_NONE +} + +lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) + + path_str := strings.clone_to_cstring(key, context.temp_allocator) + cstr := _unix_getenv(path_str) + if cstr == nil { + return "", false + } + return strings.clone(string(cstr), allocator), true +} + +get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { + value, _ = lookup_env(key, allocator) + return +} + +get_current_directory :: proc() -> string { + // NOTE(tetra): I would use PATH_MAX here, but I was not able to find + // an authoritative value for it across all systems. + // The largest value I could find was 4096, so might as well use the page size. + page_size := get_page_size() + buf := make([dynamic]u8, page_size) + #no_bounds_check for { + cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf))) + if cwd != nil { + return string(cwd) + } + if Errno(get_last_error()) != ERANGE { + delete(buf) + return "" + } + resize(&buf, len(buf)+page_size) + } + unreachable() +} + +set_current_directory :: proc(path: string) -> (err: Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_chdir(cstr) + if res == -1 do return Errno(get_last_error()) + return ERROR_NONE +} + +exit :: proc "contextless" (code: int) -> ! { + runtime._cleanup_runtime_contextless() + _unix_exit(c.int(code)) +} + +current_thread_id :: proc "contextless" () -> int { + return cast(int) unix.pthread_self() +} + +dlopen :: proc(filename: string, flags: int) -> rawptr { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(filename, context.temp_allocator) + handle := _unix_dlopen(cstr, c.int(flags)) + return handle +} + +dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { + assert(handle != nil) + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cstr := strings.clone_to_cstring(symbol, context.temp_allocator) + proc_handle := _unix_dlsym(handle, cstr) + return proc_handle +} + +dlclose :: proc(handle: rawptr) -> bool { + assert(handle != nil) + return _unix_dlclose(handle) == 0 +} + +dlerror :: proc() -> string { + return string(_unix_dlerror()) +} + +get_page_size :: proc() -> int { + // NOTE(tetra): The page size never changes, so why do anything complicated + // if we don't have to. + @static page_size := -1 + if page_size != -1 do return page_size + + page_size = int(_unix_getpagesize()) + return page_size +} + +@(private) +_processor_core_count :: proc() -> int { + count : int = 0 + count_size := size_of(count) + if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 { + if count > 0 { + return count + } + } + + return 1 +} + +_alloc_command_line_arguments :: proc() -> []string { + res := make([]string, len(runtime.args__)) + for arg, i in runtime.args__ { + res[i] = string(arg) + } + return res +} diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin index 5e83c0e16..3bd62dfc7 100644 --- a/core/os/stat_unix.odin +++ b/core/os/stat_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package os import "core:time" diff --git a/core/os/stream.odin b/core/os/stream.odin index 25f31218c..4e73284f0 100644 --- a/core/os/stream.odin +++ b/core/os/stream.odin @@ -32,7 +32,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, } case .Read_At: - when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku) { + when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku) { n_int, os_err = read_at(fd, p, offset) n = i64(n_int) if n == 0 && os_err == 0 { @@ -46,7 +46,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, err = .EOF } case .Write_At: - when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku) { + when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku) { n_int, os_err = write_at(fd, p, offset) n = i64(n_int) if n == 0 && os_err == 0 { diff --git a/core/sync/futex_netbsd.odin b/core/sync/futex_netbsd.odin new file mode 100644 index 000000000..f82af77d6 --- /dev/null +++ b/core/sync/futex_netbsd.odin @@ -0,0 +1,166 @@ +//+private +package sync + +import "core:c" +import "core:time" +import "core:sys/unix" +import "base:runtime" + +@(private="file") +Wait_Node :: struct { + thread: unix.pthread_t, + futex: ^Futex, + prev, next: ^Wait_Node, +} +@(private="file") +atomic_flag :: distinct bool +@(private="file") +Wait_Queue :: struct { + lock: atomic_flag, + list: Wait_Node, +} +@(private="file") +waitq_lock :: proc "contextless" (waitq: ^Wait_Queue) { + for cast(bool)atomic_exchange_explicit(&waitq.lock, atomic_flag(true), .Acquire) { + cpu_relax() // spin... + } +} +@(private="file") +waitq_unlock :: proc "contextless" (waitq: ^Wait_Queue) { + atomic_store_explicit(&waitq.lock, atomic_flag(false), .Release) +} + +// FIXME: This approach may scale badly in the future, +// possible solution - hash map (leads to deadlocks now). +@(private="file") +g_waitq: Wait_Queue + +@(init, private="file") +g_waitq_init :: proc() { + g_waitq = { + list = { + prev = &g_waitq.list, + next = &g_waitq.list, + }, + } +} + +@(private="file") +get_waitq :: #force_inline proc "contextless" (f: ^Futex) -> ^Wait_Queue { + _ = f + return &g_waitq +} + +_futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) { + waitq := get_waitq(f) + waitq_lock(waitq) + defer waitq_unlock(waitq) + + head := &waitq.list + waiter := Wait_Node{ + thread = unix.pthread_self(), + futex = f, + prev = head, + next = head.next, + } + + waiter.prev.next = &waiter + waiter.next.prev = &waiter + + old_mask, mask: unix.sigset_t + unix.sigemptyset(&mask) + unix.sigaddset(&mask, unix.SIGCONT) + unix.pthread_sigmask(unix.SIG_BLOCK, &mask, &old_mask) + + if u32(atomic_load_explicit(f, .Acquire)) == expect { + waitq_unlock(waitq) + defer waitq_lock(waitq) + + sig: c.int + unix.sigwait(&mask, &sig) + errno := unix.errno() + ok = errno == unix.ERROR_NONE + } + + waiter.prev.next = waiter.next + waiter.next.prev = waiter.prev + + unix.pthread_sigmask(unix.SIG_SETMASK, &old_mask, nil) + + // FIXME: Add error handling! + return +} + +_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> (ok: bool) { + if duration <= 0 { + return false + } + waitq := get_waitq(f) + waitq_lock(waitq) + defer waitq_unlock(waitq) + + head := &waitq.list + waiter := Wait_Node{ + thread = unix.pthread_self(), + futex = f, + prev = head, + next = head.next, + } + + waiter.prev.next = &waiter + waiter.next.prev = &waiter + + old_mask, mask: unix.sigset_t + unix.sigemptyset(&mask) + unix.sigaddset(&mask, unix.SIGCONT) + unix.pthread_sigmask(unix.SIG_BLOCK, &mask, &old_mask) + + if u32(atomic_load_explicit(f, .Acquire)) == expect { + waitq_unlock(waitq) + defer waitq_lock(waitq) + + info: unix.siginfo_t + ts := unix.timespec{ + tv_sec = i64(duration / 1e9), + tv_nsec = i64(duration % 1e9), + } + unix.sigtimedwait(&mask, &info, &ts) + errno := unix.errno() + ok = errno == unix.EAGAIN || errno == unix.ERROR_NONE + } + + waiter.prev.next = waiter.next + waiter.next.prev = waiter.prev + + unix.pthread_sigmask(unix.SIG_SETMASK, &old_mask, nil) + + // FIXME: Add error handling! + return +} + +_futex_signal :: proc "contextless" (f: ^Futex) { + waitq := get_waitq(f) + waitq_lock(waitq) + defer waitq_unlock(waitq) + + head := &waitq.list + for waiter := head.next; waiter != head; waiter = waiter.next { + if waiter.futex == f { + unix.pthread_kill(waiter.thread, unix.SIGCONT) + break + } + } +} + +_futex_broadcast :: proc "contextless" (f: ^Futex) { + waitq := get_waitq(f) + waitq_lock(waitq) + defer waitq_unlock(waitq) + + head := &waitq.list + for waiter := head.next; waiter != head; waiter = waiter.next { + if waiter.futex == f { + unix.pthread_kill(waiter.thread, unix.SIGCONT) + } + } +} diff --git a/core/sync/primitives_netbsd.odin b/core/sync/primitives_netbsd.odin new file mode 100644 index 000000000..2d5b6b7fe --- /dev/null +++ b/core/sync/primitives_netbsd.odin @@ -0,0 +1,9 @@ +//+build netbsd +//+private +package sync + +import "core:sys/unix" + +_current_thread_id :: proc "contextless" () -> int { + return cast(int) unix.pthread_self() +} diff --git a/core/sys/unix/pthread_netbsd.odin b/core/sys/unix/pthread_netbsd.odin new file mode 100644 index 000000000..c334fa56c --- /dev/null +++ b/core/sys/unix/pthread_netbsd.odin @@ -0,0 +1,103 @@ +//+build netbsd +package unix + +import "core:c" + +pthread_t :: distinct u64 + +SEM_T_SIZE :: 8 + +PTHREAD_CONDATTR_T_SIZE :: 16 +PTHREAD_MUTEXATTR_T_SIZE :: 16 +PTHREAD_RWLOCKATTR_T_SIZE :: 16 +PTHREAD_BARRIERATTR_T_SIZE :: 16 + +PTHREAD_COND_T_SIZE :: 40 +PTHREAD_MUTEX_T_SIZE :: 48 +PTHREAD_RWLOCK_T_SIZE :: 64 +PTHREAD_BARRIER_T_SIZE :: 48 +PTHREAD_ATTR_T_SIZE :: 16 + +pthread_cond_t :: struct #align(16) { + _: [PTHREAD_COND_T_SIZE] c.char +} + +pthread_mutex_t :: struct #align(16) { + _: [PTHREAD_MUTEX_T_SIZE] c.char +} + +pthread_rwlock_t :: struct #align(16) { + _: [PTHREAD_RWLOCK_T_SIZE] c.char +} + +pthread_barrier_t :: struct #align(16) { + _: [PTHREAD_BARRIER_T_SIZE] c.char +} + +pthread_attr_t :: struct #align(16) { + _: [PTHREAD_ATTR_T_SIZE] c.char +} + +pthread_condattr_t :: struct #align(16) { + _: [PTHREAD_CONDATTR_T_SIZE] c.char +} + +pthread_mutexattr_t :: struct #align(16) { + _: [PTHREAD_MUTEXATTR_T_SIZE] c.char +} + +pthread_rwlockattr_t :: struct #align(16) { + _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char +} + +pthread_barrierattr_t :: struct #align(16) { + _: [PTHREAD_BARRIERATTR_T_SIZE] c.char +} + +PTHREAD_MUTEX_NORMAL :: 0 +PTHREAD_MUTEX_ERRORCHECK :: 1 +PTHREAD_MUTEX_RECURSIVE :: 2 + +PTHREAD_CREATE_JOINABLE :: 0 +PTHREAD_CREATE_DETACHED :: 1 +PTHREAD_INHERIT_SCHED :: 0 +PTHREAD_EXPLICIT_SCHED :: 1 +PTHREAD_PROCESS_PRIVATE :: 0 +PTHREAD_PROCESS_SHARED :: 1 + +SCHED_NONE :: -1 +SCHED_OTHER :: 0 +SCHED_FIFO :: 1 +SCHED_RR :: 3 + +sched_param :: struct { + sched_priority: c.int, +} + +sem_t :: struct #align(16) { + _: [SEM_T_SIZE] c.char +} + +PTHREAD_CANCEL_ENABLE :: 0 +PTHREAD_CANCEL_DISABLE :: 1 +PTHREAD_CANCEL_DEFERRED :: 0 +PTHREAD_CANCEL_ASYNCHRONOUS :: 1 + +foreign import "system:pthread" + +@(default_calling_convention="c") +foreign pthread { + sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t --- + + sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int --- + sem_destroy :: proc(sem: ^sem_t) -> c.int --- + sem_post :: proc(sem: ^sem_t) -> c.int --- + sem_wait :: proc(sem: ^sem_t) -> c.int --- + sem_trywait :: proc(sem: ^sem_t) -> c.int --- + + pthread_yield :: proc() --- + + pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- + pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- + pthread_cancel :: proc (thread: pthread_t) -> c.int --- +} diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin index 4fe3c8dfa..5760560ee 100644 --- a/core/sys/unix/pthread_unix.odin +++ b/core/sys/unix/pthread_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package unix foreign import "system:pthread" diff --git a/core/sys/unix/signal_netbsd.odin b/core/sys/unix/signal_netbsd.odin new file mode 100644 index 000000000..6655a6d38 --- /dev/null +++ b/core/sys/unix/signal_netbsd.odin @@ -0,0 +1,31 @@ +package unix + +import "core:c" + +foreign import libc "system:c" + +ERROR_NONE :: 0 +EAGAIN :: 35 + +SIGCONT :: 19 + +SIG_BLOCK :: 1 +SIG_UNBLOCK :: 2 +SIG_SETMASK :: 3 + +siginfo_t :: struct { si_pad: [128]c.char } +sigset_t :: struct { bits: [4]c.uint } + +foreign libc { + sigemptyset :: proc(set: ^sigset_t) -> c.int --- + sigaddset :: proc(set: ^sigset_t, _signal: c.int) -> c.int --- + + sigtimedwait :: proc(set: ^sigset_t, info: ^siginfo_t, timeout: ^timespec) -> c.int --- + sigwait :: proc(set: ^sigset_t, _signal: ^c.int) -> c.int --- + + @(private="file", link_name="__errno") get_error_location :: proc() -> ^c.int --- +} + +errno :: #force_inline proc "contextless" () -> int { + return int(get_error_location()^) +} diff --git a/core/sys/unix/time_unix.odin b/core/sys/unix/time_unix.odin index 088dc378b..6e7386ae3 100644 --- a/core/sys/unix/time_unix.odin +++ b/core/sys/unix/time_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package unix when ODIN_OS == .Darwin { @@ -65,7 +65,8 @@ seconds_since_boot :: proc "c" () -> f64 { return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9 } - +// TODO(phix): 'nanosleep' here might refere to the wrong version on NetBSD. Need to investigate this further. +// Normally this is solved by just including 'time.h'. So I need to understand the macro-magic going on in there. inline_nanosleep :: proc "c" (nanoseconds: i64) -> (remaining: timespec, res: i32) { s, ns := nanoseconds / 1e9, nanoseconds % 1e9 requested := timespec{tv_sec=s, tv_nsec=ns} diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index c75710873..3640251dd 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -1,4 +1,4 @@ -// +build linux, darwin, freebsd, openbsd, haiku +// +build linux, darwin, freebsd, openbsd, netbsd, haiku // +private package thread diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin index 1c46b5994..f3e0d6640 100644 --- a/core/time/time_unix.odin +++ b/core/time/time_unix.odin @@ -1,5 +1,5 @@ //+private -//+build linux, darwin, freebsd, openbsd, haiku +//+build linux, darwin, freebsd, openbsd, netbsd, haiku package time import "core:sys/unix" diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 30d6f0b3c..cd57816ab 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -18,6 +18,7 @@ enum TargetOsKind : u16 { TargetOs_essence, TargetOs_freebsd, TargetOs_openbsd, + TargetOs_netbsd, TargetOs_haiku, TargetOs_wasi, @@ -79,6 +80,7 @@ gb_global String target_os_names[TargetOs_COUNT] = { str_lit("essence"), str_lit("freebsd"), str_lit("openbsd"), + str_lit("netbsd"), str_lit("haiku"), str_lit("wasi"), @@ -549,6 +551,14 @@ 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_netbsd_amd64 = { + TargetOs_netbsd, + TargetArch_amd64, + 8, 8, 8, 16, + str_lit("x86_64-unknown-netbsd-elf"), + str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), +}; + gb_global TargetMetrics target_haiku_amd64 = { TargetOs_haiku, TargetArch_amd64, @@ -655,6 +665,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freebsd_amd64"), &target_freebsd_amd64 }, { str_lit("openbsd_amd64"), &target_openbsd_amd64 }, + { str_lit("netbsd_amd64"), &target_netbsd_amd64 }, { str_lit("haiku_amd64"), &target_haiku_amd64 }, { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, @@ -1410,6 +1421,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_NETBSD) + metrics = &target_netbsd_amd64; #elif defined(GB_SYSTEM_HAIKU) metrics = &target_haiku_amd64; #elif defined(GB_CPU_ARM) @@ -1523,6 +1536,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_netbsd: + bc->link_flags = str_lit("-arch x86-64 "); + break; case TargetOs_haiku: bc->link_flags = str_lit("-arch x86-64 "); break; @@ -2002,6 +2018,7 @@ gb_internal bool init_build_paths(String init_filename) { case TargetOs_essence: case TargetOs_freebsd: case TargetOs_openbsd: + case TargetOs_netbsd: case TargetOs_haiku: gb_printf_err("-no-crt on unix systems requires either -default-to-nil-allocator or -default-to-panic-allocator to also be present because the default allocator requires crt\n"); return false; diff --git a/src/checker.cpp b/src/checker.cpp index 244e7efbd..77d894624 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1010,6 +1010,7 @@ gb_internal void init_universal(void) { {"FreeBSD", TargetOs_freebsd}, {"Haiku", TargetOs_haiku}, {"OpenBSD", TargetOs_openbsd}, + {"NetBSD", TargetOs_netbsd}, {"WASI", TargetOs_wasi}, {"JS", TargetOs_js}, {"Freestanding", TargetOs_freestanding}, diff --git a/src/gb/gb.h b/src/gb/gb.h index 868e11a16..4e05a3a0a 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -83,6 +83,10 @@ extern "C" { #ifndef GB_SYSTEM_OPENBSD #define GB_SYSTEM_OPENBSD 1 #endif + #elif defined(__NetBSD__) + #ifndef GB_SYSTEM_NETBSD + #define GB_SYSTEM_NETBSD 1 + #endif #elif defined(__HAIKU__) || defined(__haiku__) #ifndef GB_SYSTEM_HAIKU #define GB_SYSTEM_HAIKU 1 @@ -208,7 +212,7 @@ extern "C" { #endif #include // NOTE(bill): malloc on linux #include - #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__HAIKU__) + #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__HAIKU__) #include #endif #include @@ -250,6 +254,11 @@ extern "C" { #define lseek64 lseek #endif +#if defined(GB_SYSTEM_NETBSD) + #include + #define lseek64 lseek +#endif + #if defined(GB_SYSTEM_HAIKU) #include #include @@ -817,6 +826,13 @@ typedef struct gbAffinity { isize thread_count; isize threads_per_core; } gbAffinity; +#elif defined(GB_SYSTEM_NETBSD) +typedef struct gbAffinity { + b32 is_accurate; + isize core_count; + isize thread_count; + isize threads_per_core; +} gbAffinity; #elif defined(GB_SYSTEM_HAIKU) typedef struct gbAffinity { b32 is_accurate; @@ -3267,6 +3283,31 @@ 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_NETBSD) +#include + +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; +} + #elif defined(GB_SYSTEM_HAIKU) #include diff --git a/src/path.cpp b/src/path.cpp index b07f20870..2f016a578 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -341,7 +341,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array *fi) return ReadDirectory_None; } -#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_HAIKU) +#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD) || defined(GB_SYSTEM_HAIKU) #include diff --git a/src/threading.cpp b/src/threading.cpp index fbe8997d1..77aa8edf7 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -627,8 +627,12 @@ gb_internal void thread_set_name(Thread *t, char const *name) { #elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) pthread_set_name_np(t->posix_handle, name); #else - // TODO(bill): Test if this works - pthread_setname_np(t->posix_handle, name); + #ifdef GB_SYSTEM_NETBSD + // TODO(phix): Could be that libs are to old on NetBSD? Just ignore for now. + #else + // TODO(bill): Test if this works + pthread_setname_np(t->posix_handle, name); + #endif #endif } @@ -901,10 +905,11 @@ gb_internal void futex_wait(Futex *f, Footex val) { } while (f->load() == val); } -#elif defined(GB_SYSTEM_HAIKU) +#elif defined(GB_SYSTEM_HAIKU) || defined(GB_SYSTEM_NETBSD) // Futex implementation taken from https://tavianator.com/2023/futex.html +#include #include #include From 80067a959b51d4bda4aaf96677ca1d2e4831323d Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Wed, 17 Apr 2024 09:42:41 +0200 Subject: [PATCH 02/36] Added thread name Call pthread_setname_np with the correct number of arguments on NetBSD. --- src/threading.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/threading.cpp b/src/threading.cpp index 77aa8edf7..79ed8e8a4 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -626,13 +626,11 @@ gb_internal void thread_set_name(Thread *t, char const *name) { pthread_setname_np(name); #elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) pthread_set_name_np(t->posix_handle, name); +#elif defined(GB_SYSTEM_NETBSD) + pthread_setname_np(t->posix_handle, "%s", (void*)name); #else - #ifdef GB_SYSTEM_NETBSD - // TODO(phix): Could be that libs are to old on NetBSD? Just ignore for now. - #else - // TODO(bill): Test if this works - pthread_setname_np(t->posix_handle, name); - #endif + // TODO(bill): Test if this works + pthread_setname_np(t->posix_handle, name); #endif } From 2055f2b93364dc9cab1e3a93fdc02d1c5fe4ac4f Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Wed, 17 Apr 2024 09:46:57 +0200 Subject: [PATCH 03/36] Fixed link warnings Hardlink libc functions to the correct version on NetBSD 10 since we do not use the micro-magic from C. --- core/os/os_netbsd.odin | 6 +++--- core/sys/unix/signal_netbsd.odin | 11 +++++------ core/sys/unix/time_unix.odin | 21 ++++++++++++++------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index c75715ac7..e10b2eab0 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -281,7 +281,7 @@ W_OK :: 2 // Test for write permission R_OK :: 4 // Test for read permission foreign libc { - @(link_name="__errno") __errno_location :: proc() -> ^c.int --- + @(link_name="__errno") __errno_location :: proc() -> ^c.int --- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- @@ -290,8 +290,8 @@ foreign libc { @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 --- @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int --- @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int --- - @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- - @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int --- + @(link_name="__lstat50") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- + @(link_name="__fstat50") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int --- @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int --- @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- diff --git a/core/sys/unix/signal_netbsd.odin b/core/sys/unix/signal_netbsd.odin index 6655a6d38..b74c7287b 100644 --- a/core/sys/unix/signal_netbsd.odin +++ b/core/sys/unix/signal_netbsd.odin @@ -14,14 +14,13 @@ SIG_UNBLOCK :: 2 SIG_SETMASK :: 3 siginfo_t :: struct { si_pad: [128]c.char } -sigset_t :: struct { bits: [4]c.uint } +sigset_t :: struct { bits: [4]u32 } foreign libc { - sigemptyset :: proc(set: ^sigset_t) -> c.int --- - sigaddset :: proc(set: ^sigset_t, _signal: c.int) -> c.int --- - - sigtimedwait :: proc(set: ^sigset_t, info: ^siginfo_t, timeout: ^timespec) -> c.int --- - sigwait :: proc(set: ^sigset_t, _signal: ^c.int) -> c.int --- + @(link_name="__sigemptyset14") sigemptyset :: proc(set: ^sigset_t) -> c.int --- + @(link_name="__sigaddset14") sigaddset :: proc(set: ^sigset_t, _signal: c.int) -> c.int --- + @(link_name="__sigtimedwait50") sigtimedwait :: proc(set: ^sigset_t, info: ^siginfo_t, timeout: ^timespec) -> c.int --- + @(link_name="sigwait") sigwait :: proc(set: ^sigset_t, _signal: ^c.int) -> c.int --- @(private="file", link_name="__errno") get_error_location :: proc() -> ^c.int --- } diff --git a/core/sys/unix/time_unix.odin b/core/sys/unix/time_unix.odin index 6e7386ae3..442202d36 100644 --- a/core/sys/unix/time_unix.odin +++ b/core/sys/unix/time_unix.odin @@ -9,11 +9,20 @@ when ODIN_OS == .Darwin { import "core:c" -@(default_calling_convention="c") -foreign libc { - clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int --- - sleep :: proc(seconds: c.uint) -> c.int --- - nanosleep :: proc(requested, remaining: ^timespec) -> c.int --- +when ODIN_OS == .NetBSD { + @(default_calling_convention="c") + foreign libc { + @(link_name="__clock_gettime50") clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int --- + @(link_name="__nanosleep50") nanosleep :: proc(requested, remaining: ^timespec) -> c.int --- + @(link_name="sleep") sleep :: proc(seconds: c.uint) -> c.int --- + } +} else { + @(default_calling_convention="c") + foreign libc { + clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int --- + sleep :: proc(seconds: c.uint) -> c.int --- + nanosleep :: proc(requested, remaining: ^timespec) -> c.int --- + } } timespec :: struct { @@ -65,8 +74,6 @@ seconds_since_boot :: proc "c" () -> f64 { return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9 } -// TODO(phix): 'nanosleep' here might refere to the wrong version on NetBSD. Need to investigate this further. -// Normally this is solved by just including 'time.h'. So I need to understand the macro-magic going on in there. inline_nanosleep :: proc "c" (nanoseconds: i64) -> (remaining: timespec, res: i32) { s, ns := nanoseconds / 1e9, nanoseconds % 1e9 requested := timespec{tv_sec=s, tv_nsec=ns} From 38640d5d9e223c30889e33855c5fd953cc8ce37f Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Thu, 18 Apr 2024 10:12:42 +0200 Subject: [PATCH 04/36] Updated core lib and did cleanup Updated core with some path related functions and did some minor code cleanup. Most of the standard library function is just a matter of copy what is there for the other BSDs. --- core/os/{dir_freebsd.odin => dir_bsd.odin} | 1 + core/os/os_netbsd.odin | 7 +++++++ core/path/filepath/path_unix.odin | 4 ++-- core/sys/unix/signal_netbsd.odin | 4 ++-- core/thread/thread_unix.odin | 4 ++-- 5 files changed, 14 insertions(+), 6 deletions(-) rename core/os/{dir_freebsd.odin => dir_bsd.odin} (98%) diff --git a/core/os/dir_freebsd.odin b/core/os/dir_bsd.odin similarity index 98% rename from core/os/dir_freebsd.odin rename to core/os/dir_bsd.odin index 2965182cd..c0dc8ad1f 100644 --- a/core/os/dir_freebsd.odin +++ b/core/os/dir_bsd.odin @@ -1,3 +1,4 @@ +//+build freebsd, netbsd package os import "core:mem" diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index e10b2eab0..c72c6d4ab 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -483,6 +483,13 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { is_file :: proc {is_file_path, is_file_handle} is_dir :: proc {is_dir_path, is_dir_handle} +exists :: proc(path: string) -> bool { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + cpath := strings.clone_to_cstring(path, context.temp_allocator) + res := _unix_access(cpath, O_RDONLY) + return res == 0 +} + // NOTE(bill): Uses startup to initialize it stdin: Handle = 0 diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin index 9beda5557..a4b27b027 100644 --- a/core/path/filepath/path_unix.odin +++ b/core/path/filepath/path_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd +//+build linux, darwin, freebsd, openbsd, netbsd package filepath when ODIN_OS == .Darwin { @@ -61,7 +61,7 @@ when ODIN_OS == .Darwin { foreign libc { @(link_name="__error") __error :: proc() -> ^i32 --- } -} else when ODIN_OS == .OpenBSD { +} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { @(private) foreign libc { @(link_name="__errno") __error :: proc() -> ^i32 --- diff --git a/core/sys/unix/signal_netbsd.odin b/core/sys/unix/signal_netbsd.odin index b74c7287b..c32f0bfbe 100644 --- a/core/sys/unix/signal_netbsd.odin +++ b/core/sys/unix/signal_netbsd.odin @@ -13,8 +13,8 @@ SIG_BLOCK :: 1 SIG_UNBLOCK :: 2 SIG_SETMASK :: 3 -siginfo_t :: struct { si_pad: [128]c.char } -sigset_t :: struct { bits: [4]u32 } +siginfo_t :: struct { _: [128]u8 } +sigset_t :: struct { _: [4]u32 } foreign libc { @(link_name="__sigemptyset14") sigemptyset :: proc(set: ^sigset_t) -> c.int --- diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index 3640251dd..c80d6854b 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -20,7 +20,7 @@ Thread_Os_Specific :: struct #align(16) { // It then waits for `start` to be called. // _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { - __linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr { + __unix_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr { t := (^Thread)(t) when ODIN_OS != .Darwin { @@ -109,7 +109,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { assert(res == 0) thread.procedure = procedure - if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 { + if unix.pthread_create(&thread.unix_thread, &attrs, __unix_thread_entry_proc, thread) != 0 { free(thread, thread.creation_allocator) return nil } From 41d4dfbcd5d0d977ac01170fcc236a099af34f90 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Thu, 18 Apr 2024 16:20:49 +0200 Subject: [PATCH 05/36] Fixed concurrency issue Fixed broken thread policy causing deadlocks. --- core/thread/thread_unix.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index c80d6854b..e25d3541c 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -78,7 +78,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { // NOTE(tetra, 2019-11-01): These only fail if their argument is invalid. assert(unix.pthread_attr_setdetachstate(&attrs, unix.PTHREAD_CREATE_JOINABLE) == 0) - when ODIN_OS != .Haiku { + when ODIN_OS != .Haiku && ODIN_OS != .NetBSD { assert(unix.pthread_attr_setinheritsched(&attrs, unix.PTHREAD_EXPLICIT_SCHED) == 0) } @@ -91,7 +91,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { // Set thread priority. policy: i32 res: i32 - when ODIN_OS != .Haiku { + when ODIN_OS != .Haiku && ODIN_OS != .NetBSD { res = unix.pthread_attr_getschedpolicy(&attrs, &policy) assert(res == 0) } From 07fc07822d0a5c56cdd05b08c318b33aa241479b Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Thu, 18 Apr 2024 16:22:07 +0200 Subject: [PATCH 06/36] Added some libc bindings for NetBSD --- core/c/libc/errno.odin | 2 +- core/c/libc/time.odin | 2 +- core/c/libc/wctype.odin | 2 +- core/os/stream.odin | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/c/libc/errno.odin b/core/c/libc/errno.odin index 7af763706..d28a24f56 100644 --- a/core/c/libc/errno.odin +++ b/core/c/libc/errno.odin @@ -40,7 +40,7 @@ when ODIN_OS == .FreeBSD { ERANGE :: 34 } -when ODIN_OS == .OpenBSD { +when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { @(private="file") @(default_calling_convention="c") foreign libc { diff --git a/core/c/libc/time.odin b/core/c/libc/time.odin index 4c4280f30..924cf4aec 100644 --- a/core/c/libc/time.odin +++ b/core/c/libc/time.odin @@ -45,7 +45,7 @@ when ODIN_OS == .Windows { } } -when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku { +when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku { @(default_calling_convention="c") foreign libc { // 7.27.2 Time manipulation functions diff --git a/core/c/libc/wctype.odin b/core/c/libc/wctype.odin index cbce220d4..a41fe7fac 100644 --- a/core/c/libc/wctype.odin +++ b/core/c/libc/wctype.odin @@ -22,7 +22,7 @@ when ODIN_OS == .Windows { wctrans_t :: distinct int wctype_t :: distinct u32 -} else when ODIN_OS == .OpenBSD { +} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { wctrans_t :: distinct rawptr wctype_t :: distinct rawptr diff --git a/core/os/stream.odin b/core/os/stream.odin index 4e73284f0..9a168b95c 100644 --- a/core/os/stream.odin +++ b/core/os/stream.odin @@ -60,7 +60,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, case .Destroy: err = .Empty case .Query: - when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku { + when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku { return io.query_utility({.Close, .Flush, .Read, .Write, .Seek, .Size, .Query}) } else { return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query}) From dd95a8d11ddf330245ba0f98a3920e4d71e25cde Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Fri, 19 Apr 2024 10:42:09 +0200 Subject: [PATCH 07/36] More std lib fixes Just selecting the same codepath as other BSD's for the most part. --- core/c/libc/stdio.odin | 2 +- core/net/dns.odin | 4 ++-- core/prof/spall/spall_unix.odin | 2 +- core/sys/info/cpu_intel.odin | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin index b83ddecc8..f17d3bd06 100644 --- a/core/c/libc/stdio.odin +++ b/core/c/libc/stdio.odin @@ -83,7 +83,7 @@ when ODIN_OS == .Linux { } } -when ODIN_OS == .OpenBSD { +when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { fpos_t :: distinct i64 _IOFBF :: 0 diff --git a/core/net/dns.odin b/core/net/dns.odin index 48cd8bf29..408fd201b 100644 --- a/core/net/dns.odin +++ b/core/net/dns.odin @@ -30,7 +30,7 @@ when ODIN_OS == .Windows { resolv_conf = "", hosts_file = "%WINDIR%\\system32\\drivers\\etc\\hosts", } -} else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD { +} else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { DEFAULT_DNS_CONFIGURATION :: DNS_Configuration{ resolv_conf = "/etc/resolv.conf", hosts_file = "/etc/hosts", @@ -854,4 +854,4 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator } return _records[:], true -} \ No newline at end of file +} diff --git a/core/prof/spall/spall_unix.odin b/core/prof/spall/spall_unix.odin index 7915f8c32..e6199d86b 100644 --- a/core/prof/spall/spall_unix.odin +++ b/core/prof/spall/spall_unix.odin @@ -1,5 +1,5 @@ //+private -//+build darwin, freebsd, openbsd +//+build darwin, freebsd, openbsd, netbsd package spall // Only for types. diff --git a/core/sys/info/cpu_intel.odin b/core/sys/info/cpu_intel.odin index 2b8f9852f..8e56b8e79 100644 --- a/core/sys/info/cpu_intel.odin +++ b/core/sys/info/cpu_intel.odin @@ -67,7 +67,7 @@ init_cpu_features :: proc "c" () { try_set(&set, .os_xsave, 27, ecx1) try_set(&set, .rdrand, 30, ecx1) - when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD { + when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { // xgetbv is an illegal instruction under FreeBSD 13 & OpenBSD 7.1 // return before probing further cpu_features = set @@ -133,4 +133,4 @@ init_cpu_name :: proc "c" () { brand = brand[:len(brand) - 1] } cpu_name = brand -} \ No newline at end of file +} From a23c37851387c2a9d45025a0987da44ee1bc3306 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Fri, 19 Apr 2024 12:32:09 +0200 Subject: [PATCH 08/36] Make the linter happy --- core/sync/futex_netbsd.odin | 1 - core/sync/primitives_netbsd.odin | 1 - core/sys/unix/pthread_netbsd.odin | 21 ++++++++++----------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/core/sync/futex_netbsd.odin b/core/sync/futex_netbsd.odin index f82af77d6..4f52e952f 100644 --- a/core/sync/futex_netbsd.odin +++ b/core/sync/futex_netbsd.odin @@ -4,7 +4,6 @@ package sync import "core:c" import "core:time" import "core:sys/unix" -import "base:runtime" @(private="file") Wait_Node :: struct { diff --git a/core/sync/primitives_netbsd.odin b/core/sync/primitives_netbsd.odin index 2d5b6b7fe..042e744e8 100644 --- a/core/sync/primitives_netbsd.odin +++ b/core/sync/primitives_netbsd.odin @@ -1,4 +1,3 @@ -//+build netbsd //+private package sync diff --git a/core/sys/unix/pthread_netbsd.odin b/core/sys/unix/pthread_netbsd.odin index c334fa56c..4cfe3c85c 100644 --- a/core/sys/unix/pthread_netbsd.odin +++ b/core/sys/unix/pthread_netbsd.odin @@ -1,4 +1,3 @@ -//+build netbsd package unix import "core:c" @@ -19,39 +18,39 @@ PTHREAD_BARRIER_T_SIZE :: 48 PTHREAD_ATTR_T_SIZE :: 16 pthread_cond_t :: struct #align(16) { - _: [PTHREAD_COND_T_SIZE] c.char + _: [PTHREAD_COND_T_SIZE] c.char, } pthread_mutex_t :: struct #align(16) { - _: [PTHREAD_MUTEX_T_SIZE] c.char + _: [PTHREAD_MUTEX_T_SIZE] c.char, } pthread_rwlock_t :: struct #align(16) { - _: [PTHREAD_RWLOCK_T_SIZE] c.char + _: [PTHREAD_RWLOCK_T_SIZE] c.char, } pthread_barrier_t :: struct #align(16) { - _: [PTHREAD_BARRIER_T_SIZE] c.char + _: [PTHREAD_BARRIER_T_SIZE] c.char, } pthread_attr_t :: struct #align(16) { - _: [PTHREAD_ATTR_T_SIZE] c.char + _: [PTHREAD_ATTR_T_SIZE] c.char, } pthread_condattr_t :: struct #align(16) { - _: [PTHREAD_CONDATTR_T_SIZE] c.char + _: [PTHREAD_CONDATTR_T_SIZE] c.char, } pthread_mutexattr_t :: struct #align(16) { - _: [PTHREAD_MUTEXATTR_T_SIZE] c.char + _: [PTHREAD_MUTEXATTR_T_SIZE] c.char, } pthread_rwlockattr_t :: struct #align(16) { - _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char + _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char, } pthread_barrierattr_t :: struct #align(16) { - _: [PTHREAD_BARRIERATTR_T_SIZE] c.char + _: [PTHREAD_BARRIERATTR_T_SIZE] c.char, } PTHREAD_MUTEX_NORMAL :: 0 @@ -75,7 +74,7 @@ sched_param :: struct { } sem_t :: struct #align(16) { - _: [SEM_T_SIZE] c.char + _: [SEM_T_SIZE] c.char, } PTHREAD_CANCEL_ENABLE :: 0 From 50cbb802b71630ee695e87a7042840180eec120e Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Fri, 19 Apr 2024 14:44:35 +0200 Subject: [PATCH 09/36] Fixed bug report information for NetBSD --- src/bug_report.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/bug_report.cpp b/src/bug_report.cpp index e919ba67b..b514df3eb 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -17,7 +17,7 @@ #include #endif -#if defined(GB_SYSTEM_OPENBSD) +#if defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD) #include #include #endif @@ -250,7 +250,7 @@ gb_internal void report_ram_info() { if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); } - #elif defined(GB_SYSTEM_OPENBSD) + #elif defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD) uint64_t ram_amount; size_t val_size = sizeof(ram_amount); @@ -985,13 +985,17 @@ gb_internal void report_os_info() { gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch); return; } - #elif defined(GB_SYSTEM_OPENBSD) + #elif defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD) struct utsname un; if (uname(&un) != -1) { gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine); } else { - gb_printf("OpenBSD: Unknown\n"); + #if defined(GB_SYSTEM_NETBSD) + gb_printf("NetBSD: Unknown\n"); + #else + gb_printf("OpenBSD: Unknown\n"); + #endif } #elif defined(GB_SYSTEM_FREEBSD) #define freebsd_version_buffer 129 From 1b15d8b4537a8568ddb6eaae5a8ecde383c49d2f Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Mon, 22 Apr 2024 09:32:19 +0200 Subject: [PATCH 10/36] Added build tag Added build tag to rand_bsd.odin and fixed build warning. --- core/crypto/rand_bsd.odin | 2 +- src/bug_report.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/crypto/rand_bsd.odin b/core/crypto/rand_bsd.odin index 61eaf652f..658221769 100644 --- a/core/crypto/rand_bsd.odin +++ b/core/crypto/rand_bsd.odin @@ -1,4 +1,4 @@ -//+build freebsd, openbsd +//+build freebsd, openbsd, netbsd package crypto foreign import libc "system:c" diff --git a/src/bug_report.cpp b/src/bug_report.cpp index b514df3eb..2262779c1 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -250,7 +250,7 @@ gb_internal void report_ram_info() { if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); } - #elif defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD) + #elif defined(GB_SYSTEM_OPENBSD) uint64_t ram_amount; size_t val_size = sizeof(ram_amount); @@ -258,7 +258,7 @@ gb_internal void report_ram_info() { if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); } - #elif defined(GB_SYSTEM_FREEBSD) + #elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_NETBSD) uint64_t ram_amount; size_t val_size = sizeof(ram_amount); From 384137d4e9958044e200f42ac346130bca20b36c Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Mon, 22 Apr 2024 10:15:52 +0200 Subject: [PATCH 11/36] Use PHYSMEM64 on NetBSD I realize we should use PHYSMEM64 on NetBSD. So we can not share code with FreeBSD. --- src/bug_report.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/bug_report.cpp b/src/bug_report.cpp index 2262779c1..0ec383b44 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -250,6 +250,14 @@ gb_internal void report_ram_info() { if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); } + #elif defined(GB_SYSTEM_NETBSD) + uint64_t ram_amount; + size_t val_size = sizeof(ram_amount); + + int mibs[] = { CTL_HW, HW_PHYSMEM64 }; + if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { + gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1)); + } #elif defined(GB_SYSTEM_OPENBSD) uint64_t ram_amount; size_t val_size = sizeof(ram_amount); @@ -258,7 +266,7 @@ gb_internal void report_ram_info() { if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); } - #elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_NETBSD) + #elif defined(GB_SYSTEM_FREEBSD) uint64_t ram_amount; size_t val_size = sizeof(ram_amount); From 8ffe577a150ae5d13adc2ad1826e0001fb1fcc78 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Thu, 25 Apr 2024 09:43:54 +0200 Subject: [PATCH 12/36] Added missing build tags in core --- core/crypto/rand_generic.odin | 1 + core/mem/virtual/virtual_bsd.odin | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/crypto/rand_generic.odin b/core/crypto/rand_generic.odin index 006ca51fe..cab76418b 100644 --- a/core/crypto/rand_generic.odin +++ b/core/crypto/rand_generic.odin @@ -2,6 +2,7 @@ //+build !windows //+build !openbsd //+build !freebsd +//+build !netbsd //+build !darwin //+build !js package crypto diff --git a/core/mem/virtual/virtual_bsd.odin b/core/mem/virtual/virtual_bsd.odin index 7568084c0..12e7818ef 100644 --- a/core/mem/virtual/virtual_bsd.odin +++ b/core/mem/virtual/virtual_bsd.odin @@ -1,9 +1,7 @@ -//+build freebsd, openbsd +//+build freebsd, openbsd, netbsd //+private package mem_virtual - - _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { return nil, nil } @@ -11,16 +9,18 @@ _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Err _commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error { return nil } + _decommit :: proc "contextless" (data: rawptr, size: uint) { } + _release :: proc "contextless" (data: rawptr, size: uint) { } + _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool { return false } _platform_memory_init :: proc() { - } _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { From 642391eb4946e690f8c6765ed0bb37857e8eb64c Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Thu, 25 Apr 2024 09:45:28 +0200 Subject: [PATCH 13/36] Implemented absolute_path_from_handle for NetBSD --- core/os/os_netbsd.odin | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index c72c6d4ab..bb89a4a50 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -176,6 +176,8 @@ RTLD_TRACE :: 0x200 RTLD_NODELETE :: 0x01000 RTLD_NOLOAD :: 0x02000 +F_GETPATH :: 15 + MAX_PATH :: 1024 MAXNAMLEN :: 511 @@ -188,7 +190,7 @@ Unix_File_Time :: struct { dev_t :: u64 ino_t :: u64 -nlink_t :: u64 +nlink_t :: u32 off_t :: i64 mode_t :: u16 pid_t :: u32 @@ -300,6 +302,7 @@ foreign libc { @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- + @(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int --- @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- @@ -490,6 +493,14 @@ exists :: proc(path: string) -> bool { return res == 0 } +fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { + result := _unix_fcntl(Handle(fd), c.int(cmd), uintptr(arg)) + if result < 0 { + return 0, Errno(get_last_error()) + } + return int(result), ERROR_NONE +} + // NOTE(bill): Uses startup to initialize it stdin: Handle = 0 @@ -618,7 +629,14 @@ _readlink :: proc(path: string) -> (string, Errno) { } absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { - return "", Errno(ENOSYS) + buf: [MAX_PATH]byte + _, err := fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0]))) + if err != ERROR_NONE { + return "", err + } + + path := strings.clone_from_cstring(cstring(&buf[0])) + return path, err } absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { From ce80c37c751e2c59151ad5bcbb6137c560d72d64 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Thu, 25 Apr 2024 11:04:44 +0200 Subject: [PATCH 14/36] Fixed potential memory leak --- core/os/os_netbsd.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index bb89a4a50..37b0ab54a 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -391,7 +391,7 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { } file_size :: proc(fd: Handle) -> (i64, Errno) { - s, err := fstat(fd) + s, err := _fstat(fd) if err != ERROR_NONE { return -1, err } From 3000508c027c9d30c168266d0ae276cc14de3982 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Thu, 25 Apr 2024 21:50:34 +0200 Subject: [PATCH 15/36] Switched to native futex on NetBSD --- core/sync/futex_netbsd.odin | 189 ++++++++--------------------- core/sys/unix/signal_netbsd.odin | 30 ----- core/sys/unix/syscalls_netbsd.odin | 3 + src/threading.cpp | 13 +- 4 files changed, 61 insertions(+), 174 deletions(-) delete mode 100644 core/sys/unix/signal_netbsd.odin create mode 100644 core/sys/unix/syscalls_netbsd.odin diff --git a/core/sync/futex_netbsd.odin b/core/sync/futex_netbsd.odin index 4f52e952f..06ff00a98 100644 --- a/core/sync/futex_netbsd.odin +++ b/core/sync/futex_netbsd.odin @@ -1,165 +1,74 @@ //+private package sync -import "core:c" +import "base:intrinsics" import "core:time" +import "core:c" import "core:sys/unix" -@(private="file") -Wait_Node :: struct { - thread: unix.pthread_t, - futex: ^Futex, - prev, next: ^Wait_Node, +foreign import libc "system:c" + +FUTEX_PRIVATE_FLAG :: 128 + +FUTEX_WAIT_PRIVATE :: 0 | FUTEX_PRIVATE_FLAG +FUTEX_WAKE_PRIVATE :: 1 | FUTEX_PRIVATE_FLAG + +EINTR :: 4 /* Interrupted system call */ +EAGAIN :: 35 /* Resource temporarily unavailable */ +ETIMEDOUT :: 60 /* Operation timed out */ + +Time_Spec :: struct { + time_sec: uint, + time_nsec: uint, } -@(private="file") -atomic_flag :: distinct bool -@(private="file") -Wait_Queue :: struct { - lock: atomic_flag, - list: Wait_Node, -} -@(private="file") -waitq_lock :: proc "contextless" (waitq: ^Wait_Queue) { - for cast(bool)atomic_exchange_explicit(&waitq.lock, atomic_flag(true), .Acquire) { - cpu_relax() // spin... + +get_last_error :: proc "contextless" () -> int { + foreign libc { + __errno :: proc() -> ^c.int --- } -} -@(private="file") -waitq_unlock :: proc "contextless" (waitq: ^Wait_Queue) { - atomic_store_explicit(&waitq.lock, atomic_flag(false), .Release) + return int(__errno()^) } -// FIXME: This approach may scale badly in the future, -// possible solution - hash map (leads to deadlocks now). -@(private="file") -g_waitq: Wait_Queue - -@(init, private="file") -g_waitq_init :: proc() { - g_waitq = { - list = { - prev = &g_waitq.list, - next = &g_waitq.list, - }, +_futex_wait :: proc "contextless" (futex: ^Futex, expected: u32) -> bool { + if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), 0) == -1 { + switch get_last_error() { + case EINTR, EAGAIN: + return true + case: + _panic("futex_wait failure") + } } + return true } -@(private="file") -get_waitq :: #force_inline proc "contextless" (f: ^Futex) -> ^Wait_Queue { - _ = f - return &g_waitq -} - -_futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) { - waitq := get_waitq(f) - waitq_lock(waitq) - defer waitq_unlock(waitq) - - head := &waitq.list - waiter := Wait_Node{ - thread = unix.pthread_self(), - futex = f, - prev = head, - next = head.next, - } - - waiter.prev.next = &waiter - waiter.next.prev = &waiter - - old_mask, mask: unix.sigset_t - unix.sigemptyset(&mask) - unix.sigaddset(&mask, unix.SIGCONT) - unix.pthread_sigmask(unix.SIG_BLOCK, &mask, &old_mask) - - if u32(atomic_load_explicit(f, .Acquire)) == expect { - waitq_unlock(waitq) - defer waitq_lock(waitq) - - sig: c.int - unix.sigwait(&mask, &sig) - errno := unix.errno() - ok = errno == unix.ERROR_NONE - } - - waiter.prev.next = waiter.next - waiter.next.prev = waiter.prev - - unix.pthread_sigmask(unix.SIG_SETMASK, &old_mask, nil) - - // FIXME: Add error handling! - return -} - -_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> (ok: bool) { +_futex_wait_with_timeout :: proc "contextless" (futex: ^Futex, expected: u32, duration: time.Duration) -> bool { if duration <= 0 { return false } - waitq := get_waitq(f) - waitq_lock(waitq) - defer waitq_unlock(waitq) - - head := &waitq.list - waiter := Wait_Node{ - thread = unix.pthread_self(), - futex = f, - prev = head, - next = head.next, - } - - waiter.prev.next = &waiter - waiter.next.prev = &waiter - - old_mask, mask: unix.sigset_t - unix.sigemptyset(&mask) - unix.sigaddset(&mask, unix.SIGCONT) - unix.pthread_sigmask(unix.SIG_BLOCK, &mask, &old_mask) - - if u32(atomic_load_explicit(f, .Acquire)) == expect { - waitq_unlock(waitq) - defer waitq_lock(waitq) - - info: unix.siginfo_t - ts := unix.timespec{ - tv_sec = i64(duration / 1e9), - tv_nsec = i64(duration % 1e9), + if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), cast(uintptr) &Time_Spec{ + time_sec = cast(uint)(duration / 1e9), + time_nsec = cast(uint)(duration % 1e9), + }) == -1 { + switch get_last_error() { + case EINTR, EAGAIN: + return true + case ETIMEDOUT: + return false + case: + _panic("futex_wait_with_timeout failure") } - unix.sigtimedwait(&mask, &info, &ts) - errno := unix.errno() - ok = errno == unix.EAGAIN || errno == unix.ERROR_NONE } - - waiter.prev.next = waiter.next - waiter.next.prev = waiter.prev - - unix.pthread_sigmask(unix.SIG_SETMASK, &old_mask, nil) - - // FIXME: Add error handling! - return + return true } -_futex_signal :: proc "contextless" (f: ^Futex) { - waitq := get_waitq(f) - waitq_lock(waitq) - defer waitq_unlock(waitq) - - head := &waitq.list - for waiter := head.next; waiter != head; waiter = waiter.next { - if waiter.futex == f { - unix.pthread_kill(waiter.thread, unix.SIGCONT) - break - } +_futex_signal :: proc "contextless" (futex: ^Futex) { + if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, 1) == -1 { + _panic("futex_wake_single failure") } } -_futex_broadcast :: proc "contextless" (f: ^Futex) { - waitq := get_waitq(f) - waitq_lock(waitq) - defer waitq_unlock(waitq) - - head := &waitq.list - for waiter := head.next; waiter != head; waiter = waiter.next { - if waiter.futex == f { - unix.pthread_kill(waiter.thread, unix.SIGCONT) - } +_futex_broadcast :: proc "contextless" (futex: ^Futex) { + if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, uintptr(max(i32))) == -1 { + _panic("_futex_wake_all failure") } } diff --git a/core/sys/unix/signal_netbsd.odin b/core/sys/unix/signal_netbsd.odin deleted file mode 100644 index c32f0bfbe..000000000 --- a/core/sys/unix/signal_netbsd.odin +++ /dev/null @@ -1,30 +0,0 @@ -package unix - -import "core:c" - -foreign import libc "system:c" - -ERROR_NONE :: 0 -EAGAIN :: 35 - -SIGCONT :: 19 - -SIG_BLOCK :: 1 -SIG_UNBLOCK :: 2 -SIG_SETMASK :: 3 - -siginfo_t :: struct { _: [128]u8 } -sigset_t :: struct { _: [4]u32 } - -foreign libc { - @(link_name="__sigemptyset14") sigemptyset :: proc(set: ^sigset_t) -> c.int --- - @(link_name="__sigaddset14") sigaddset :: proc(set: ^sigset_t, _signal: c.int) -> c.int --- - @(link_name="__sigtimedwait50") sigtimedwait :: proc(set: ^sigset_t, info: ^siginfo_t, timeout: ^timespec) -> c.int --- - @(link_name="sigwait") sigwait :: proc(set: ^sigset_t, _signal: ^c.int) -> c.int --- - - @(private="file", link_name="__errno") get_error_location :: proc() -> ^c.int --- -} - -errno :: #force_inline proc "contextless" () -> int { - return int(get_error_location()^) -} diff --git a/core/sys/unix/syscalls_netbsd.odin b/core/sys/unix/syscalls_netbsd.odin new file mode 100644 index 000000000..e92dc6d92 --- /dev/null +++ b/core/sys/unix/syscalls_netbsd.odin @@ -0,0 +1,3 @@ +package unix + +SYS___futex : uintptr : 166 diff --git a/src/threading.cpp b/src/threading.cpp index 79ed8e8a4..b2cfa6d8e 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -634,9 +634,15 @@ gb_internal void thread_set_name(Thread *t, char const *name) { #endif } -#if defined(GB_SYSTEM_LINUX) -#include +#if defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_NETBSD) + #include +#ifdef GB_SYSTEM_LINUX + #include +#else + #include + #define SYS_futex SYS___futex +#endif gb_internal void futex_signal(Futex *addr) { int ret = syscall(SYS_futex, addr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, NULL, NULL, 0); @@ -903,11 +909,10 @@ gb_internal void futex_wait(Futex *f, Footex val) { } while (f->load() == val); } -#elif defined(GB_SYSTEM_HAIKU) || defined(GB_SYSTEM_NETBSD) +#elif defined(GB_SYSTEM_HAIKU) // Futex implementation taken from https://tavianator.com/2023/futex.html -#include #include #include From 22fa420c4f655ab9380b5384350be2e2281b39a6 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Thu, 25 Apr 2024 22:22:59 +0200 Subject: [PATCH 16/36] Should pass 0 as the rest of futex arguments --- core/sync/futex_netbsd.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/sync/futex_netbsd.odin b/core/sync/futex_netbsd.odin index 06ff00a98..08f7cd6c9 100644 --- a/core/sync/futex_netbsd.odin +++ b/core/sync/futex_netbsd.odin @@ -30,7 +30,7 @@ get_last_error :: proc "contextless" () -> int { } _futex_wait :: proc "contextless" (futex: ^Futex, expected: u32) -> bool { - if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), 0) == -1 { + if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), 0, 0, 0) == -1 { switch get_last_error() { case EINTR, EAGAIN: return true @@ -48,7 +48,7 @@ _futex_wait_with_timeout :: proc "contextless" (futex: ^Futex, expected: u32, du if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), cast(uintptr) &Time_Spec{ time_sec = cast(uint)(duration / 1e9), time_nsec = cast(uint)(duration % 1e9), - }) == -1 { + }, 0, 0) == -1 { switch get_last_error() { case EINTR, EAGAIN: return true @@ -62,13 +62,13 @@ _futex_wait_with_timeout :: proc "contextless" (futex: ^Futex, expected: u32, du } _futex_signal :: proc "contextless" (futex: ^Futex) { - if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, 1) == -1 { + if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, 1, 0, 0, 0) == -1 { _panic("futex_wake_single failure") } } _futex_broadcast :: proc "contextless" (futex: ^Futex) { - if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, uintptr(max(i32))) == -1 { + if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, uintptr(max(i32)), 0, 0, 0) == -1 { _panic("_futex_wake_all failure") } } From 6bbdbb4447b0a2b5b485ae4351016b05ae79758f Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Fri, 26 Apr 2024 11:04:27 +0200 Subject: [PATCH 17/36] Added missing core:sys/info package for NetBSD --- ...latform_openbsd.odin => platform_bsd.odin} | 12 +++-- core/sys/unix/sysctl_netbsd.odin | 44 +++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) rename core/sys/info/{platform_openbsd.odin => platform_bsd.odin} (89%) create mode 100644 core/sys/unix/sysctl_netbsd.odin diff --git a/core/sys/info/platform_openbsd.odin b/core/sys/info/platform_bsd.odin similarity index 89% rename from core/sys/info/platform_openbsd.odin rename to core/sys/info/platform_bsd.odin index 772531ceb..3826129d1 100644 --- a/core/sys/info/platform_openbsd.odin +++ b/core/sys/info/platform_bsd.odin @@ -1,4 +1,4 @@ -// +build openbsd +//+build openbsd, netbsd package sysinfo import sys "core:sys/unix" @@ -11,12 +11,16 @@ version_string_buf: [1024]u8 @(init, private) init_os_version :: proc () { - os_version.platform = .OpenBSD + when ODIN_OS == .NetBSD { + os_version.platform = .NetBSD + } else { + os_version.platform = .OpenBSD + } kernel_version_buf: [1024]u8 b := strings.builder_from_bytes(version_string_buf[:]) - // Retrieve kernel info using `sysctl`, e.g. OpenBSD + // Retrieve kernel info using `sysctl`, e.g. OpenBSD and NetBSD mib := []i32{sys.CTL_KERN, sys.KERN_OSTYPE} if !sys.sysctl(mib, &kernel_version_buf) { return @@ -69,4 +73,4 @@ init_ram :: proc() { if sys.sysctl(mib, &mem_size) { ram.total_ram = int(mem_size) } -} \ No newline at end of file +} diff --git a/core/sys/unix/sysctl_netbsd.odin b/core/sys/unix/sysctl_netbsd.odin new file mode 100644 index 000000000..ad89b9ad4 --- /dev/null +++ b/core/sys/unix/sysctl_netbsd.odin @@ -0,0 +1,44 @@ +package unix + +import "core:c" +foreign import libc "system:c" + +@(default_calling_convention="c") +foreign libc { + @(link_name="sysctl") _unix_sysctl :: proc(name: [^]i32, namelen: u32, oldp: rawptr, oldlenp: ^c.size_t, newp: rawptr, newlen: c.size_t) -> i32 --- +} + +sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) { + mib := mib + result_size := c.size_t(size_of(T)) + res := _unix_sysctl(raw_data(mib), u32(len(mib)), val, &result_size, nil, 0) + return res == 0 +} + +// See /usr/include/sys/sysctl.h for details +CTL_KERN :: 1 + KERN_OSTYPE :: 1 + KERN_OSRELEASE :: 2 + KERN_OSREV :: 3 + KERN_VERSION :: 4 +CTL_VM :: 2 +CTL_FS :: 3 +CTL_NET :: 4 +CTL_DEBUG :: 5 +CTL_HW :: 6 + HW_MACHINE :: 1 + HW_MODEL :: 2 + HW_NCPU :: 3 + HW_BYTEORDER :: 4 + HW_PHYSMEM :: 5 + HW_USERMEM :: 6 + HW_PAGESIZE :: 7 + HW_DISKNAMES :: 8 + HW_IOSTATS :: 9 + HW_MACHINE_ARCH :: 10 + HW_ALIGNBYTES :: 11 + HW_CNMAGIC :: 12 + HW_PHYSMEM64 :: 13 + HW_USERMEM64 :: 14 + HW_IOSTATNAMES :: 15 + HW_NCPUONLINE :: 16 From f1491280abcd25aa0f7e87df747e97dc907df1eb Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Fri, 10 May 2024 08:16:10 +0200 Subject: [PATCH 18/36] Fixed invalid alignment --- core/sys/unix/pthread_netbsd.odin | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/sys/unix/pthread_netbsd.odin b/core/sys/unix/pthread_netbsd.odin index 4cfe3c85c..afbbc321c 100644 --- a/core/sys/unix/pthread_netbsd.odin +++ b/core/sys/unix/pthread_netbsd.odin @@ -17,39 +17,39 @@ PTHREAD_RWLOCK_T_SIZE :: 64 PTHREAD_BARRIER_T_SIZE :: 48 PTHREAD_ATTR_T_SIZE :: 16 -pthread_cond_t :: struct #align(16) { +pthread_cond_t :: struct #align(8) { _: [PTHREAD_COND_T_SIZE] c.char, } -pthread_mutex_t :: struct #align(16) { +pthread_mutex_t :: struct #align(8) { _: [PTHREAD_MUTEX_T_SIZE] c.char, } -pthread_rwlock_t :: struct #align(16) { +pthread_rwlock_t :: struct #align(8) { _: [PTHREAD_RWLOCK_T_SIZE] c.char, } -pthread_barrier_t :: struct #align(16) { +pthread_barrier_t :: struct #align(8) { _: [PTHREAD_BARRIER_T_SIZE] c.char, } -pthread_attr_t :: struct #align(16) { +pthread_attr_t :: struct #align(8) { _: [PTHREAD_ATTR_T_SIZE] c.char, } -pthread_condattr_t :: struct #align(16) { +pthread_condattr_t :: struct #align(8) { _: [PTHREAD_CONDATTR_T_SIZE] c.char, } -pthread_mutexattr_t :: struct #align(16) { +pthread_mutexattr_t :: struct #align(8) { _: [PTHREAD_MUTEXATTR_T_SIZE] c.char, } -pthread_rwlockattr_t :: struct #align(16) { +pthread_rwlockattr_t :: struct #align(8) { _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char, } -pthread_barrierattr_t :: struct #align(16) { +pthread_barrierattr_t :: struct #align(8) { _: [PTHREAD_BARRIERATTR_T_SIZE] c.char, } From 273e4c6b4ce6f1060870782c8e780fe2b371ede4 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Fri, 10 May 2024 08:47:51 +0200 Subject: [PATCH 19/36] Changed target data layout --- src/build_settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index b63f08980..77f16402b 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -564,7 +564,7 @@ gb_global TargetMetrics target_netbsd_amd64 = { TargetArch_amd64, 8, 8, 8, 16, str_lit("x86_64-unknown-netbsd-elf"), - str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), + 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 = { From a45e69e6564c2518c699fd58f8d9123cfa7496b0 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Fri, 10 May 2024 09:12:56 +0200 Subject: [PATCH 20/36] Removed dead code --- src/build_settings.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index dca57f73a..0fd5d504a 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1022,9 +1022,8 @@ gb_global TargetMetrics target_openbsd_amd64 = { gb_global TargetMetrics target_netbsd_amd64 = { TargetOs_netbsd, TargetArch_amd64, - 8, 8, 8, 16, + 8, 8, AMD64_MAX_ALIGNMENT, 16, str_lit("x86_64-unknown-netbsd-elf"), - 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 = { From f428e30211c3113ac7c7918f9a0f5c03cc7b4669 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Fri, 10 May 2024 17:49:56 +0200 Subject: [PATCH 21/36] Minor code cleanup --- core/os/os_netbsd.odin | 14 +++++++------- core/sys/info/cpu_intel.odin | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index 37b0ab54a..e8e551340 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -269,13 +269,13 @@ S_ISUID :: 0o4000 // Set user id on execution S_ISGID :: 0o2000 // Set group id on execution S_ISVTX :: 0o1000 // Directory restrcted delete -S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } -S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } -S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } -S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } -S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } -S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } -S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } +S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } +S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } +S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } +S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } +S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } +S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } +S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } F_OK :: 0 // Test for file existance X_OK :: 1 // Test for execute permission diff --git a/core/sys/info/cpu_intel.odin b/core/sys/info/cpu_intel.odin index 8e56b8e79..63af853d0 100644 --- a/core/sys/info/cpu_intel.odin +++ b/core/sys/info/cpu_intel.odin @@ -68,7 +68,7 @@ init_cpu_features :: proc "c" () { try_set(&set, .rdrand, 30, ecx1) when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { - // xgetbv is an illegal instruction under FreeBSD 13 & OpenBSD 7.1 + // xgetbv is an illegal instruction under FreeBSD 13, OpenBSD 7.1 and NetBSD 10 // return before probing further cpu_features = set return From 88528f76133304eeddd2f8046df304e25e6e98b1 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Mon, 13 May 2024 09:41:54 +0200 Subject: [PATCH 22/36] Updated makefiles to exclude some tests on BSDs --- tests/core/Makefile | 52 +++++++++++++++++++++-------------------- tests/internal/Makefile | 6 +++-- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/tests/core/Makefile b/tests/core/Makefile index 8996e2241..9026ed3d9 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -3,30 +3,32 @@ PYTHON=$(shell which python3) COMMON=-vet -strict-style COLLECTION=-collection:tests=.. -all: c_libc_test \ - compress_test \ - container_test \ - crypto_test \ - download_test_assets \ - encoding_test \ - filepath_test \ - fmt_test \ - hash_test \ - i18n_test \ - image_test \ - linalg_glsl_math_test \ - match_test \ - math_test \ - net_test \ - noise_test \ - os_exit_test \ - reflect_test \ - slice_test \ - strings_test \ - thread_test \ - runtime_test \ - time_test \ - fmt_test +all: all_bsd \ + net_test + +all_bsd: c_libc_test \ + compress_test \ + container_test \ + crypto_test \ + download_test_assets \ + encoding_test \ + filepath_test \ + fmt_test \ + hash_test \ + i18n_test \ + image_test \ + linalg_glsl_math_test \ + match_test \ + math_test \ + noise_test \ + os_exit_test \ + reflect_test \ + slice_test \ + strings_test \ + thread_test \ + runtime_test \ + time_test \ + fmt_test download_test_assets: $(PYTHON) download_assets.py @@ -101,4 +103,4 @@ runtime_test: $(ODIN) run runtime $(COMMON) -out:test_core_runtime time_test: - $(ODIN) run time $(COMMON) -out:test_core_time \ No newline at end of file + $(ODIN) run time $(COMMON) -out:test_core_time diff --git a/tests/internal/Makefile b/tests/internal/Makefile index d17b9d012..09182cd23 100644 --- a/tests/internal/Makefile +++ b/tests/internal/Makefile @@ -1,6 +1,8 @@ ODIN=../../odin -all: rtti_test map_test pow_test 128_test asan_test string_compare_test +all: all_bsd asan_test + +all_bsd: rtti_test map_test pow_test 128_test string_compare_test rtti_test: $(ODIN) run test_rtti.odin -file -vet -strict-style -o:minimal @@ -18,4 +20,4 @@ asan_test: $(ODIN) run test_asan.odin -file -sanitize:address -debug string_compare_test: - $(ODIN) run test_string_compare.odin -file -vet -strict-style -o:minimal \ No newline at end of file + $(ODIN) run test_string_compare.odin -file -vet -strict-style -o:minimal From 6298d4a36c484e0a78dd61a9b1bf760dbae1969a Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Mon, 13 May 2024 10:00:19 +0200 Subject: [PATCH 23/36] Fixed some build tags in vendor libs --- core/dynlib/lib_unix.odin | 2 +- vendor/ENet/unix.odin | 2 +- vendor/directx/dxc/dxcdef_unix.odin | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/dynlib/lib_unix.odin b/core/dynlib/lib_unix.odin index 97f70b576..fb0270ea3 100644 --- a/core/dynlib/lib_unix.odin +++ b/core/dynlib/lib_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd +//+build linux, darwin, freebsd, openbsd, netbsd //+private package dynlib diff --git a/vendor/ENet/unix.odin b/vendor/ENet/unix.odin index 05ce41e05..0bbfce20a 100644 --- a/vendor/ENet/unix.odin +++ b/vendor/ENet/unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd +//+build linux, darwin, freebsd, openbsd, netbsd package ENet // When we implement the appropriate bindings for Unix, the section separated diff --git a/vendor/directx/dxc/dxcdef_unix.odin b/vendor/directx/dxc/dxcdef_unix.odin index 12a682310..530d03ff0 100644 --- a/vendor/directx/dxc/dxcdef_unix.odin +++ b/vendor/directx/dxc/dxcdef_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd +//+build linux, darwin, freebsd, openbsd, netbsd package directx_dxc import "core:c" From ecd7846ec31f4e582a4bd0bb6fc0d8ce9ea258bf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 17:10:53 +0100 Subject: [PATCH 24/36] Clean up allocator stuff into `allocators.odin` --- core/os/os2/allocators.odin | 46 +++++++++++++++++++++++++++ core/os/os2/env_windows.odin | 8 ++--- core/os/os2/file.odin | 6 ++-- core/os/os2/file_linux.odin | 8 ++--- core/os/os2/file_windows.odin | 51 +++--------------------------- core/os/os2/path_windows.odin | 14 ++++---- core/os/os2/stat_windows.odin | 14 ++++---- core/os/os2/temp_file_windows.odin | 4 +-- 8 files changed, 75 insertions(+), 76 deletions(-) create mode 100644 core/os/os2/allocators.odin diff --git a/core/os/os2/allocators.odin b/core/os/os2/allocators.odin new file mode 100644 index 000000000..c044e4522 --- /dev/null +++ b/core/os/os2/allocators.odin @@ -0,0 +1,46 @@ +//+private +package os2 + +import "base:runtime" + +file_allocator :: proc() -> runtime.Allocator { + return heap_allocator() +} + +temp_allocator_proc :: runtime.arena_allocator_proc + +@(private="file", thread_local) +global_default_temp_allocator_arena: runtime.Arena + +temp_allocator :: proc() -> runtime.Allocator { + return runtime.Allocator{ + procedure = temp_allocator_proc, + data = &global_default_temp_allocator_arena, + } +} + +@(require_results) +temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: runtime.Arena_Temp) { + temp = runtime.arena_temp_begin(&global_default_temp_allocator_arena, loc) + return +} + +temp_allocator_temp_end :: proc(temp: runtime.Arena_Temp, loc := #caller_location) { + runtime.arena_temp_end(temp, loc) +} + +@(fini, private) +temp_allocator_fini :: proc() { + runtime.arena_destroy(&global_default_temp_allocator_arena) + global_default_temp_allocator_arena = {} +} + +@(deferred_out=temp_allocator_temp_end) +TEMP_ALLOCATOR_GUARD :: #force_inline proc(ignore := false, loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) { + if ignore { + return {}, loc + } else { + return temp_allocator_temp_begin(loc), loc + } +} + diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin index 774af9e8f..39694b821 100644 --- a/core/os/os2/env_windows.odin +++ b/core/os/os2/env_windows.odin @@ -19,9 +19,9 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string return "", true } - _TEMP_ALLOCATOR_GUARD() + TEMP_ALLOCATOR_GUARD() - b := make([]u16, n+1, _temp_allocator()) + b := make([]u16, n+1, temp_allocator()) n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) if n == 0 { @@ -50,8 +50,8 @@ _unset_env :: proc(key: string) -> bool { } _clear_env :: proc() { - _TEMP_ALLOCATOR_GUARD() - envs := environ(_temp_allocator()) + TEMP_ALLOCATOR_GUARD() + envs := environ(temp_allocator()) for env in envs { for j in 1.. Error { return p, false, nil } - _TEMP_ALLOCATOR_GUARD() + TEMP_ALLOCATOR_GUARD() - dir, err := stat(path, _temp_allocator()) + dir, err := stat(path, temp_allocator()) if err == nil { if dir.is_directory { return nil @@ -54,14 +54,14 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { if j > 1 { new_path, allocated := fix_root_directory(path[:j-1]) or_return defer if allocated { - delete(new_path, _file_allocator()) + delete(new_path, file_allocator()) } mkdir_all(new_path, perm) or_return } err = mkdir(path, perm) if err != nil { - dir1, err1 := lstat(path, _temp_allocator()) + dir1, err1 := lstat(path, temp_allocator()) if err1 == nil && dir1.is_directory { return nil } @@ -127,10 +127,10 @@ _fix_long_path_internal :: proc(path: string) -> string { return path } - _TEMP_ALLOCATOR_GUARD() + TEMP_ALLOCATOR_GUARD() PREFIX :: `\\?` - path_buf := make([]byte, len(PREFIX)+len(path)+1, _temp_allocator()) + path_buf := make([]byte, len(PREFIX)+len(path)+1, temp_allocator()) copy(path_buf, PREFIX) n := len(path) r, w := 0, len(PREFIX) diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index 154a5bbe3..03ad2052f 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -46,15 +46,15 @@ full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path if name == "" { name = "." } - _TEMP_ALLOCATOR_GUARD() + TEMP_ALLOCATOR_GUARD() - p := win32.utf8_to_utf16(name, _temp_allocator()) + p := win32.utf8_to_utf16(name, temp_allocator()) n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil) if n == 0 { return "", _get_platform_error() } - buf := make([]u16, n+1, _temp_allocator()) + buf := make([]u16, n+1, temp_allocator()) n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) if n == 0 { return "", _get_platform_error() @@ -131,8 +131,8 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin if n == 0 { return "", _get_platform_error() } - _TEMP_ALLOCATOR_GUARD() - buf := make([]u16, max(n, 260)+1, _temp_allocator()) + TEMP_ALLOCATOR_GUARD() + buf := make([]u16, max(n, 260)+1, temp_allocator()) n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) return _cleanpath_from_buf(buf[:n], allocator) } @@ -147,8 +147,8 @@ _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) { if n == 0 { return nil, _get_platform_error() } - _TEMP_ALLOCATOR_GUARD() - buf := make([]u16, max(n, 260)+1, _temp_allocator()) + TEMP_ALLOCATOR_GUARD() + buf := make([]u16, max(n, 260)+1, temp_allocator()) n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) return _cleanpath_strip_prefix(buf[:n]), nil } diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin index c42da84f5..09b5675f2 100644 --- a/core/os/os2/temp_file_windows.odin +++ b/core/os/os2/temp_file_windows.odin @@ -17,9 +17,9 @@ _temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Er if n == 0 { return "", nil } - _TEMP_ALLOCATOR_GUARD() + TEMP_ALLOCATOR_GUARD() - b := make([]u16, max(win32.MAX_PATH, n), _temp_allocator()) + b := make([]u16, max(win32.MAX_PATH, n), temp_allocator()) n = win32.GetTempPathW(u32(len(b)), raw_data(b)) if n == 3 && b[1] == ':' && b[2] == '\\' { From 361be301fa1aec086de457ae01784c66304df4a9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 17:24:37 +0100 Subject: [PATCH 25/36] Use internal `temp_allocator()` --- core/os/os2/file_linux.odin | 59 ++++++++++++++++++++++------------- core/os/os2/file_windows.odin | 18 +++++++++-- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 13b5f2966..b1a6d84f5 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -36,8 +36,8 @@ _File :: struct { } _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) // Just default to using O_NOCTTY because needing to open a controlling // terminal would be incredibly rare. This has no effect on files while @@ -49,12 +49,12 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error case O_RDWR: flags_i = _O_RDWR } - flags_i |= (_O_APPEND * int(.Append in flags)) - flags_i |= (_O_CREAT * int(.Create in flags)) - flags_i |= (_O_EXCL * int(.Excl in flags)) - flags_i |= (_O_SYNC * int(.Sync in flags)) - flags_i |= (_O_TRUNC * int(.Trunc in flags)) - flags_i |= (_O_CLOEXEC * int(.Close_On_Exec in flags)) + if .Append in flags { flags_i |= _O_APPEND } + if .Create in flags { flags_i |= _O_CREAT } + if .Excl in flags { flags_i |= _O_EXCL } + if .Sync in flags { flags_i |= _O_SYNC } + if .Trunc in flags { flags_i |= _O_TRUNC } + if .Close_On_Exec in flags { flags_i |= _O_CLOEXEC } fd := unix.sys_open(name_cstr, flags_i, uint(perm)) if fd < 0 { @@ -87,8 +87,12 @@ _destroy :: proc(f: ^File) -> Error { _close :: proc(f: ^File) -> Error { - res := unix.sys_close(f.impl.fd) - return _ok_or_error(res) + if f != nil { + res := unix.sys_close(f.impl.fd) + _destroy(f) + return _ok_or_error(res) + } + return nil } _fd :: proc(f: ^File) -> uintptr { @@ -190,7 +194,8 @@ _truncate :: proc(f: ^File, size: i64) -> Error { } _remove :: proc(name: string) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) fd := unix.sys_open(name_cstr, int(File_Flags.Read)) if fd < 0 { @@ -244,17 +249,20 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (st } _read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _read_link_cstr(name_cstr, allocator) } _unlink :: proc(name: string) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _ok_or_error(unix.sys_unlink(name_cstr)) } _chdir :: proc(name: string) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _ok_or_error(unix.sys_chdir(name_cstr)) } @@ -263,7 +271,8 @@ _fchdir :: proc(f: ^File) -> Error { } _chmod :: proc(name: string, mode: File_Mode) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _ok_or_error(unix.sys_chmod(name_cstr, uint(mode))) } @@ -273,13 +282,15 @@ _fchmod :: proc(f: ^File, mode: File_Mode) -> Error { // NOTE: will throw error without super user priviledges _chown :: proc(name: string, uid, gid: int) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _ok_or_error(unix.sys_chown(name_cstr, uid, gid)) } // NOTE: will throw error without super user priviledges _lchown :: proc(name: string, uid, gid: int) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid)) } @@ -289,7 +300,8 @@ _fchown :: proc(f: ^File, uid, gid: int) -> Error { } _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) times := [2]Unix_File_Time { { atime._nsec, 0 }, { mtime._nsec, 0 }, @@ -306,12 +318,14 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { } _exists :: proc(name: string) -> bool { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return unix.sys_access(name_cstr, F_OK) == 0 } _is_file :: proc(name: string) -> bool { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) s: _Stat res := unix.sys_stat(name_cstr, &s) if res < 0 { @@ -330,7 +344,8 @@ _is_file_fd :: proc(fd: int) -> bool { } _is_dir :: proc(name: string) -> bool { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) s: _Stat res := unix.sys_stat(name_cstr, &s) if res < 0 { @@ -354,7 +369,7 @@ _is_dir_fd :: proc(fd: int) -> bool { // buffer. Therefor, any large paths will use context.allocator. @(private="file") _temp_name_to_cstring :: proc(name: string) -> (cname: cstring) { - return strings.clone_to_cstring(name, context.temp_allocator) + return strings.clone_to_cstring(name, temp_allocator()) } diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 4c0f62c7a..1984c9baf 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -36,6 +36,20 @@ _File :: struct { p_mutex: sync.Mutex, // pread pwrite calls } +@(init) +init_std_files :: proc() { + stdin = new_file(uintptr(win32.GetStdHandle(win32.STD_INPUT_HANDLE)), "") + stdout = new_file(uintptr(win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)), "") + stderr = new_file(uintptr(win32.GetStdHandle(win32.STD_ERROR_HANDLE)), "") +} +@(fini) +fini_std_files :: proc() { + close(stdin) + close(stdout) + close(stderr) +} + + _handle :: proc(f: ^File) -> win32.HANDLE { return win32.HANDLE(_fd(f)) } @@ -755,14 +769,12 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, ferr = _flush(f) err = error_to_io_error(ferr) return - case .Close: + case .Close, .Destroy: ferr = _close(f) err = error_to_io_error(ferr) return case .Query: return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query}) - case .Destroy: - return 0, .Empty } return 0, .Empty } From 91b7cdaad207511260c8cead669c276b255e7c78 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:11:50 +0100 Subject: [PATCH 26/36] Mock out `temp_file.odin` stuff --- core/os/os2/errors.odin | 3 + core/os/os2/file_linux.odin | 1 - core/os/os2/file_util.odin | 4 +- core/os/os2/file_windows.odin | 6 +- core/os/os2/path.odin | 5 +- core/os/os2/path_linux.odin | 5 +- core/os/os2/path_windows.odin | 5 +- core/os/os2/temp_file.odin | 181 ++++++++++++++++++++++++++++- core/os/os2/temp_file_linux.odin | 10 -- core/os/os2/temp_file_windows.odin | 8 -- 10 files changed, 194 insertions(+), 34 deletions(-) diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin index d76b2d549..2c570170d 100644 --- a/core/os/os2/errors.odin +++ b/core/os/os2/errors.odin @@ -23,6 +23,8 @@ General_Error :: enum u32 { Invalid_Dir, Invalid_Path, + Pattern_Has_Separator, + Unsupported, } @@ -63,6 +65,7 @@ error_string :: proc(ferr: Error) -> string { case .Invalid_Dir: return "invalid directory" case .Invalid_Path: return "invalid path" case .Unsupported: return "unsupported" + case .Pattern_Has_Separator: return "pattern has separator" } case io.Error: switch e { diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index b1a6d84f5..3f66bbd09 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -377,7 +377,6 @@ _temp_name_to_cstring :: proc(name: string) -> (cname: cstring) { _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { f := (^File)(stream_data) ferr: Error - i: int switch mode { case .Read: n, ferr = _read(f, p) diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 0708f708e..9ec75fc91 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -88,11 +88,11 @@ read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator) - read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (data: []byte, err: Error) { size: int has_size := true - if size64, err := file_size(f); err == nil { + if size64, serr := file_size(f); serr == nil { if i64(int(size64)) != size64 { size = int(size64) } - } else if err == .No_Size { + } else if serr == .No_Size { has_size = false } else { return diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 1984c9baf..1e0899992 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -282,10 +282,10 @@ _read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { to_read := min(win32.DWORD(length), MAX_RW) ok: win32.BOOL if f.impl.kind == .Console { - n, err := read_console(handle, p[total_read:][:to_read]) + n, cerr := read_console(handle, p[total_read:][:to_read]) total_read += n - if err != nil { - return i64(total_read), err + if cerr != nil { + return i64(total_read), cerr } } else { ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil) diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin index a3e7a5a96..277da56dd 100644 --- a/core/os/os2/path.odin +++ b/core/os/os2/path.odin @@ -2,8 +2,9 @@ package os2 import "base:runtime" -Path_Separator :: _Path_Separator // OS-Specific -Path_List_Separator :: _Path_List_Separator // OS-Specific +Path_Separator :: _Path_Separator // OS-Specific +Path_Separator_String :: _Path_Separator_String // OS-Specific +Path_List_Separator :: _Path_List_Separator // OS-Specific is_path_separator :: proc(c: byte) -> bool { return _is_path_separator(c) diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index 93de749b8..fde4ba5b1 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -6,8 +6,9 @@ import "core:strconv" import "base:runtime" import "core:sys/unix" -_Path_Separator :: '/' -_Path_List_Separator :: ':' +_Path_Separator :: '/' +_Path_Separator_String :: "/" +_Path_List_Separator :: ':' _S_IFMT :: 0o170000 // Type of file mask _S_IFIFO :: 0o010000 // Named pipe (fifo) diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index f3a45768d..064356cf1 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -5,8 +5,9 @@ import win32 "core:sys/windows" import "base:runtime" import "core:strings" -_Path_Separator :: '\\' -_Path_List_Separator :: ';' +_Path_Separator :: '\\' +_Path_Separator_String :: "\\" +_Path_List_Separator :: ';' _is_path_separator :: proc(c: byte) -> bool { return c == '\\' || c == '/' diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index f12c2800e..d415eb7f9 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -1,17 +1,190 @@ package os2 +import "base:intrinsics" import "base:runtime" -create_temp_file :: proc(dir, pattern: string) -> (^File, Error) { - return _create_temp(dir, pattern) +@(private="file") +MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right? + +// Creates a new temperatory file in the directory `dir`. +// +// Opens the file for reading and writing, with 0o666 permissions, and returns the new `^File`. +// The filename is generated by taking a pattern, and adding a randomized string to the end. +// If the pattern includes an "*", the randm string replaces the last "*". +// If `dir` is an empty tring, `temp_directory()` will be used. +// +// The caller must `close` the file once finished with. +create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) { + TEMP_ALLOCATOR_GUARD() + dir := dir if dir != "" else temp_directory(temp_allocator()) or_return + prefix, suffix := _prefix_and_suffix(pattern) or_return + prefix = temp_join_path(dir, prefix) + + rand_buf: [32]byte + name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator()) + + attempts := 0 + for { + name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix) + f, err = open(name, {.Read, .Write, .Create, .Excl}, File_Mode(0o666)) + if err == .Exist { + close(f) + attempts += 1 + if attempts < MAX_ATTEMPTS { + continue + } + return nil, err + } + return f, err + } } mkdir_temp :: make_directory_temp -make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { - return _mkdir_temp(dir, pattern, allocator) +// Creates a new temporary directory in the directory `dir`, and returns the path of the new directory. +// +// The directory name is generated by taking a pattern, and adding a randomized string to the end. +// If the pattern includes an "*", the randm string replaces the last "*". +// If `dir` is an empty tring, `temp_directory()` will be used. +make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (temp_path: string, err: Error) { + TEMP_ALLOCATOR_GUARD() + dir := dir if dir != "" else temp_directory(temp_allocator()) or_return + prefix, suffix := _prefix_and_suffix(pattern) or_return + prefix = temp_join_path(dir, prefix) + + rand_buf: [32]byte + name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator()) + + attempts := 0 + for { + name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix) + err = make_directory(name, 0o700) + if err == nil { + return clone_string(name, allocator), nil + } + if err == .Exist { + attempts += 1 + if attempts < MAX_ATTEMPTS { + continue + } + return "", err + } + if err == .Not_Exist { + if _, serr := stat(dir, temp_allocator()); serr == .Not_Exist { + return "", serr + } + } + return "", err + } + } temp_dir :: temp_directory temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) { return _temp_dir(allocator) } + + +// Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix +// parts which are split by the last "*" +@(private) +_prefix_and_suffix :: proc(pattern: string) -> (prefix, suffix: string, err: Error) { + for i in 0..= 0; i -= 1 { + if pattern[i] == '*' { + prefix, suffix = pattern[:i], pattern[i+1:] + break + } + } + return +} + +@(private) +clone_string :: proc(s: string, allocator: runtime.Allocator) -> string { + buf := make([]byte, len(s), allocator) + copy(buf, s) + return string(buf) +} + + +@(private) +concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> string { + n := 0 + for s in strings { + (n < len(buf)) or_break + n += copy(buf[n:], s) + } + n = min(len(buf), n) + return string(buf[:n]) +} + + + +@(private) +temp_join_path :: proc(dir, name: string) -> string { + concat :: proc(strings: ..string) -> string { + n := 0 + for s in strings { + n += len(s) + } + buf := make([]byte, n) + n = 0 + for s in strings { + n += copy(buf[n:], s) + } + return string(buf) + } + + if len(dir) > 0 && is_path_separator(dir[len(dir)-1]) { + return concat(dir, name) + } + + return concat(dir, Path_Separator_String, name) +} + + +@(private="file") +random_string_seed: [2]u64 + +@(init, private="file") +init_random_string_seed :: proc() { + seed := u64(intrinsics.read_cycle_counter()) + s := &random_string_seed + s[0] = 0 + s[1] = (seed << 1) | 1 + _ = next_random(s) + s[1] += seed + _ = next_random(s) +} + +@(private="file") +next_random :: proc(r: ^[2]u64) -> u64 { + old_state := r[0] + r[0] = old_state * 6364136223846793005 + (r[1]|1) + xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081 + rot := (old_state >> 59) + return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) +} + +@(private="file") +random_string :: proc(buf: []byte) -> string { + @static digits := "0123456789" + + u := next_random(&random_string_seed) + + b :: 10 + i := len(buf) + for u >= b { + i -= 1 + buf[i] = digits[u % b] + u /= b + } + i -= 1 + buf[i] = digits[u % b] + return string(buf[i:]) +} diff --git a/core/os/os2/temp_file_linux.odin b/core/os/os2/temp_file_linux.odin index dd7ac5c97..92afcde47 100644 --- a/core/os/os2/temp_file_linux.odin +++ b/core/os/os2/temp_file_linux.odin @@ -4,16 +4,6 @@ package os2 import "base:runtime" -_create_temp :: proc(dir, pattern: string) -> (^File, Error) { - //TODO - return nil, nil -} - -_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { - //TODO - return "", nil -} - _temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) { //TODO return "", nil diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin index 09b5675f2..4c8ab9fb7 100644 --- a/core/os/os2/temp_file_windows.odin +++ b/core/os/os2/temp_file_windows.odin @@ -4,14 +4,6 @@ package os2 import "base:runtime" import win32 "core:sys/windows" -_create_temp :: proc(dir, pattern: string) -> (^File, Error) { - return nil, nil -} - -_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { - return "", nil -} - _temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { n := win32.GetTempPathW(0, nil) if n == 0 { From 48c18227097cf81b04dc45ee918faf37e73517be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:14:44 +0100 Subject: [PATCH 27/36] Move utility stuff to `internal_util.odin` --- core/os/os2/internal_util.odin | 83 +++++++++++++++++++++++++++++++++ core/os/os2/temp_file.odin | 85 +--------------------------------- 2 files changed, 84 insertions(+), 84 deletions(-) create mode 100644 core/os/os2/internal_util.odin diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin new file mode 100644 index 000000000..69c3c8941 --- /dev/null +++ b/core/os/os2/internal_util.odin @@ -0,0 +1,83 @@ +//+private +package os2 + +import "base:intrinsics" +import "base:runtime" + + +// Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix +// parts which are split by the last "*" +_prefix_and_suffix :: proc(pattern: string) -> (prefix, suffix: string, err: Error) { + for i in 0..= 0; i -= 1 { + if pattern[i] == '*' { + prefix, suffix = pattern[:i], pattern[i+1:] + break + } + } + return +} + +clone_string :: proc(s: string, allocator: runtime.Allocator) -> string { + buf := make([]byte, len(s), allocator) + copy(buf, s) + return string(buf) +} + + +concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> string { + n := 0 + for s in strings { + (n < len(buf)) or_break + n += copy(buf[n:], s) + } + n = min(len(buf), n) + return string(buf[:n]) +} + + + +@(private="file") +random_string_seed: [2]u64 + +@(init, private="file") +init_random_string_seed :: proc() { + seed := u64(intrinsics.read_cycle_counter()) + s := &random_string_seed + s[0] = 0 + s[1] = (seed << 1) | 1 + _ = next_random(s) + s[1] += seed + _ = next_random(s) +} + +next_random :: proc(r: ^[2]u64) -> u64 { + old_state := r[0] + r[0] = old_state * 6364136223846793005 + (r[1]|1) + xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081 + rot := (old_state >> 59) + return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) +} + +random_string :: proc(buf: []byte) -> string { + @static digits := "0123456789" + + u := next_random(&random_string_seed) + + b :: 10 + i := len(buf) + for u >= b { + i -= 1 + buf[i] = digits[u % b] + u /= b + } + i -= 1 + buf[i] = digits[u % b] + return string(buf[i:]) +} diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index d415eb7f9..a1be8f690 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -1,6 +1,5 @@ package os2 -import "base:intrinsics" import "base:runtime" @(private="file") @@ -84,48 +83,8 @@ temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) { } -// Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix -// parts which are split by the last "*" -@(private) -_prefix_and_suffix :: proc(pattern: string) -> (prefix, suffix: string, err: Error) { - for i in 0..= 0; i -= 1 { - if pattern[i] == '*' { - prefix, suffix = pattern[:i], pattern[i+1:] - break - } - } - return -} -@(private) -clone_string :: proc(s: string, allocator: runtime.Allocator) -> string { - buf := make([]byte, len(s), allocator) - copy(buf, s) - return string(buf) -} - - -@(private) -concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> string { - n := 0 - for s in strings { - (n < len(buf)) or_break - n += copy(buf[n:], s) - } - n = min(len(buf), n) - return string(buf[:n]) -} - - - -@(private) +@(private="file") temp_join_path :: proc(dir, name: string) -> string { concat :: proc(strings: ..string) -> string { n := 0 @@ -146,45 +105,3 @@ temp_join_path :: proc(dir, name: string) -> string { return concat(dir, Path_Separator_String, name) } - - -@(private="file") -random_string_seed: [2]u64 - -@(init, private="file") -init_random_string_seed :: proc() { - seed := u64(intrinsics.read_cycle_counter()) - s := &random_string_seed - s[0] = 0 - s[1] = (seed << 1) | 1 - _ = next_random(s) - s[1] += seed - _ = next_random(s) -} - -@(private="file") -next_random :: proc(r: ^[2]u64) -> u64 { - old_state := r[0] - r[0] = old_state * 6364136223846793005 + (r[1]|1) - xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081 - rot := (old_state >> 59) - return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) -} - -@(private="file") -random_string :: proc(buf: []byte) -> string { - @static digits := "0123456789" - - u := next_random(&random_string_seed) - - b :: 10 - i := len(buf) - for u >= b { - i -= 1 - buf[i] = digits[u % b] - u /= b - } - i -= 1 - buf[i] = digits[u % b] - return string(buf[i:]) -} From 450b9ceaec2bf30baa4ffe8acdaa2339a84f3590 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:25:15 +0100 Subject: [PATCH 28/36] Add `@(require_results)` everywhere in os2 --- core/os/os2/allocators.odin | 2 ++ core/os/os2/env.odin | 3 ++ core/os/os2/file.odin | 8 +++++ core/os/os2/file_linux.odin | 60 +++++++++++++++------------------- core/os/os2/file_util.odin | 3 ++ core/os/os2/heap.odin | 2 ++ core/os/os2/internal_util.odin | 28 ++++++++++++++-- core/os/os2/path.odin | 2 ++ core/os/os2/path_linux.odin | 3 +- core/os/os2/pipe.odin | 1 + core/os/os2/stat.odin | 4 +++ core/os/os2/temp_file.odin | 5 ++- core/os/os2/user.odin | 3 ++ 13 files changed, 85 insertions(+), 39 deletions(-) diff --git a/core/os/os2/allocators.odin b/core/os/os2/allocators.odin index c044e4522..40672face 100644 --- a/core/os/os2/allocators.odin +++ b/core/os/os2/allocators.odin @@ -3,6 +3,7 @@ package os2 import "base:runtime" +@(require_results) file_allocator :: proc() -> runtime.Allocator { return heap_allocator() } @@ -12,6 +13,7 @@ temp_allocator_proc :: runtime.arena_allocator_proc @(private="file", thread_local) global_default_temp_allocator_arena: runtime.Arena +@(require_results) temp_allocator :: proc() -> runtime.Allocator { return runtime.Allocator{ procedure = temp_allocator_proc, diff --git a/core/os/os2/env.odin b/core/os/os2/env.odin index bed4bebd9..c8d39b270 100644 --- a/core/os/os2/env.odin +++ b/core/os/os2/env.odin @@ -6,6 +6,7 @@ import "base:runtime" // It returns the value, which will be empty if the variable is not present // To distinguish between an empty value and an unset value, use lookup_env // NOTE: the value will be allocated with the supplied allocator +@(require_results) get_env :: proc(key: string, allocator: runtime.Allocator) -> string { value, _ := lookup_env(key, allocator) return value @@ -15,6 +16,7 @@ get_env :: proc(key: string, allocator: runtime.Allocator) -> string { // If the variable is found in the environment the value (which can be empty) is returned and the boolean is true // Otherwise the returned value will be empty and the boolean will be false // NOTE: the value will be allocated with the supplied allocator +@(require_results) lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) { return _lookup_env(key, allocator) } @@ -38,6 +40,7 @@ clear_env :: proc() { // environ returns a copy of strings representing the environment, in the form "key=value" // NOTE: the slice of strings and the strings with be allocated using the supplied allocator +@(require_results) environ :: proc(allocator: runtime.Allocator) -> []string { return _environ(allocator) } diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index a0fd48c27..b6e9472fb 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -51,22 +51,27 @@ stdout: ^File = nil // OS-Specific stderr: ^File = nil // OS-Specific +@(require_results) create :: proc(name: string) -> (^File, Error) { return open(name, {.Read, .Write, .Create}, File_Mode(0o777)) } +@(require_results) open :: proc(name: string, flags := File_Flags{.Read}, perm := File_Mode(0o777)) -> (^File, Error) { return _open(name, flags, perm) } +@(require_results) new_file :: proc(handle: uintptr, name: string) -> ^File { return _new_file(handle, name) } +@(require_results) fd :: proc(f: ^File) -> uintptr { return _fd(f) } +@(require_results) name :: proc(f: ^File) -> string { return _name(f) } @@ -200,15 +205,18 @@ fchange_times :: proc(f: ^File, atime, mtime: time.Time) -> Error { return _fchtimes(f, atime, mtime) } +@(require_results) exists :: proc(path: string) -> bool { return _exists(path) } +@(require_results) is_file :: proc(path: string) -> bool { return _is_file(path) } is_dir :: is_directory +@(require_results) is_directory :: proc(path: string) -> bool { return _is_dir(path) } diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 3f66bbd09..3843c1105 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -1,10 +1,9 @@ //+private package os2 +import "base:runtime" import "core:io" import "core:time" -import "core:strings" -import "base:runtime" import "core:sys/unix" INVALID_HANDLE :: -1 @@ -35,9 +34,9 @@ _File :: struct { allocator: runtime.Allocator, } -_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) { +_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return // Just default to using O_NOCTTY because needing to open a controlling // terminal would be incredibly rare. This has no effect on files while @@ -195,7 +194,7 @@ _truncate :: proc(f: ^File, size: i64) -> Error { _remove :: proc(name: string) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return fd := unix.sys_open(name_cstr, int(File_Flags.Read)) if fd < 0 { @@ -210,22 +209,25 @@ _remove :: proc(name: string) -> Error { } _rename :: proc(old_name, new_name: string) -> Error { - old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator) - new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + old_name_cstr := temp_cstring(old_name) or_return + new_name_cstr := temp_cstring(new_name) or_return return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr)) } _link :: proc(old_name, new_name: string) -> Error { - old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator) - new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + old_name_cstr := temp_cstring(old_name) or_return + new_name_cstr := temp_cstring(new_name) or_return return _ok_or_error(unix.sys_link(old_name_cstr, new_name_cstr)) } _symlink :: proc(old_name, new_name: string) -> Error { - old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator) - new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + old_name_cstr := temp_cstring(old_name) or_return + new_name_cstr := temp_cstring(new_name) or_return return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr)) } @@ -234,7 +236,7 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (st bufsz : uint = 256 buf := make([]byte, bufsz, allocator) for { - rc := unix.sys_readlink(name_cstr, &(buf[0]), bufsz) + rc := unix.sys_readlink(name_cstr, &buf[0], bufsz) if rc < 0 { delete(buf) return "", _get_platform_error(rc) @@ -243,26 +245,26 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (st delete(buf) buf = make([]byte, bufsz, allocator) } else { - return strings.string_from_ptr(&buf[0], rc), nil + return string(buf[:rc]), nil } } } -_read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) { +_read_link :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _read_link_cstr(name_cstr, allocator) } _unlink :: proc(name: string) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _ok_or_error(unix.sys_unlink(name_cstr)) } _chdir :: proc(name: string) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _ok_or_error(unix.sys_chdir(name_cstr)) } @@ -272,7 +274,7 @@ _fchdir :: proc(f: ^File) -> Error { _chmod :: proc(name: string, mode: File_Mode) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _ok_or_error(unix.sys_chmod(name_cstr, uint(mode))) } @@ -283,14 +285,14 @@ _fchmod :: proc(f: ^File, mode: File_Mode) -> Error { // NOTE: will throw error without super user priviledges _chown :: proc(name: string, uid, gid: int) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _ok_or_error(unix.sys_chown(name_cstr, uid, gid)) } // NOTE: will throw error without super user priviledges _lchown :: proc(name: string, uid, gid: int) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid)) } @@ -301,7 +303,7 @@ _fchown :: proc(f: ^File, uid, gid: int) -> Error { _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return times := [2]Unix_File_Time { { atime._nsec, 0 }, { mtime._nsec, 0 }, @@ -319,13 +321,13 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { _exists :: proc(name: string) -> bool { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr, _ := temp_cstring(name) return unix.sys_access(name_cstr, F_OK) == 0 } _is_file :: proc(name: string) -> bool { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr, _ := temp_cstring(name) s: _Stat res := unix.sys_stat(name_cstr, &s) if res < 0 { @@ -345,7 +347,7 @@ _is_file_fd :: proc(fd: int) -> bool { _is_dir :: proc(name: string) -> bool { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr, _ := temp_cstring(name) s: _Stat res := unix.sys_stat(name_cstr, &s) if res < 0 { @@ -363,16 +365,6 @@ _is_dir_fd :: proc(fd: int) -> bool { return S_ISDIR(s.mode) } -// Ideally we want to use the temp_allocator. PATH_MAX on Linux is commonly -// defined as 512, however, it is well known that paths can exceed that limit. -// So, in theory you could have a path larger than the entire temp_allocator's -// buffer. Therefor, any large paths will use context.allocator. -@(private="file") -_temp_name_to_cstring :: proc(name: string) -> (cname: cstring) { - return strings.clone_to_cstring(name, temp_allocator()) -} - - @(private="package") _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { f := (^File)(stream_data) diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 9ec75fc91..977979bae 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -76,6 +76,7 @@ read_entire_file :: proc{ read_entire_file_from_file, } +@(require_results) read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator) -> (data: []byte, err: Error) { f, ferr := open(name) if ferr != nil { @@ -85,6 +86,7 @@ read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator) - return read_entire_file_from_file(f, allocator) } +@(require_results) read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (data: []byte, err: Error) { size: int has_size := true @@ -135,6 +137,7 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d } } +@(require_results) write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate := true) -> Error { flags := O_WRONLY|O_CREATE if truncate { diff --git a/core/os/os2/heap.odin b/core/os/os2/heap.odin index a07a0d618..e0cffaf0d 100644 --- a/core/os/os2/heap.odin +++ b/core/os/os2/heap.odin @@ -2,6 +2,7 @@ package os2 import "base:runtime" +@(require_results) heap_allocator :: proc() -> runtime.Allocator { return runtime.Allocator{ procedure = heap_allocator_proc, @@ -10,6 +11,7 @@ heap_allocator :: proc() -> runtime.Allocator { } +@(require_results) heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, runtime.Allocator_Error) { diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin index 69c3c8941..756343efd 100644 --- a/core/os/os2/internal_util.odin +++ b/core/os/os2/internal_util.odin @@ -7,6 +7,7 @@ import "base:runtime" // Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix // parts which are split by the last "*" +@(require_results) _prefix_and_suffix :: proc(pattern: string) -> (prefix, suffix: string, err: Error) { for i in 0.. (prefix, suffix: string, err: Err return } -clone_string :: proc(s: string, allocator: runtime.Allocator) -> string { - buf := make([]byte, len(s), allocator) +@(require_results) +clone_string :: proc(s: string, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) { + buf := make([]byte, len(s), allocator) or_return copy(buf, s) - return string(buf) + return string(buf), nil } +@(require_results) +clone_to_cstring :: proc(s: string, allocator: runtime.Allocator) -> (res: cstring, err: runtime.Allocator_Error) { + res = "" // do not use a `nil` cstring + buf := make([]byte, len(s)+1, allocator) or_return + copy(buf, s) + buf[len(s)] = 0 + return cstring(&buf[0]), nil +} + +@(require_results) +temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) { + return clone_to_cstring(s, temp_allocator()) +} + + + + +@(require_results) concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> string { n := 0 for s in strings { @@ -57,6 +77,7 @@ init_random_string_seed :: proc() { _ = next_random(s) } +@(require_results) next_random :: proc(r: ^[2]u64) -> u64 { old_state := r[0] r[0] = old_state * 6364136223846793005 + (r[1]|1) @@ -65,6 +86,7 @@ next_random :: proc(r: ^[2]u64) -> u64 { return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) } +@(require_results) random_string :: proc(buf: []byte) -> string { @static digits := "0123456789" diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin index 277da56dd..27c3d6b0b 100644 --- a/core/os/os2/path.odin +++ b/core/os/os2/path.odin @@ -6,6 +6,7 @@ Path_Separator :: _Path_Separator // OS-Specific Path_Separator_String :: _Path_Separator_String // OS-Specific Path_List_Separator :: _Path_List_Separator // OS-Specific +@(require_results) is_path_separator :: proc(c: byte) -> bool { return _is_path_separator(c) } @@ -26,6 +27,7 @@ remove_all :: proc(path: string) -> Error { getwd :: get_working_directory +@(require_results) get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _getwd(allocator) } diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index fde4ba5b1..aaaafa2b4 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -32,7 +32,8 @@ _mkdir :: proc(path: string, perm: File_Mode) -> Error { return .Invalid_Argument } - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + path_cstr := strings.clone_to_cstring(path, temp_allocator()) return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777))) } diff --git a/core/os/os2/pipe.odin b/core/os/os2/pipe.odin index 62f7ddf10..9254d6f8e 100644 --- a/core/os/os2/pipe.odin +++ b/core/os/os2/pipe.odin @@ -1,5 +1,6 @@ package os2 +@(require_results) pipe :: proc() -> (r, w: ^File, err: Error) { return _pipe() } diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index a64522ac1..16f319046 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -25,20 +25,24 @@ file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) { delete(fi.fullpath, allocator) } +@(require_results) fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { return _fstat(f, allocator) } +@(require_results) stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { return _stat(name, allocator) } lstat :: stat_do_not_follow_links +@(require_results) stat_do_not_follow_links :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { return _lstat(name, allocator) } +@(require_results) same_file :: proc(fi1, fi2: File_Info) -> bool { return _same_file(fi1, fi2) } diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index a1be8f690..fa71f44a7 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -13,6 +13,7 @@ MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right? // If `dir` is an empty tring, `temp_directory()` will be used. // // The caller must `close` the file once finished with. +@(require_results) create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) { TEMP_ALLOCATOR_GUARD() dir := dir if dir != "" else temp_directory(temp_allocator()) or_return @@ -44,6 +45,7 @@ mkdir_temp :: make_directory_temp // The directory name is generated by taking a pattern, and adding a randomized string to the end. // If the pattern includes an "*", the randm string replaces the last "*". // If `dir` is an empty tring, `temp_directory()` will be used. +@(require_results) make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (temp_path: string, err: Error) { TEMP_ALLOCATOR_GUARD() dir := dir if dir != "" else temp_directory(temp_allocator()) or_return @@ -58,7 +60,7 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix) err = make_directory(name, 0o700) if err == nil { - return clone_string(name, allocator), nil + return clone_string(name, allocator) } if err == .Exist { attempts += 1 @@ -78,6 +80,7 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) } temp_dir :: temp_directory +@(require_results) temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) { return _temp_dir(allocator) } diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index 0af461bf5..a4ef7c1dd 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -3,6 +3,7 @@ package os2 import "core:strings" import "base:runtime" +@(require_results) user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Windows: @@ -31,6 +32,7 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error return } +@(require_results) user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Windows: @@ -59,6 +61,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro return } +@(require_results) user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { env := "HOME" #partial switch ODIN_OS { From 453fc5182b85af2e3254f242e23f0d5e89fde78d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:34:05 +0100 Subject: [PATCH 29/36] Remove all uses of `context` stuff in os2 --- core/os/os2/file_windows.odin | 3 +-- core/os/os2/internal_util.odin | 26 ++++++++++++++++++++++++-- core/os/os2/path_linux.odin | 14 ++++++++------ core/os/os2/path_windows.odin | 3 +-- core/os/os2/stat_linux.odin | 22 ++++++++++++---------- core/os/os2/temp_file.odin | 23 +++++------------------ core/os/os2/user.odin | 13 ++++++------- 7 files changed, 57 insertions(+), 47 deletions(-) diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 1e0899992..37f8f44de 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -6,7 +6,6 @@ import "base:runtime" import "core:io" import "core:mem" import "core:sync" -import "core:strings" import "core:time" import "core:unicode/utf16" import win32 "core:sys/windows" @@ -137,7 +136,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File { f.impl.allocator = file_allocator() f.impl.fd = rawptr(handle) - f.impl.name = strings.clone(name, f.impl.allocator) + f.impl.name, _ = clone_string(name, f.impl.allocator) f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator) handle := _handle(f) diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin index 756343efd..57c21c6bd 100644 --- a/core/os/os2/internal_util.odin +++ b/core/os/os2/internal_util.odin @@ -47,8 +47,16 @@ temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) { return clone_to_cstring(s, temp_allocator()) } - - +string_from_null_terminated_bytes :: proc(b: []byte) -> (res: string) { + s := string(b) + i := 0 + for ; i < len(s); i += 1 { + if s[i] == 0 { + break + } + } + return s[:i] +} @(require_results) concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> string { @@ -61,6 +69,20 @@ concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> strin return string(buf[:n]) } +@(require_results) +concatenate :: proc(strings: []string, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) { + n := 0 + for s in strings { + n += len(s) + } + buf := make([]byte, n) or_return + n = 0 + for s in strings { + n += copy(buf[n:], s) + } + return string(buf), nil +} + @(private="file") diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index aaaafa2b4..81176c219 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -1,7 +1,6 @@ //+private package os2 -import "core:strings" import "core:strconv" import "base:runtime" import "core:sys/unix" @@ -33,7 +32,7 @@ _mkdir :: proc(path: string, perm: File_Mode) -> Error { } TEMP_ALLOCATOR_GUARD() - path_cstr := strings.clone_to_cstring(path, temp_allocator()) + path_cstr := temp_cstring(path) or_return return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777))) } @@ -73,6 +72,8 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { return .Invalid_Argument } + TEMP_ALLOCATOR_GUARD() + // need something we can edit, and use to generate cstrings allocated: bool path_bytes: []u8 @@ -80,7 +81,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { allocated = true path_bytes = make([]u8, len(path) + 1) } else { - path_bytes = make([]u8, len(path) + 1, context.temp_allocator) + path_bytes = make([]u8, len(path) + 1, temp_allocator()) } // NULL terminate the byte slice to make it a valid cstring @@ -178,7 +179,8 @@ _remove_all :: proc(path: string) -> Error { return nil } - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + path_cstr := temp_cstring(path) or_return fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS) switch fd { @@ -204,7 +206,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) { #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf))) if res >= 0 { - return strings.string_from_null_terminated_ptr(&buf[0], len(buf)), nil + return string_from_null_terminated_bytes(buf[:]), nil } if res != -ERANGE { return "", _get_platform_error(res) @@ -215,7 +217,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) { } _setwd :: proc(dir: string) -> Error { - dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator) + dir_cstr := temp_cstring(dir) or_return return _ok_or_error(unix.sys_chdir(dir_cstr)) } diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index 064356cf1..fcd1e3321 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -3,7 +3,6 @@ package os2 import win32 "core:sys/windows" import "base:runtime" -import "core:strings" _Path_Separator :: '\\' _Path_Separator_String :: "\\" @@ -24,7 +23,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) { if len(p) == len(`\\?\c:`) { if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' { - s = strings.concatenate({p, `\`}, file_allocator()) or_return + s = concatenate({p, `\`}, file_allocator()) or_return allocated = true return } diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index db929a719..4508ab892 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -3,7 +3,6 @@ package os2 import "core:time" import "base:runtime" -import "core:strings" import "core:sys/unix" import "core:path/filepath" @@ -112,8 +111,9 @@ _fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Er } // NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath -_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) +_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) { + TEMP_ALLOCATOR_GUARD() + name_cstr := temp_cstring(name) or_return fd := unix.sys_open(name_cstr, _O_RDONLY) if fd < 0 { @@ -123,8 +123,9 @@ _stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) return _fstat_internal(fd, allocator) } -_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) +_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) { + TEMP_ALLOCATOR_GUARD() + name_cstr := temp_cstring(name) or_return fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW) if fd < 0 { @@ -138,8 +139,9 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool { return fi1.fullpath == fi2.fullpath } -_stat_internal :: proc(name: string) -> (s: _Stat, res: int) { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) - res = unix.sys_stat(name_cstr, &s) - return -} +// _stat_internal :: proc(name: string) -> (s: _Stat, res: int) { +// TEMP_ALLOCATOR_GUARD() +// name_cstr := temp_cstring(name) or_return +// res = unix.sys_stat(name_cstr, &s) +// return +// } diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index fa71f44a7..ead2108de 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -18,7 +18,7 @@ create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) { TEMP_ALLOCATOR_GUARD() dir := dir if dir != "" else temp_directory(temp_allocator()) or_return prefix, suffix := _prefix_and_suffix(pattern) or_return - prefix = temp_join_path(dir, prefix) + prefix = temp_join_path(dir, prefix) or_return rand_buf: [32]byte name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator()) @@ -50,7 +50,7 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) TEMP_ALLOCATOR_GUARD() dir := dir if dir != "" else temp_directory(temp_allocator()) or_return prefix, suffix := _prefix_and_suffix(pattern) or_return - prefix = temp_join_path(dir, prefix) + prefix = temp_join_path(dir, prefix) or_return rand_buf: [32]byte name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator()) @@ -88,23 +88,10 @@ temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) { @(private="file") -temp_join_path :: proc(dir, name: string) -> string { - concat :: proc(strings: ..string) -> string { - n := 0 - for s in strings { - n += len(s) - } - buf := make([]byte, n) - n = 0 - for s in strings { - n += copy(buf[n:], s) - } - return string(buf) - } - +temp_join_path :: proc(dir, name: string) -> (string, runtime.Allocator_Error) { if len(dir) > 0 && is_path_separator(dir[len(dir)-1]) { - return concat(dir, name) + return concatenate({dir, name}, temp_allocator(),) } - return concat(dir, Path_Separator_String, name) + return concatenate({dir, Path_Separator_String, name}, temp_allocator()) } diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index a4ef7c1dd..ca099f7ae 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -1,6 +1,5 @@ package os2 -import "core:strings" import "base:runtime" @(require_results) @@ -9,12 +8,12 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error case .Windows: dir = get_env("LocalAppData", allocator) if dir != "" { - dir = strings.clone(dir, allocator) or_return + dir = clone_string(dir, allocator) or_return } case .Darwin: dir = get_env("HOME", allocator) if dir != "" { - dir = strings.concatenate({dir, "/Library/Caches"}, allocator) or_return + dir = concatenate({dir, "/Library/Caches"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME", allocator) @@ -23,7 +22,7 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error if dir == "" { return } - dir = strings.concatenate({dir, "/.cache"}, allocator) or_return + dir = concatenate({dir, "/.cache"}, allocator) or_return } } if dir == "" { @@ -38,12 +37,12 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro case .Windows: dir = get_env("AppData", allocator) if dir != "" { - dir = strings.clone(dir, allocator) or_return + dir = clone_string(dir, allocator) or_return } case .Darwin: dir = get_env("HOME", allocator) if dir != "" { - dir = strings.concatenate({dir, "/Library/Application Support"}, allocator) or_return + dir = concatenate({dir, "/Library/Application Support"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME", allocator) @@ -52,7 +51,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro if dir == "" { return } - dir = strings.concatenate({dir, "/.config"}, allocator) or_return + dir = concatenate({dir, "/.config"}, allocator) or_return } } if dir == "" { From 9a487ccb6f9834a351aff6330255bd7174f14bb9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:35:24 +0100 Subject: [PATCH 30/36] Delete dead code --- core/os/os2/stat_linux.odin | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index 4508ab892..dc287cafe 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -138,10 +138,3 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er _same_file :: proc(fi1, fi2: File_Info) -> bool { return fi1.fullpath == fi2.fullpath } - -// _stat_internal :: proc(name: string) -> (s: _Stat, res: int) { -// TEMP_ALLOCATOR_GUARD() -// name_cstr := temp_cstring(name) or_return -// res = unix.sys_stat(name_cstr, &s) -// return -// } From 61826594c940f86ec6ed900f6c191ed98065bab5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:37:23 +0100 Subject: [PATCH 31/36] Add comment state to prefer `virtual.Arena` over `runtime.Arena` --- base/runtime/default_allocators_arena.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/base/runtime/default_allocators_arena.odin b/base/runtime/default_allocators_arena.odin index fcc98abfa..571590f93 100644 --- a/base/runtime/default_allocators_arena.odin +++ b/base/runtime/default_allocators_arena.odin @@ -12,6 +12,7 @@ Memory_Block :: struct { capacity: uint, } +// NOTE: This is for internal use, prefer `Arena` from `core:mem/virtual` if necessary Arena :: struct { backing_allocator: Allocator, curr_block: ^Memory_Block, From 2b43535961f1e02ab3e333019c0da6fc3034d432 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:45:47 +0100 Subject: [PATCH 32/36] Fix typo --- core/os/os2/temp_file.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index ead2108de..3b3dbdd57 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -9,7 +9,7 @@ MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right? // // Opens the file for reading and writing, with 0o666 permissions, and returns the new `^File`. // The filename is generated by taking a pattern, and adding a randomized string to the end. -// If the pattern includes an "*", the randm string replaces the last "*". +// If the pattern includes an "*", the random string replaces the last "*". // If `dir` is an empty tring, `temp_directory()` will be used. // // The caller must `close` the file once finished with. @@ -43,7 +43,7 @@ mkdir_temp :: make_directory_temp // Creates a new temporary directory in the directory `dir`, and returns the path of the new directory. // // The directory name is generated by taking a pattern, and adding a randomized string to the end. -// If the pattern includes an "*", the randm string replaces the last "*". +// If the pattern includes an "*", the random string replaces the last "*". // If `dir` is an empty tring, `temp_directory()` will be used. @(require_results) make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (temp_path: string, err: Error) { From 6f9dcb4e02e3da76b17276a0d5de73f338d214c6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:46:45 +0100 Subject: [PATCH 33/36] Add missing `@(require_results)` --- core/os/os2/internal_util.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin index 57c21c6bd..59d845350 100644 --- a/core/os/os2/internal_util.odin +++ b/core/os/os2/internal_util.odin @@ -47,6 +47,7 @@ temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) { return clone_to_cstring(s, temp_allocator()) } +@(require_results) string_from_null_terminated_bytes :: proc(b: []byte) -> (res: string) { s := string(b) i := 0 From 59c33dd9fce58f2fdf396a622c446f3912e88a95 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:57:03 +0100 Subject: [PATCH 34/36] Add `Fstat_Callback` for `File.user_fstat` --- core/os/os2/file.odin | 3 ++- core/os/os2/stat.odin | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index b6e9472fb..4f4bd942e 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -5,8 +5,9 @@ import "core:time" import "base:runtime" File :: struct { - impl: _File, + impl: _File, stream: io.Stream, + user_fstat: Fstat_Callback, } File_Mode :: distinct u32 diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index 16f319046..f79ad9165 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -3,6 +3,8 @@ package os2 import "core:time" import "base:runtime" +Fstat_Callback :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) + File_Info :: struct { fullpath: string, name: string, @@ -27,6 +29,9 @@ file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) { @(require_results) fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { + if f != nil && f.user_fstat != nil { + return f->user_fstat(allocator) + } return _fstat(f, allocator) } From e71cd871c45d99ce6f593b44002dc0e3ad57593b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 16 May 2024 14:27:05 +0100 Subject: [PATCH 35/36] Reimplement `-build-mode:static`/`-build-mode:lib` --- src/build_settings.cpp | 8 +++++++- src/checker.cpp | 1 + src/linker.cpp | 31 ++++++++++++++++++++++++------- src/main.cpp | 10 ++++++++++ 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 7eb198185..f4e957479 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -646,6 +646,7 @@ struct QueryDataSetSettings { enum BuildModeKind { BuildMode_Executable, BuildMode_DynamicLibrary, + BuildMode_StaticLibrary, BuildMode_Object, BuildMode_Assembly, BuildMode_LLVM_IR, @@ -2284,7 +2285,12 @@ gb_internal bool init_build_paths(String init_filename) { } else if (build_context.metrics.os == TargetOs_darwin) { output_extension = STR_LIT("dylib"); } - } else if (build_context.build_mode == BuildMode_Object) { + } else if (build_context.build_mode == BuildMode_StaticLibrary) { + output_extension = STR_LIT("a"); + if (build_context.metrics.os == TargetOs_windows) { + output_extension = STR_LIT("lib"); + } + }else if (build_context.build_mode == BuildMode_Object) { // By default use a .o object extension. output_extension = STR_LIT("o"); diff --git a/src/checker.cpp b/src/checker.cpp index 12c8f5291..7cda0aa42 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1044,6 +1044,7 @@ gb_internal void init_universal(void) { GlobalEnumValue values[BuildMode_COUNT] = { {"Executable", BuildMode_Executable}, {"Dynamic", BuildMode_DynamicLibrary}, + {"Static", BuildMode_StaticLibrary}, {"Object", BuildMode_Object}, {"Assembly", BuildMode_Assembly}, {"LLVM_IR", BuildMode_LLVM_IR}, diff --git a/src/linker.cpp b/src/linker.cpp index 33ca70462..c41f10593 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -212,10 +212,12 @@ gb_internal i32 linker_stage(LinkerData *gen) { link_settings = gb_string_append_fmt(link_settings, " /PDB:\"%.*s\"", LIT(pdb_path)); } - if (build_context.no_crt) { - link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib"); - } else { - link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt"); + if (build_context.build_mode != BuildMode_StaticLibrary) { + if (build_context.no_crt) { + link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib"); + } else { + link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt"); + } } if (build_context.ODIN_DEBUG) { @@ -257,20 +259,31 @@ gb_internal i32 linker_stage(LinkerData *gen) { } } + String linker_name = str_lit("link.exe"); switch (build_context.build_mode) { case BuildMode_Executable: link_settings = gb_string_append_fmt(link_settings, " /NOIMPLIB /NOEXP"); break; } + switch (build_context.build_mode) { + case BuildMode_StaticLibrary: + linker_name = str_lit("lib.exe"); + break; + default: + link_settings = gb_string_append_fmt(link_settings, " /incremental:no /opt:ref"); + break; + } + + result = system_exec_command_line_app("msvc-link", - "\"%.*slink.exe\" %s %.*s -OUT:\"%.*s\" %s " - "/nologo /incremental:no /opt:ref /subsystem:%.*s " + "\"%.*s%.*s\" %s %.*s -OUT:\"%.*s\" %s " + "/nologo /subsystem:%.*s " "%.*s " "%.*s " "%s " "", - LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename), + LIT(vs_exe_path), LIT(linker_name), object_files, LIT(res_path), LIT(output_filename), link_settings, LIT(build_context.ODIN_WINDOWS_SUBSYSTEM), LIT(build_context.link_flags), @@ -458,6 +471,10 @@ gb_internal i32 linker_stage(LinkerData *gen) { link_settings = gb_string_append_fmt(link_settings, "-nostdlib "); } + if (build_context.build_mode == BuildMode_StaticLibrary) { + compiler_error("TODO(bill): -build-mode:static on non-windows targets"); + } + // NOTE(dweiler): We use clang as a frontend for the linker as there are // other runtime and compiler support libraries that need to be linked in // very specific orders such as libgcc_s, ld-linux-so, unwind, etc. diff --git a/src/main.cpp b/src/main.cpp index e76b0e380..4df6f97d5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -989,6 +989,8 @@ gb_internal bool parse_build_flags(Array args) { build_context.build_mode = BuildMode_DynamicLibrary; } else if (str == "obj" || str == "object") { build_context.build_mode = BuildMode_Object; + } else if (str == "static" || str == "lib") { + build_context.build_mode = BuildMode_StaticLibrary; } else if (str == "exe") { build_context.build_mode = BuildMode_Executable; } else if (str == "asm" || str == "assembly" || str == "assembler") { @@ -999,6 +1001,7 @@ gb_internal bool parse_build_flags(Array args) { gb_printf_err("Unknown build mode '%.*s'\n", LIT(str)); gb_printf_err("Valid build modes:\n"); gb_printf_err("\tdll, shared, dynamic\n"); + gb_printf_err("\tlib, static\n"); gb_printf_err("\tobj, object\n"); gb_printf_err("\texe\n"); gb_printf_err("\tasm, assembly, assembler\n"); @@ -1637,6 +1640,7 @@ gb_internal void remove_temp_files(lbGenerator *gen) { switch (build_context.build_mode) { case BuildMode_Executable: + case BuildMode_StaticLibrary: case BuildMode_DynamicLibrary: break; @@ -1655,6 +1659,7 @@ gb_internal void remove_temp_files(lbGenerator *gen) { if (!build_context.keep_object_files) { switch (build_context.build_mode) { case BuildMode_Executable: + case BuildMode_StaticLibrary: case BuildMode_DynamicLibrary: for (String const &path : gen->output_object_paths) { gb_file_remove(cast(char const *)path.text); @@ -1833,6 +1838,9 @@ gb_internal void print_show_help(String const arg0, String const &command) { print_usage_line(3, "-build-mode:exe Builds as an executable."); print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library."); print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library."); + print_usage_line(3, "-build-mode:lib Builds as a statically linked library."); + print_usage_line(3, "-build-mode:static Builds as a statically linked library."); + print_usage_line(3, "-build-mode:lib Builds as an static library."); print_usage_line(3, "-build-mode:obj Builds as an object file."); print_usage_line(3, "-build-mode:object Builds as an object file."); print_usage_line(3, "-build-mode:assembly Builds as an assembly file."); @@ -2866,6 +2874,7 @@ int main(int arg_count, char const **arg_ptr) { switch (build_context.build_mode) { case BuildMode_Executable: + case BuildMode_StaticLibrary: case BuildMode_DynamicLibrary: i32 result = linker_stage(&linker_data); if (result) { @@ -2887,6 +2896,7 @@ int main(int arg_count, char const **arg_ptr) { if (lb_generate_code(gen)) { switch (build_context.build_mode) { case BuildMode_Executable: + case BuildMode_StaticLibrary: case BuildMode_DynamicLibrary: i32 result = linker_stage(gen); if (result) { From a344bc4c0e672d9740f5777dae862723fe269973 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 16 May 2024 14:39:16 +0100 Subject: [PATCH 36/36] Remove the old switch/for semantics entirely and enforce `switch &x in y` --- src/check_expr.cpp | 46 +++++++++++++--------------------------------- src/check_stmt.cpp | 30 ++---------------------------- src/entity.cpp | 2 -- 3 files changed, 15 insertions(+), 63 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6a293a97e..c44232dab 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2440,32 +2440,6 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable; } -gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { - Entity *e = entity_of_node(expr); - if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) { - GB_ASSERT(e->kind == Entity_Variable); - - ERROR_BLOCK(); - - if ((e->flags & EntityFlag_ForValue) != 0) { - Type *parent_type = type_deref(e->Variable.for_loop_parent_type); - - error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed."); - - if (is_type_map(parent_type)) { - error_line("\tSuggestion: Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string)); - } else { - error_line("\tSuggestion: Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string)); - } - } else { - GB_ASSERT((e->flags & EntityFlag_SwitchValue) != 0); - - error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed."); - error_line("\tSuggestion: Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string)); - } - } -} - gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) { switch (op.kind) { case Token_And: { // Pointer address @@ -2493,7 +2467,10 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * { ERROR_BLOCK(); error(op, "Cannot take the pointer address of '%s'", str); - if (e != nullptr && (e->flags & EntityFlag_ForValue) != 0) { + if (e == nullptr) { + break; + } + if ((e->flags & EntityFlag_ForValue) != 0) { Type *parent_type = type_deref(e->Variable.for_loop_parent_type); if (parent_type != nullptr && is_type_string(parent_type)) { @@ -2503,9 +2480,17 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * } else { error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n"); } + + if (is_type_map(parent_type)) { + error_line("\t Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string)); + } else { + error_line("\t Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string)); + } } - if (e != nullptr && (e->flags & EntityFlag_SwitchValue) != 0) { + if ((e->flags & EntityFlag_SwitchValue) != 0) { error_line("\tSuggestion: Did you want to pass the value to the switch statement by pointer to get addressable semantics?\n"); + + error_line("\t Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string)); } } break; @@ -2527,11 +2512,6 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * o->type = alloc_type_pointer(o->type); } } else { - if (ast_node_expect(node, Ast_UnaryExpr)) { - ast_node(ue, UnaryExpr, node); - check_old_for_or_switch_value_usage(ue->expr); - } - o->type = alloc_type_pointer(o->type); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 875503874..0267bdf80 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -501,7 +501,6 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O return nullptr; case Addressing_Variable: - check_old_for_or_switch_value_usage(lhs->expr); break; case Addressing_MapIndex: { @@ -523,9 +522,8 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O break; } - case Addressing_Context: { + case Addressing_Context: break; - } case Addressing_SoaVariable: break; @@ -1328,7 +1326,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ } } - bool is_ptr = is_type_pointer(x.type); // NOTE(bill): Check for multiple defaults Ast *first_default = nullptr; @@ -1447,15 +1444,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ } bool is_reference = is_addressed; - bool old_style = false; - - if (!is_reference && - is_ptr && - cc->list.count == 1 && - case_type != nullptr) { - is_reference = true; - old_style = true; - } if (cc->list.count > 1 || saw_nil) { case_type = nullptr; @@ -1477,9 +1465,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ if (!is_reference) { tag_var->flags |= EntityFlag_Value; } - if (old_style) { - tag_var->flags |= EntityFlag_OldForOrSwitchValue; - } add_entity(ctx, ctx->scope, lhs, tag_var); add_entity_use(ctx, lhs, tag_var); add_implicit_entity(ctx, stmt, tag_var); @@ -1618,7 +1603,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) auto entities = array_make(temporary_allocator(), 0, 2); bool is_map = false; bool is_bit_set = false; - bool use_by_reference_for_value = false; bool is_soa = false; bool is_reverse = rs->reverse; @@ -1679,7 +1663,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } } } - bool is_ptr = is_type_pointer(operand.type); Type *t = base_type(type_deref(operand.type)); switch (t->kind) { @@ -1719,32 +1702,27 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) break; case Type_EnumeratedArray: - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->EnumeratedArray.elem); array_add(&vals, t->EnumeratedArray.index); break; case Type_Array: - if (is_ptr) use_by_reference_for_value = true; - if (!is_ptr) is_possibly_addressable = operand.mode == Addressing_Variable; + is_possibly_addressable = operand.mode == Addressing_Variable; array_add(&vals, t->Array.elem); array_add(&vals, t_int); break; case Type_DynamicArray: - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->DynamicArray.elem); array_add(&vals, t_int); break; case Type_Slice: - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->Slice.elem); array_add(&vals, t_int); break; case Type_Map: - if (is_ptr) use_by_reference_for_value = true; is_map = true; array_add(&vals, t->Map.key); array_add(&vals, t->Map.value); @@ -1817,7 +1795,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) case Type_Struct: if (t->Struct.soa_kind != StructSoa_None) { is_soa = true; - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->Struct.soa_elem); array_add(&vals, t_int); } @@ -1894,9 +1871,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) char const *idx_name = is_map ? "key" : is_bit_set ? "element" : "index"; error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str)); } - } else if (i == addressable_index && use_by_reference_for_value) { - entity->flags |= EntityFlag_OldForOrSwitchValue; - entity->flags &= ~EntityFlag_Value; } if (is_soa) { if (i == 0) { diff --git a/src/entity.cpp b/src/entity.cpp index 60ca208ec..8a7417006 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -85,8 +85,6 @@ enum EntityFlag : u64 { EntityFlag_Require = 1ull<<50, EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer - EntityFlag_OldForOrSwitchValue = 1ull<<52, - EntityFlag_Overridden = 1ull<<63, };