diff --git a/rt/src/scheduler/process.rs b/rt/src/scheduler/process.rs index 7bd12bf4f..e9b58dc2e 100644 --- a/rt/src/scheduler/process.rs +++ b/rt/src/scheduler/process.rs @@ -175,6 +175,9 @@ pub struct Thread { /// A value of 0 indicates the thread isn't blocked. blocked_at: u64, + /// The number of nested blocking calls we're in. + blocked_nesting: u64, + /// The ID of the network poller assigned to this thread. /// /// Threads are each assigned a network poller in a round-robin fashion. @@ -206,6 +209,7 @@ impl Thread { work: pool.threads[id].queue.clone(), backup: false, blocked_at: NOT_BLOCKING, + blocked_nesting: 0, network_poller, rng: thread_rng(), stacks: StackPool::new(pool.stack_size), @@ -222,6 +226,7 @@ impl Thread { work: pool.threads[0].queue.clone(), backup: true, blocked_at: NOT_BLOCKING, + blocked_nesting: 0, network_poller, rng: thread_rng(), stacks: StackPool::new(pool.stack_size), @@ -267,10 +272,20 @@ impl Thread { return; } + // It's possible a user signals the start of a blocking call while this + // was already done so. This ensures that we handle such cases + // gracefully instead of potentially leaving the thread in a weird + // state. + if self.blocked_nesting > 0 { + self.blocked_nesting += 1; + return; + } + let epoch = self.pool.current_epoch(); let shared = &self.pool.threads[self.id]; self.blocked_at = epoch; + self.blocked_nesting = 1; shared.blocked_at.store(epoch, Ordering::Release); // The monitor thread may be sleeping indefinitely if we're the first @@ -296,30 +311,18 @@ impl Thread { return; } - let shared = &self.pool.threads[self.id]; - let epoch = self.blocked_at; + self.blocked_nesting = self.blocked_nesting.saturating_sub(1); - if shared - .blocked_at - .compare_exchange( - epoch, - NOT_BLOCKING, - Ordering::AcqRel, - Ordering::Acquire, - ) - .is_err() - { - // The monitor thread determined we took too long and we have to - // become a backup thread. - self.backup = true; + if self.blocked_nesting > 0 { + return; } - self.blocked_at = NOT_BLOCKING; + self.reset_blocked_at(); - // If the closure took too long to run (e.g. an IO operation took too - // long), we have to give up running the process. If we continue running - // we could mess up whatever thread has taken over our queue/work, and - // we'd be using the OS thread even longer than we already have. + // If the operation took too long to run, we have to give up running the + // process. If we continue running we could mess up whatever thread has + // taken over our queue/work, and we'd be using the OS thread even + // longer than we already have. // // We schedule onto the global queue because if another thread took over // but found no other work, it may have gone to sleep. In that case @@ -340,6 +343,28 @@ impl Thread { } } + fn reset_blocked_at(&mut self) { + let shared = &self.pool.threads[self.id]; + let epoch = self.blocked_at; + + if shared + .blocked_at + .compare_exchange( + epoch, + NOT_BLOCKING, + Ordering::AcqRel, + Ordering::Acquire, + ) + .is_err() + { + // The monitor thread determined we took too long and we have to + // become a backup thread. + self.backup = true; + } + + self.blocked_at = NOT_BLOCKING; + } + fn run(&mut self, state: &State) { while self.pool.is_alive() { if self.backup { @@ -555,6 +580,15 @@ impl Thread { CURRENT_PROCESS.set(null_mut()); } + // It's possible that we finish work with an uneven number of calls to + // `start_blocking` and `stop_blocking`, such as when the developer + // didn't pay attention to the documentation telling them to do just + // that. + if self.blocked_nesting > 0 { + self.blocked_nesting = 0; + self.reset_blocked_at(); + } + match self.action.take() { Action::Terminate => { // Process termination can't be safely done on the process' @@ -1196,6 +1230,7 @@ mod tests { thread.start_blocking(); assert_eq!(thread.blocked_at, 4); + assert_eq!(thread.blocked_nesting, 1); assert_eq!(pool.threads[1].blocked_at.load(Ordering::Acquire), 4); assert_eq!(pool.monitor.status.load(), MonitorStatus::Notified); } @@ -1213,6 +1248,7 @@ mod tests { assert!(!thread.backup); assert!(pool.global.lock().unwrap().is_empty()); + assert_eq!(thread.blocked_nesting, 0); thread.start_blocking(); pool.threads[1].blocked_at.store(NOT_BLOCKING, Ordering::Release); @@ -1221,6 +1257,62 @@ mod tests { assert!(thread.backup); assert_eq!(thread.blocked_at, NOT_BLOCKING); assert_eq!(pool.global.lock().unwrap().len(), 1); + assert_eq!(thread.blocked_nesting, 0); + } + + #[test] + fn test_thread_start_blocking_nested() { + let state = setup(); + let pool = &state.scheduler.pool; + let mut thread = Thread::new(1, 0, pool.clone()); + let class = empty_process_class("A"); + let process = new_process(*class).take_and_forget(); + + thread.start_blocking(); + thread.start_blocking(); + thread.start_blocking(); + pool.threads[1].blocked_at.store(NOT_BLOCKING, Ordering::Release); + + thread.stop_blocking(process); + assert!(!thread.backup); + assert!(pool.global.lock().unwrap().is_empty()); + assert_eq!(thread.blocked_nesting, 2); + + thread.stop_blocking(process); + assert!(!thread.backup); + assert!(pool.global.lock().unwrap().is_empty()); + assert_eq!(thread.blocked_nesting, 1); + + thread.stop_blocking(process); + assert!(thread.backup); + assert_eq!(pool.global.lock().unwrap().len(), 1); + assert_eq!(thread.blocked_nesting, 0); + assert_eq!(thread.blocked_at, NOT_BLOCKING); + } + + #[test] + fn test_thread_start_blocking_without_stop_blocking() { + let class = empty_process_class("A"); + let proc = new_process_with_message(*class, method).take_and_forget(); + let state = setup(); + let pool = &state.scheduler.pool; + let mut thread = Thread::new(1, 0, pool.clone()); + + pool.epoch.store(4, Ordering::Release); + pool.monitor.status.store(MonitorStatus::Sleeping); + + thread.schedule(proc); + thread.start_blocking(); + pool.threads[1].blocked_at.store(NOT_BLOCKING, Ordering::Release); + thread.run(&state); + + assert!(thread.backup); + assert_eq!(thread.blocked_nesting, 0); + assert_eq!( + pool.threads[1].blocked_at.load(Ordering::Acquire), + NOT_BLOCKING + ); + assert_eq!(pool.monitor.status.load(), MonitorStatus::Notified); } #[test] diff --git a/std/src/std/alloc.inko b/std/src/std/alloc.inko new file mode 100644 index 000000000..9c364c763 --- /dev/null +++ b/std/src/std/alloc.inko @@ -0,0 +1,22 @@ +import std.libc.freebsd (self as libc) if freebsd +import std.libc.linux (self as libc) if linux +import std.libc.mac (self as libc) if mac + +fn free(pointer: Pointer[UInt8]) { + libc.free(pointer) +} + +fn resize[T](buffer: Pointer[T], size: Int) -> Pointer[T] { + let ptr = libc.realloc(buffer as Pointer[UInt8], size) + + # In this case there's nothing we can do but abort. + if ptr as Int == 0 and size != 0 { + panic('std.libc.unix.alloc.resize() failed to allocate memory') + } + + ptr as Pointer[T] +} + +fn copy[T](from: Pointer[T], to: Pointer[T], size: Int) { + libc.memmove(to as Pointer[UInt8], from as Pointer[UInt8], size) +} diff --git a/std/src/std/array.inko b/std/src/std/array.inko index a31962b83..f1b770009 100644 --- a/std/src/std/array.inko +++ b/std/src/std/array.inko @@ -1,11 +1,11 @@ # An ordered, integer-indexed generic collection of values. +import std.alloc import std.clone (Clone) import std.cmp (Compare, Contains, Equal, Ordering, max, min) import std.drop (Drop) import std.fmt (Format, Formatter) import std.hash (Hash, Hasher) import std.iter (Iter, Stream) -import std.libc.unix.alloc (self as alloc_imp) if unix import std.option (Option) import std.rand (Shuffle) @@ -113,7 +113,7 @@ class builtin Array[T] { if size < 0 { panic('The capacity must be greater than or equal to zero') } let vsize = _INKO.size_of_type_parameter(T) - let buffer = alloc_imp.resize(0x0 as Pointer[T], size: size * vsize) + let buffer = alloc.resize(0x0 as Pointer[T], size: size * vsize) Array(size: 0, capacity: size, buffer: buffer) } @@ -153,7 +153,7 @@ class builtin Array[T] { let vsize = _INKO.size_of_type_parameter(T) @capacity = max(@capacity * 2, @capacity + size) - @buffer = alloc_imp.resize(@buffer, @capacity * vsize) + @buffer = alloc.resize(@buffer, @capacity * vsize) } # Removes all values in the Array. @@ -250,7 +250,7 @@ class builtin Array[T] { let val = addr.0 let vsize = _INKO.size_of_type_parameter(T) - alloc_imp.copy( + alloc.copy( from: addr as Int + vsize as Pointer[T], to: addr, size: len - index - 1 * vsize, @@ -598,7 +598,7 @@ class builtin Array[T] { let to = address_of(index + 1) let vsize = _INKO.size_of_type_parameter(T) - alloc_imp.copy(from, to, size: @size - index * vsize) + alloc.copy(from, to, size: @size - index * vsize) } write_to(index, value) @@ -733,7 +733,7 @@ impl Array if T: mut { impl Drop for Array { fn mut drop { clear - alloc_imp.free(@buffer as Pointer[UInt8]) + alloc.free(@buffer as Pointer[UInt8]) } } diff --git a/std/src/std/fs/unix/dir.inko b/std/src/std/fs/unix/dir.inko index 7d0862ce2..703a87c22 100644 --- a/std/src/std/fs/unix/dir.inko +++ b/std/src/std/fs/unix/dir.inko @@ -7,10 +7,9 @@ import std.iter (Iter) # different names for `readdir()` (e.g. `readdir$INODE64` on macOS). To handle # this, platform specific code (e.g. calculating the offset of `dirent.d_name`) # is pushed into specific modules. -import std.libc.freebsd.dir (self as dir_imp) if freebsd -import std.libc.linux.dir (self as dir_imp) if linux -import std.libc.mac.amd64.dir (self as dir_imp) if mac and amd64 -import std.libc.mac.arm64.dir (self as dir_imp) if mac and arm64 +import std.libc.freebsd (self as libc) if freebsd +import std.libc.linux (self as libc) if linux +import std.libc.mac (self as libc) if mac # These constants are the same across the various Unix platforms we support, so # we can just define them here. @@ -18,10 +17,6 @@ let DT_DIR = 4 let DT_LNK = 10 let DT_REG = 8 -fn extern closedir(stream: Pointer[UInt8]) -> Int32 - -fn extern strlen(pointer: Pointer[UInt8]) -> UInt64 - fn extern inko_reset_error # The byte of the "." character. @@ -31,7 +26,7 @@ class ReadDirectory { let @stream: Pointer[UInt8] fn static new(path: String) -> Result[ReadDirectory, Error] { - let stream = dir_imp.opendir(path.to_pointer) + let stream = libc.opendir(path.to_pointer) if stream as Int != 0 { Result.Ok(ReadDirectory(stream)) @@ -48,7 +43,7 @@ impl Iter[Result[(String, FileType), Error]] for ReadDirectory { # don't reschedule the process until after the call and the `errno` check. inko_reset_error - let entry = dir_imp.readdir(@stream) + let entry = libc.readdir(@stream) if entry as Int == 0 { return match Error.last_os_error { @@ -57,8 +52,8 @@ impl Iter[Result[(String, FileType), Error]] for ReadDirectory { } } - let name_ptr = dir_imp.dirent_name(entry) - let name_len = strlen(name_ptr) as Int + let name_ptr = libc.dirent_name(entry) + let name_len = libc.strlen(name_ptr) as Int let first = name_ptr.0 as Int # Skip "." @@ -74,7 +69,7 @@ impl Iter[Result[(String, FileType), Error]] for ReadDirectory { } let name = String.from_pointer(name_ptr) - let type = match dir_imp.dirent_type(entry) { + let type = match libc.dirent_type(entry) { case DT_DIR -> FileType.Directory case DT_REG -> FileType.File case DT_LNK -> FileType.SymbolicLink @@ -90,6 +85,6 @@ impl Drop for ReadDirectory { fn mut drop { # closedir() only errors if the stream itself is invalid, which shouldn't be # the case at this point. - closedir(@stream) + libc.closedir(@stream) } } diff --git a/std/src/std/fs/unix/file.inko b/std/src/std/fs/unix/file.inko index a0409e0d8..e6fb35a65 100644 --- a/std/src/std/fs/unix/file.inko +++ b/std/src/std/fs/unix/file.inko @@ -1,28 +1,10 @@ import std.io (Error, start_blocking, stop_blocking) -import std.libc.bsd.file (self as file_imp) if bsd -import std.libc.linux.file (self as file_imp) if linux -import std.libc.mac.file (self as file_imp) if mac - -# TODO: remove -import std.stdio (STDOUT) +import std.libc.freebsd (self as libc) if freebsd +import std.libc.linux (self as libc) if linux +import std.libc.mac (self as libc) if mac let MODE = 0x1B6 # 666 -# Opens the file at `path` with a set of flags and an optional mode. See -# `open(2)` for more details. -# -# Since Inko requires a 64-bits platform, and all 64-bits platforms implement -# `open()` the same as `open64()`, we can just use `open()`. This also removes -# the need for dealing with some platforms that _don't_ offer `open64()` such as -# musl. -fn extern open(path: Pointer[UInt8], flags: Int32, ...) -> Int32 - -fn extern read(fd: Int32, buf: Pointer[UInt8], size: UInt64) -> Int - -fn extern lseek(fd: Int32, offset: Int, whence: Int32) -> Int - -fn extern close(fd: Int32) -> Int32 - fn new( path: String, read: Bool, @@ -30,20 +12,20 @@ fn new( append: Bool, ) -> Result[Int32, Error] { let path_ptr = path.to_pointer - let mut flags = file_imp.O_CLOEXEC + let mut flags = libc.O_CLOEXEC | if read and (write or append) { - file_imp.O_RDWR + libc.O_RDWR } else if write { - file_imp.O_WRONLY + libc.O_WRONLY } else { - file_imp.O_RDONLY + libc.O_RDONLY } - if append { flags |= file_imp.O_APPEND } + if append { flags |= libc.O_APPEND } start_blocking - let file = open(path_ptr, flags as Int32, MODE as Int32) + let file = libc.open(path_ptr, flags as Int32, MODE as Int32) let err = stop_blocking if file as Int >= 0 { @@ -55,7 +37,7 @@ fn new( fn drop(file: Int32) { start_blocking - close(file) + libc.close(file) stop_blocking } @@ -71,7 +53,7 @@ fn read_from( start_blocking - let res = read(file, ptr, size as UInt64) + let res = libc.read(file, ptr, size as UInt64) let err = stop_blocking if res >= 0 { @@ -83,12 +65,11 @@ fn read_from( } fn seek_to(file: Int32, position: Int) -> Result[Int, Error] { - let whence = if position < 0 { file_imp.SEEK_END } else { file_imp.SEEK_SET } - as Int32 + let whence = if position < 0 { libc.SEEK_END } else { libc.SEEK_SET } as Int32 start_blocking - let res = lseek(file, position, whence) + let res = libc.lseek(file, position, whence) let err = stop_blocking if res >= 0 { Result.Ok(res) } else { Result.Error(Error.from_os_error(err)) } diff --git a/std/src/std/io.inko b/std/src/std/io.inko index 9c2ccd495..2e0bf7335 100644 --- a/std/src/std/io.inko +++ b/std/src/std/io.inko @@ -6,9 +6,9 @@ import std.cmp (Equal) import std.drop (Drop) import std.fmt (Format, Formatter) import std.iter (Stream) -import std.libc.bsd.errors if bsd -import std.libc.linux.errors if linux -import std.libc.mac.errors if mac +import std.libc.freebsd (self as libc) if freebsd +import std.libc.linux (self as libc) if linux +import std.libc.mac (self as libc) if mac import std.string (Bytes, ToString) # The initial number of bytes to read in `Read.read_all` @@ -38,21 +38,59 @@ fn extern inko_process_start_blocking(process: Pointer[UInt8]) fn extern inko_process_stop_blocking(process: Pointer[UInt8]) -# Signals the start of a blocking IO call. +# Signals the start of a C function call that may perform blocking IO +# operations. # -# After calling this method the next call should be restricted to a C function -# call, as Inko code might reschedule the process, resulting in the current -# thread being marked as blocking for too long. -fn start_blocking { +# This method should be called immediately before a C function call that might +# block the current OS thread. It _must not_ be used for anything else. +# +# If this method is called multiple times in the same process, it is required +# for there to be an equal number of calls to `stop_blocking`. For example, this +# is OK: +# +# ```inko +# import std.io (start_blocking, stop_blocking) +# +# start_blocking +# start_blocking +# perform_c_function_call(...) +# stop_blocking +# stop_blocking +# ``` +# +# # Examples +# +# ```inko +# import std.io (Error, start_blocking, stop_blocking) +# +# fn extern open(path: Pointer[UInt8], flags: Int32, ...) -> Int32 +# +# start_blocking +# +# let fd = open('test.txt'.to_pointer, 0 as Int32, 0 as Int32) +# let err = stop_blocking +# +# if fd as Int == -1 { +# panic('failed to open the file: ${Error.from_os_error(err)}') +# } +# ``` +fn pub start_blocking { inko_process_start_blocking(_INKO.process) } -# Signals the end of a blocking IO call. +# Signals the end of a C function call that may perform blocking IO operations. # # The return value is the value of `errno` as an `Int`. This value is read # _before_ signalling the end of the blocking call, ensuring we read the value # from the same thread the IO call is performed on. -fn stop_blocking -> Int { +# +# If `start_blocking` is called multiple times, this method _must_ be called the +# same number of times. In this case only the _last_ call may reschedule the +# current process. If a C function call mutates any global state, that state +# _must_ be retrieved before the final call to this method, as upon being +# rescheduled the current process may run on a different OS thread and thus read +# a different state. +fn pub stop_blocking -> Int { let err = inko_last_error # If the operation took too long, this reschedules the current process. This @@ -187,39 +225,39 @@ class pub enum Error { # ``` fn pub static from_os_error(code: Int) -> Error { match code { - case errors.EPERM -> Error.PermissionDenied - case errors.ENOENT -> Error.NotFound - case errors.EINTR -> Error.Interrupted - case errors.EAGAIN -> Error.WouldBlock - case errors.ENOMEM -> Error.OutOfMemory - case errors.EACCES -> Error.PermissionDenied - case errors.EBUSY -> Error.ResourceBusy - case errors.EEXIST -> Error.AlreadyExists - case errors.ENOTDIR -> Error.NotADirectory - case errors.EISDIR -> Error.IsADirectory - case errors.EINVAL -> Error.InvalidArgument - case errors.EFBIG -> Error.FileTooLarge - case errors.ENOSPC -> Error.StorageFull - case errors.ESPIPE -> Error.InvalidSeek - case errors.EROFS -> Error.ReadOnlyFilesystem - case errors.EPIPE -> Error.BrokenPipe - case errors.EDEADLK -> Error.Deadlock - case errors.ENAMETOOLONG -> Error.InvalidFileName - case errors.ENOTEMPTY -> Error.DirectoryNotEmpty - case errors.ETIME -> Error.TimedOut - case errors.EADDRINUSE -> Error.AddressInUse - case errors.EADDRNOTAVAIL -> Error.AddressUnavailable - case errors.ENETDOWN -> Error.NetworkDown - case errors.ENETUNREACH -> Error.NetworkDown - case errors.ECONNABORTED -> Error.ConnectionAborted - case errors.ECONNRESET -> Error.ConnectionReset - case errors.EISCONN -> Error.AlreadyConnected - case errors.ENOTCONN -> Error.NotConnected - case errors.ETIMEDOUT -> Error.TimedOut - case errors.ECONNREFUSED -> Error.ConnectionRefused - case errors.EHOSTUNREACH -> Error.HostUnreachable - case errors.EINPROGRESS -> Error.InProgress - case errors.EFAULT -> Error.BadAddress + case libc.EPERM -> Error.PermissionDenied + case libc.ENOENT -> Error.NotFound + case libc.EINTR -> Error.Interrupted + case libc.EAGAIN -> Error.WouldBlock + case libc.ENOMEM -> Error.OutOfMemory + case libc.EACCES -> Error.PermissionDenied + case libc.EBUSY -> Error.ResourceBusy + case libc.EEXIST -> Error.AlreadyExists + case libc.ENOTDIR -> Error.NotADirectory + case libc.EISDIR -> Error.IsADirectory + case libc.EINVAL -> Error.InvalidArgument + case libc.EFBIG -> Error.FileTooLarge + case libc.ENOSPC -> Error.StorageFull + case libc.ESPIPE -> Error.InvalidSeek + case libc.EROFS -> Error.ReadOnlyFilesystem + case libc.EPIPE -> Error.BrokenPipe + case libc.EDEADLK -> Error.Deadlock + case libc.ENAMETOOLONG -> Error.InvalidFileName + case libc.ENOTEMPTY -> Error.DirectoryNotEmpty + case libc.ETIME -> Error.TimedOut + case libc.EADDRINUSE -> Error.AddressInUse + case libc.EADDRNOTAVAIL -> Error.AddressUnavailable + case libc.ENETDOWN -> Error.NetworkDown + case libc.ENETUNREACH -> Error.NetworkDown + case libc.ECONNABORTED -> Error.ConnectionAborted + case libc.ECONNRESET -> Error.ConnectionReset + case libc.EISCONN -> Error.AlreadyConnected + case libc.ENOTCONN -> Error.NotConnected + case libc.ETIMEDOUT -> Error.TimedOut + case libc.ECONNREFUSED -> Error.ConnectionRefused + case libc.EHOSTUNREACH -> Error.HostUnreachable + case libc.EINPROGRESS -> Error.InProgress + case libc.EFAULT -> Error.BadAddress case INVALID_DATA -> Error.InvalidData case UNEXPECTED_EOF -> Error.EndOfInput case val -> Error.Other(val) diff --git a/std/src/std/libc/bsd/errors.inko b/std/src/std/libc/bsd/errors.inko deleted file mode 100644 index 832d037bb..000000000 --- a/std/src/std/libc/bsd/errors.inko +++ /dev/null @@ -1,37 +0,0 @@ -# BSD specific error codes. -let EPERM = 1 -let ENOENT = 2 -let EINTR = 4 -let EDEADLK = 11 -let ENOMEM = 12 -let EACCES = 13 -let EFAULT = 14 -let EBUSY = 16 -let EEXIST = 17 -let ENOTDIR = 20 -let EISDIR = 21 -let EINVAL = 22 -let EFBIG = 27 -let ENOSPC = 28 -let ESPIPE = 29 -let EROFS = 30 -let EPIPE = 32 -let EAGAIN = 35 -let EINPROGRESS = 36 -let EADDRINUSE = 48 -let EADDRNOTAVAIL = 49 -let ENETDOWN = 50 -let ENETUNREACH = 51 -let ECONNABORTED = 53 -let ECONNRESET = 54 -let EISCONN = 56 -let ENOTCONN = 57 -let ETIMEDOUT = 60 -let ECONNREFUSED = 61 -let ENAMETOOLONG = 63 -let EHOSTUNREACH = 65 -let ENOTEMPTY = 66 - -# BSDs don't define this constant, but we still define it here to make it easier -# to handle platform differences. -let ETIME = -1 diff --git a/std/src/std/libc/bsd/file.inko b/std/src/std/libc/bsd/file.inko deleted file mode 100644 index 0bbc49cd3..000000000 --- a/std/src/std/libc/bsd/file.inko +++ /dev/null @@ -1,9 +0,0 @@ -let O_RDONLY = 0 -let O_WRONLY = 0x1 -let O_RDWR = 0x2 -let O_CREAT = 0x200 -let O_TRUNC = 0x400 -let O_APPEND = 0x8 -let O_CLOEXEC = 0x100000 -let SEEK_SET = 0 -let SEEK_END = 2 diff --git a/std/src/std/libc/bsd/socket.inko b/std/src/std/libc/bsd/socket.inko deleted file mode 100644 index b9881ba07..000000000 --- a/std/src/std/libc/bsd/socket.inko +++ /dev/null @@ -1,14 +0,0 @@ -let IPPROTO_IP = 0 -let IPPROTO_IPV6 = 41 -let IPPROTO_TCP = 6 -let IPV6_V6ONLY = 27 -let IP_TTL = 4 -let SOL_SOCKET = 0xFFFF -let SO_BROADCAST = 0x20 -let SO_KEEPALIVE = 0x8 -let SO_LINGER = 0x80 -let SO_RCVBUF = 0x1002 -let SO_REUSEADDR = 0x4 -let SO_REUSEPORT = 0x200 -let SO_SNDBUF = 0x1001 -let TCP_NODELAY = 1 diff --git a/std/src/std/libc/freebsd.inko b/std/src/std/libc/freebsd.inko new file mode 100644 index 000000000..e170d7f11 --- /dev/null +++ b/std/src/std/libc/freebsd.inko @@ -0,0 +1,133 @@ +let EACCES = 13 +let EADDRINUSE = 48 +let EADDRNOTAVAIL = 49 +let EAGAIN = 35 +let EBUSY = 16 +let ECONNABORTED = 53 +let ECONNREFUSED = 61 +let ECONNRESET = 54 +let EDEADLK = 11 +let EEXIST = 17 +let EFAULT = 14 +let EFBIG = 27 +let EHOSTUNREACH = 65 +let EINPROGRESS = 36 +let EINTR = 4 +let EINVAL = 22 +let EISCONN = 56 +let EISDIR = 21 +let ENAMETOOLONG = 63 +let ENETDOWN = 50 +let ENETUNREACH = 51 +let ENOENT = 2 +let ENOMEM = 12 +let ENOSPC = 28 +let ENOTCONN = 57 +let ENOTDIR = 20 +let ENOTEMPTY = 66 +let EPERM = 1 +let EPIPE = 32 +let EROFS = 30 +let ESPIPE = 29 +let ETIMEDOUT = 60 +let IPPROTO_IP = 0 +let IPPROTO_IPV6 = 41 +let IPPROTO_TCP = 6 +let IPV6_V6ONLY = 27 +let IP_TTL = 4 +let O_APPEND = 0x8 +let O_CLOEXEC = 0x100000 +let O_CREAT = 0x200 +let O_RDONLY = 0 +let O_RDWR = 0x2 +let O_TRUNC = 0x400 +let O_WRONLY = 0x1 +let SEEK_END = 2 +let SEEK_SET = 0 +let SOL_SOCKET = 0xFFFF +let SO_BROADCAST = 0x20 +let SO_KEEPALIVE = 0x8 +let SO_LINGER = 0x80 +let SO_RCVBUF = 0x1002 +let SO_REUSEADDR = 0x4 +let SO_REUSEPORT = 0x200 +let SO_SNDBUF = 0x1001 +let TCP_NODELAY = 1 + +# FreeBSD doesn't define this constant, but we still define it here to make it +# easier to handle platform differences. +let ETIME = -1 + +class extern Dirent { + let @d_fileno: UInt64 + let @d_off: UInt64 + let @d_reclen: UInt16 + let @d_type: UInt8 + let @d_namlen: UInt8 + let @d_pad0: UInt32 +} + +class extern Stat { + let @st_dev: UInt32 + let @st_ino: UInt32 + let @st_mode: UInt16 + let @st_nlink: UInt16 + let @st_uid: UInt32 + let @st_gid: UInt32 + let @st_rdev: UInt32 + let @st_atime: Int64 + let @st_atime_nsec: Int64 + let @st_mtime: Int64 + let @st_mtime_nsec: Int64 + let @st_ctime: Int64 + let @st_ctime_nsec: Int64 + let @st_size: Int64 + let @st_blocks: Int64 + let @st_blksize: Int32 + let @st_flags: UInt32 + let @st_gen: UInt32 + let @st_lspare: Int32 + let @st_birthtime: Int64 + let @st_birthtime_nsec: Int64 +} + +fn extern opendir(path: Pointer[UInt8]) -> Pointer[UInt8] + +fn extern readdir(stream: Pointer[UInt8]) -> Pointer[UInt8] + +fn extern closedir(stream: Pointer[UInt8]) -> Int32 + +# Opens the file at `path` with a set of flags and an optional mode. See +# `open(2)` for more details. +# +# Since Inko requires a 64-bits platform, and all 64-bits platforms implement +# `open()` the same as `open64()`, we can just use `open()`. This also removes +# the need for dealing with some platforms that _don't_ offer `open64()` such as +# musl. +fn extern open(path: Pointer[UInt8], flags: Int32, ...) -> Int32 + +fn extern read(fd: Int32, buf: Pointer[UInt8], size: UInt64) -> Int + +fn extern lseek(fd: Int32, offset: Int, whence: Int32) -> Int + +fn extern close(fd: Int32) -> Int32 + +fn extern strlen(pointer: Pointer[UInt8]) -> UInt64 + +fn extern realloc(pointer: Pointer[UInt8], size: Int) -> Pointer[UInt8] + +fn extern memmove( + to: Pointer[UInt8], + from: Pointer[UInt8], + size: Int, +) -> Pointer[UInt8] + +fn extern free(pointer: Pointer[UInt8]) + +fn dirent_type(pointer: Pointer[UInt8]) -> Int { + (pointer as Pointer[Dirent]).d_type as Int +} + +fn dirent_name(pointer: Pointer[UInt8]) -> Pointer[UInt8] { + pointer as Int + 24 as Pointer[UInt8] +} diff --git a/std/src/std/libc/freebsd/dir.inko b/std/src/std/libc/freebsd/dir.inko deleted file mode 100644 index 7d117d6d9..000000000 --- a/std/src/std/libc/freebsd/dir.inko +++ /dev/null @@ -1,20 +0,0 @@ -class extern Dirent { - let @d_fileno: UInt64 - let @d_off: UInt64 - let @d_reclen: UInt16 - let @d_type: UInt8 - let @d_namlen: UInt8 - let @d_pad0: UInt32 -} - -fn extern opendir(path: Pointer[UInt8]) -> Pointer[UInt8] - -fn extern readdir(stream: Pointer[UInt8]) -> Pointer[UInt8] - -fn dirent_type(pointer: Pointer[UInt8]) -> Int { - (pointer as Pointer[Dirent]).d_type as Int -} - -fn dirent_name(pointer: Pointer[UInt8]) -> Pointer[UInt8] { - pointer as Int + 24 as Pointer[UInt8] -} diff --git a/std/src/std/libc/linux.inko b/std/src/std/libc/linux.inko new file mode 100644 index 000000000..8652bfe0d --- /dev/null +++ b/std/src/std/libc/linux.inko @@ -0,0 +1,104 @@ +let EACCES = 13 +let EADDRINUSE = 98 +let EADDRNOTAVAIL = 99 +let EAGAIN = 11 +let EBUSY = 16 +let ECONNABORTED = 103 +let ECONNREFUSED = 111 +let ECONNRESET = 104 +let EDEADLK = 35 +let EEXIST = 17 +let EFAULT = 14 +let EFBIG = 27 +let EHOSTUNREACH = 113 +let EINPROGRESS = 115 +let EINTR = 4 +let EINVAL = 22 +let EISCONN = 106 +let EISDIR = 21 +let ENAMETOOLONG = 36 +let ENETDOWN = 100 +let ENETUNREACH = 101 +let ENOENT = 2 +let ENOMEM = 12 +let ENOSPC = 28 +let ENOTCONN = 107 +let ENOTDIR = 20 +let ENOTEMPTY = 39 +let EPERM = 1 +let EPIPE = 32 +let EROFS = 30 +let ESPIPE = 29 +let ETIME = 62 +let ETIMEDOUT = 110 +let IPPROTO_IP = 0 +let IPPROTO_IPV6 = 41 +let IPPROTO_TCP = 6 +let IPV6_V6ONLY = 26 +let IP_TTL = 2 +let O_APPEND = 0x400 +let O_CLOEXEC = 0x80000 +let O_CREAT = 0x40 +let O_RDONLY = 0 +let O_RDWR = 0x2 +let O_TRUNC = 0x200 +let O_WRONLY = 0x1 +let SEEK_END = 2 +let SEEK_SET = 0 +let SOL_SOCKET = 1 +let SO_BROADCAST = 6 +let SO_KEEPALIVE = 9 +let SO_LINGER = 13 +let SO_RCVBUF = 8 +let SO_REUSEADDR = 2 +let SO_REUSEPORT = 15 +let SO_SNDBUF = 7 +let TCP_NODELAY = 1 + +class extern Dirent { + let @d_ino: UInt64 + let @d_off: UInt64 + let @d_reclen: UInt16 + let @d_type: UInt8 +} + +fn extern opendir(path: Pointer[UInt8]) -> Pointer[UInt8] + +fn extern readdir(stream: Pointer[UInt8]) -> Pointer[UInt8] + +fn extern closedir(stream: Pointer[UInt8]) -> Int32 + +# Opens the file at `path` with a set of flags and an optional mode. See +# `open(2)` for more details. +# +# Since Inko requires a 64-bits platform, and all 64-bits platforms implement +# `open()` the same as `open64()`, we can just use `open()`. This also removes +# the need for dealing with some platforms that _don't_ offer `open64()` such as +# musl. +fn extern open(path: Pointer[UInt8], flags: Int32, ...) -> Int32 + +fn extern read(fd: Int32, buf: Pointer[UInt8], size: UInt64) -> Int + +fn extern lseek(fd: Int32, offset: Int, whence: Int32) -> Int + +fn extern close(fd: Int32) -> Int32 + +fn extern strlen(pointer: Pointer[UInt8]) -> UInt64 + +fn extern realloc(pointer: Pointer[UInt8], size: Int) -> Pointer[UInt8] + +fn extern memmove( + to: Pointer[UInt8], + from: Pointer[UInt8], + size: Int, +) -> Pointer[UInt8] + +fn extern free(pointer: Pointer[UInt8]) + +fn dirent_type(pointer: Pointer[UInt8]) -> Int { + (pointer as Pointer[Dirent]).d_type as Int +} + +fn dirent_name(pointer: Pointer[UInt8]) -> Pointer[UInt8] { + pointer as Int + 19 as Pointer[UInt8] +} diff --git a/std/src/std/libc/linux/amd64/gnu.inko b/std/src/std/libc/linux/amd64/gnu.inko new file mode 100644 index 000000000..b2a0b0819 --- /dev/null +++ b/std/src/std/libc/linux/amd64/gnu.inko @@ -0,0 +1,23 @@ +# TODO: replace with statx +class extern Stat { + let @st_dev: UInt64 + let @st_ino: UInt64 + let @st_nlink: UInt64 + let @st_mode: UInt32 + let @st_uid: UInt32 + let @st_gid: UInt32 + let @__pad0: Int32 + let @st_rdev: UInt64 + let @st_size: Int64 + let @st_blksize: Int64 + let @st_blocks: Int64 + let @st_atime: Int64 + let @st_atime_nsec: Int64 + let @st_mtime: Int64 + let @st_mtime_nsec: Int64 + let @st_ctime: Int64 + let @st_ctime_nsec: Int64 + let @__reserved0: Int64 + let @__reserved1: Int64 + let @__reserved2: Int64 +} diff --git a/std/src/std/libc/linux/amd64/musl.inko b/std/src/std/libc/linux/amd64/musl.inko new file mode 100644 index 000000000..b2a0b0819 --- /dev/null +++ b/std/src/std/libc/linux/amd64/musl.inko @@ -0,0 +1,23 @@ +# TODO: replace with statx +class extern Stat { + let @st_dev: UInt64 + let @st_ino: UInt64 + let @st_nlink: UInt64 + let @st_mode: UInt32 + let @st_uid: UInt32 + let @st_gid: UInt32 + let @__pad0: Int32 + let @st_rdev: UInt64 + let @st_size: Int64 + let @st_blksize: Int64 + let @st_blocks: Int64 + let @st_atime: Int64 + let @st_atime_nsec: Int64 + let @st_mtime: Int64 + let @st_mtime_nsec: Int64 + let @st_ctime: Int64 + let @st_ctime_nsec: Int64 + let @__reserved0: Int64 + let @__reserved1: Int64 + let @__reserved2: Int64 +} diff --git a/std/src/std/libc/linux/arm64/gnu.inko b/std/src/std/libc/linux/arm64/gnu.inko new file mode 100644 index 000000000..62665be4c --- /dev/null +++ b/std/src/std/libc/linux/arm64/gnu.inko @@ -0,0 +1,23 @@ +# TODO: replace with statx +class extern Stat { + let @st_dev: UInt64 + let @st_ino: UInt64 + let @st_mode: UInt32 + let @st_nlink: UInt32 + let @st_uid: UInt32 + let @st_gid: UInt32 + let @st_rdev: UInt64 + let @__pad0: UInt64 + let @st_size: Int64 + let @st_blksize: Int32 + let @__pad1: Int32 + let @st_blocks: Int64 + let @st_atime: Int64 + let @st_atime_nsec: Int64 + let @st_mtime: Int64 + let @st_mtime_nsec: Int64 + let @st_ctime: Int64 + let @st_ctime_nsec: Int64 + let @__unused0: Int32 + let @__unused1: Int32 +} diff --git a/std/src/std/libc/linux/arm64/musl.inko b/std/src/std/libc/linux/arm64/musl.inko new file mode 100644 index 000000000..d911e9499 --- /dev/null +++ b/std/src/std/libc/linux/arm64/musl.inko @@ -0,0 +1,23 @@ +# TODO: replace with statx? +class extern Stat { + let @st_dev: UInt64 + let @st_ino: UInt64 + let @st_mode: UInt32 + let @st_nlink: UInt32 + let @st_uid: UInt32 + let @st_gid: UInt32 + let @st_rdev: UInt64 + let @__pad0: UInt64 + let @st_size: Int64 + let @st_blksize: Int32 + let @__pad1: Int32 + let @st_blocks: Int64 + let @st_atime: Int64 + let @st_atime_nsec: Int64 + let @st_mtime: Int64 + let @st_mtime_nsec: Int64 + let @st_ctime: Int64 + let @st_ctime_nsec: Int64 + let @__unused0: Int32 + let @__unused1: Int32 +} diff --git a/std/src/std/libc/linux/dir.inko b/std/src/std/libc/linux/dir.inko deleted file mode 100644 index a8d2cc86b..000000000 --- a/std/src/std/libc/linux/dir.inko +++ /dev/null @@ -1,18 +0,0 @@ -class extern Dirent { - let @d_ino: UInt64 - let @d_off: UInt64 - let @d_reclen: UInt16 - let @d_type: UInt8 -} - -fn extern opendir(path: Pointer[UInt8]) -> Pointer[UInt8] - -fn extern readdir(stream: Pointer[UInt8]) -> Pointer[UInt8] - -fn dirent_type(pointer: Pointer[UInt8]) -> Int { - (pointer as Pointer[Dirent]).d_type as Int -} - -fn dirent_name(pointer: Pointer[UInt8]) -> Pointer[UInt8] { - pointer as Int + 19 as Pointer[UInt8] -} diff --git a/std/src/std/libc/linux/errors.inko b/std/src/std/libc/linux/errors.inko deleted file mode 100644 index 9c333050d..000000000 --- a/std/src/std/libc/linux/errors.inko +++ /dev/null @@ -1,34 +0,0 @@ -# Linux specific error codes. -let EPERM = 1 -let ENOENT = 2 -let EINTR = 4 -let EAGAIN = 11 -let ENOMEM = 12 -let EACCES = 13 -let EFAULT = 14 -let EBUSY = 16 -let EEXIST = 17 -let ENOTDIR = 20 -let EISDIR = 21 -let EINVAL = 22 -let EFBIG = 27 -let ENOSPC = 28 -let ESPIPE = 29 -let EROFS = 30 -let EPIPE = 32 -let EDEADLK = 35 -let ENAMETOOLONG = 36 -let ENOTEMPTY = 39 -let ETIME = 62 -let EADDRINUSE = 98 -let EADDRNOTAVAIL = 99 -let ENETDOWN = 100 -let ENETUNREACH = 101 -let ECONNABORTED = 103 -let ECONNRESET = 104 -let EISCONN = 106 -let ENOTCONN = 107 -let ETIMEDOUT = 110 -let ECONNREFUSED = 111 -let EHOSTUNREACH = 113 -let EINPROGRESS = 115 diff --git a/std/src/std/libc/linux/file.inko b/std/src/std/libc/linux/file.inko deleted file mode 100644 index d33775fa5..000000000 --- a/std/src/std/libc/linux/file.inko +++ /dev/null @@ -1,9 +0,0 @@ -let O_RDONLY = 0 -let O_WRONLY = 0x1 -let O_RDWR = 0x2 -let O_CREAT = 0x40 -let O_TRUNC = 0x200 -let O_APPEND = 0x400 -let O_CLOEXEC = 0x80000 -let SEEK_SET = 0 -let SEEK_END = 2 diff --git a/std/src/std/libc/linux/socket.inko b/std/src/std/libc/linux/socket.inko deleted file mode 100644 index cedc47304..000000000 --- a/std/src/std/libc/linux/socket.inko +++ /dev/null @@ -1,14 +0,0 @@ -let IPPROTO_IP = 0 -let IPPROTO_IPV6 = 41 -let IPPROTO_TCP = 6 -let IPV6_V6ONLY = 26 -let IP_TTL = 2 -let SOL_SOCKET = 1 -let SO_BROADCAST = 6 -let SO_KEEPALIVE = 9 -let SO_LINGER = 13 -let SO_RCVBUF = 8 -let SO_REUSEADDR = 2 -let SO_REUSEPORT = 15 -let SO_SNDBUF = 7 -let TCP_NODELAY = 1 diff --git a/std/src/std/libc/mac.inko b/std/src/std/libc/mac.inko new file mode 100644 index 000000000..f4b85c14a --- /dev/null +++ b/std/src/std/libc/mac.inko @@ -0,0 +1,142 @@ +import std.libc.mac.amd64 (self as arch) if amd64 +import std.libc.mac.arm64 (self as arch) if arm64 + +let EACCES = 13 +let EADDRINUSE = 48 +let EADDRNOTAVAIL = 49 +let EAGAIN = 35 +let EBUSY = 16 +let ECONNABORTED = 53 +let ECONNREFUSED = 61 +let ECONNRESET = 54 +let EDEADLK = 11 +let EEXIST = 17 +let EFAULT = 14 +let EFBIG = 27 +let EHOSTUNREACH = 65 +let EINPROGRESS = 36 +let EINTR = 4 +let EINVAL = 22 +let EISCONN = 56 +let EISDIR = 21 +let ENAMETOOLONG = 63 +let ENETDOWN = 50 +let ENETUNREACH = 51 +let ENOENT = 2 +let ENOMEM = 12 +let ENOSPC = 28 +let ENOTCONN = 57 +let ENOTDIR = 20 +let ENOTEMPTY = 66 +let EPERM = 1 +let EPIPE = 32 +let EROFS = 30 +let ESPIPE = 29 +let ETIME = 101 +let ETIMEDOUT = 60 +let IPPROTO_IP = 0 +let IPPROTO_IPV6 = 41 +let IPPROTO_TCP = 6 +let IPV6_V6ONLY = 27 +let IP_TTL = 4 +let O_APPEND = 0x8 +let O_CLOEXEC = 0x1000000 +let O_CREAT = 0x200 +let O_RDONLY = 0 +let O_RDWR = 0x2 +let O_TRUNC = 0x400 +let O_WRONLY = 0x1 +let SEEK_END = 2 +let SEEK_SET = 0 +let SOL_SOCKET = 0xFFFF +let SO_BROADCAST = 0x20 +let SO_KEEPALIVE = 0x8 +let SO_LINGER_SEC = 0x1080 +let SO_RCVBUF = 0x1002 +let SO_REUSEADDR = 0x4 +let SO_REUSEPORT = 0x200 +let SO_SNDBUF = 0x1001 +let TCP_NODELAY = 1 + +# For macOS we need to use `SO_LINGER_SEC` to control the time in seconds +# instead of ticks, and `SO_LINGER` itself isn't useful. +let SO_LINGER = SO_LINGER_SEC + +class extern Dirent { + let @d_ino: UInt64 + let @d_seekoff: UInt64 + let @d_reclen: UInt16 + let @d_namlen: UInt16 + let @d_type: UInt8 +} + +class extern Stat { + let @st_dev: Int32 + let @st_mode: UInt16 + let @st_nlink: UInt16 + let @st_ino: UInt64 + let @st_uid: UInt32 + let @st_gid: UInt32 + let @st_rdev: Int32 + let @st_atime: Int64 + let @st_atime_nsec: Int64 + let @st_mtime: Int64 + let @st_mtime_nsec: Int64 + let @st_ctime: Int64 + let @st_ctime_nsec: Int64 + let @st_birthtime: Int64 + let @st_birthtime_nsec: Int64 + let @st_size: Int64 + let @st_blocks: Int64 + let @st_blksize: Int32 + let @st_flags: UInt32 + let @st_gen: UInt32 + let @st_lspare: Int32 + let @st_qspare0: Int64 + let @st_qspare1: Int64 +} + +fn opendir(path: Pointer[UInt8]) -> Pointer[UInt8] { + arch.opendir(path) +} + +fn readdir(path: Pointer[UInt8]) -> Pointer[UInt8] { + arch.readdir(path) +} + +fn extern closedir(stream: Pointer[UInt8]) -> Int32 + +# Opens the file at `path` with a set of flags and an optional mode. See +# `open(2)` for more details. +# +# Since Inko requires a 64-bits platform, and all 64-bits platforms implement +# `open()` the same as `open64()`, we can just use `open()`. This also removes +# the need for dealing with some platforms that _don't_ offer `open64()` such as +# musl. +fn extern open(path: Pointer[UInt8], flags: Int32, ...) -> Int32 + +fn extern read(fd: Int32, buf: Pointer[UInt8], size: UInt64) -> Int + +fn extern lseek(fd: Int32, offset: Int, whence: Int32) -> Int + +fn extern close(fd: Int32) -> Int32 + +fn extern strlen(pointer: Pointer[UInt8]) -> UInt64 + +fn extern realloc(pointer: Pointer[UInt8], size: Int) -> Pointer[UInt8] + +fn extern memmove( + to: Pointer[UInt8], + from: Pointer[UInt8], + size: Int, +) -> Pointer[UInt8] + +fn extern free(pointer: Pointer[UInt8]) + +fn dirent_type(pointer: Pointer[UInt8]) -> Int { + (pointer as Pointer[Dirent]).d_type as Int +} + +fn dirent_name(pointer: Pointer[UInt8]) -> Pointer[UInt8] { + pointer as Int + 21 as Pointer[UInt8] +} diff --git a/std/src/std/libc/mac/amd64/dir.inko b/std/src/std/libc/mac/amd64.inko similarity index 63% rename from std/src/std/libc/mac/amd64/dir.inko rename to std/src/std/libc/mac/amd64.inko index 15cb0dc76..b3cb1077e 100644 --- a/std/src/std/libc/mac/amd64/dir.inko +++ b/std/src/std/libc/mac/amd64.inko @@ -1,11 +1,3 @@ -class extern Dirent { - let @d_ino: UInt64 - let @d_seekoff: UInt64 - let @d_reclen: UInt16 - let @d_namlen: UInt16 - let @d_type: UInt8 -} - # When using macOS on an amd64 platform, opendir/readdir produce data using # 32-bit inodes, unless we use the $INODE64 suffix. See the following for more # information: @@ -23,11 +15,3 @@ fn opendir(path: Pointer[UInt8]) -> Pointer[UInt8] { fn readdir(path: Pointer[UInt8]) -> Pointer[UInt8] { readdir$INODE64(path) } - -fn dirent_type(pointer: Pointer[UInt8]) -> Int { - (pointer as Pointer[Dirent]).d_type as Int -} - -fn dirent_name(pointer: Pointer[UInt8]) -> Pointer[UInt8] { - pointer as Int + 21 as Pointer[UInt8] -} diff --git a/std/src/std/libc/mac/arm64.inko b/std/src/std/libc/mac/arm64.inko new file mode 100644 index 000000000..640e44260 --- /dev/null +++ b/std/src/std/libc/mac/arm64.inko @@ -0,0 +1,4 @@ +# Unlike amd64, macOS on arm64 doesn't need/use the $INODE64 suffixes. +fn extern opendir(path: Pointer[UInt8]) -> Pointer[UInt8] + +fn extern readdir(stream: Pointer[UInt8]) -> Pointer[UInt8] diff --git a/std/src/std/libc/mac/arm64/dir.inko b/std/src/std/libc/mac/arm64/dir.inko deleted file mode 100644 index ba48cc4be..000000000 --- a/std/src/std/libc/mac/arm64/dir.inko +++ /dev/null @@ -1,20 +0,0 @@ -class extern Dirent { - let @d_ino: UInt64 - let @d_seekoff: UInt64 - let @d_reclen: UInt16 - let @d_namlen: UInt16 - let @d_type: UInt8 -} - -# Unlike amd64, macOS on arm64 doesn't need/use the $INODE64 suffixes. -fn extern opendir(path: Pointer[UInt8]) -> Pointer[UInt8] - -fn extern readdir(stream: Pointer[UInt8]) -> Pointer[UInt8] - -fn dirent_type(pointer: Pointer[UInt8]) -> Int { - (pointer as Pointer[Dirent]).d_type as Int -} - -fn dirent_name(pointer: Pointer[UInt8]) -> Pointer[UInt8] { - pointer as Int + 21 as Pointer[UInt8] -} diff --git a/std/src/std/libc/mac/errors.inko b/std/src/std/libc/mac/errors.inko deleted file mode 100644 index 4b8b9f39d..000000000 --- a/std/src/std/libc/mac/errors.inko +++ /dev/null @@ -1,34 +0,0 @@ -# macOS specific error codes. -let EPERM = 1 -let ENOENT = 2 -let EINTR = 4 -let EDEADLK = 11 -let ENOMEM = 12 -let EACCES = 13 -let EFAULT = 14 -let EBUSY = 16 -let EEXIST = 17 -let ENOTDIR = 20 -let EISDIR = 21 -let EINVAL = 22 -let EFBIG = 27 -let ENOSPC = 28 -let ESPIPE = 29 -let EROFS = 30 -let EPIPE = 32 -let EAGAIN = 35 -let EINPROGRESS = 36 -let EADDRINUSE = 48 -let EADDRNOTAVAIL = 49 -let ENETDOWN = 50 -let ENETUNREACH = 51 -let ECONNABORTED = 53 -let ECONNRESET = 54 -let EISCONN = 56 -let ENOTCONN = 57 -let ETIMEDOUT = 60 -let ECONNREFUSED = 61 -let ENAMETOOLONG = 63 -let EHOSTUNREACH = 65 -let ENOTEMPTY = 66 -let ETIME = 101 diff --git a/std/src/std/libc/mac/file.inko b/std/src/std/libc/mac/file.inko deleted file mode 100644 index 0580291c2..000000000 --- a/std/src/std/libc/mac/file.inko +++ /dev/null @@ -1,9 +0,0 @@ -let O_RDONLY = 0 -let O_WRONLY = 0x1 -let O_RDWR = 0x2 -let O_CREAT = 0x200 -let O_TRUNC = 0x400 -let O_APPEND = 0x8 -let O_CLOEXEC = 0x1000000 -let SEEK_SET = 0 -let SEEK_END = 2 diff --git a/std/src/std/libc/mac/socket.inko b/std/src/std/libc/mac/socket.inko deleted file mode 100644 index 5ee42bc12..000000000 --- a/std/src/std/libc/mac/socket.inko +++ /dev/null @@ -1,18 +0,0 @@ -let IPPROTO_IP = 0 -let IPPROTO_IPV6 = 41 -let IPPROTO_TCP = 6 -let IPV6_V6ONLY = 27 -let IP_TTL = 4 -let SOL_SOCKET = 0xFFFF -let SO_BROADCAST = 0x20 -let SO_KEEPALIVE = 0x8 -let SO_LINGER_SEC = 0x1080 -let SO_RCVBUF = 0x1002 -let SO_REUSEADDR = 0x4 -let SO_REUSEPORT = 0x200 -let SO_SNDBUF = 0x1001 -let TCP_NODELAY = 1 - -# For macOS we need to use `SO_LINGER_SEC` to control the time in seconds -# instead of ticks, and `SO_LINGER` itself isn't useful. -let SO_LINGER = SO_LINGER_SEC diff --git a/std/src/std/libc/unix/alloc.inko b/std/src/std/libc/unix/alloc.inko deleted file mode 100644 index bc2afde65..000000000 --- a/std/src/std/libc/unix/alloc.inko +++ /dev/null @@ -1,24 +0,0 @@ -fn extern realloc(pointer: Pointer[UInt8], size: Int) -> Pointer[UInt8] - -fn extern memmove( - to: Pointer[UInt8], - from: Pointer[UInt8], - size: Int, -) -> Pointer[UInt8] - -fn extern free(pointer: Pointer[UInt8]) - -fn resize[T](buffer: Pointer[T], size: Int) -> Pointer[T] { - let ptr = realloc(buffer as Pointer[UInt8], size) - - # In this case there's nothing we can do but abort. - if ptr as Int == 0 and size != 0 { - panic('std.libc.unix.alloc.resize() failed to allocate memory') - } - - ptr as Pointer[T] -} - -fn copy[T](from: Pointer[T], to: Pointer[T], size: Int) { - memmove(to as Pointer[UInt8], from as Pointer[UInt8], size) -} diff --git a/std/src/std/net/socket.inko b/std/src/std/net/socket.inko index 81807b0a3..d14873731 100644 --- a/std/src/std/net/socket.inko +++ b/std/src/std/net/socket.inko @@ -64,9 +64,9 @@ import std.drop (Drop) import std.fmt (Format, Formatter) import std.fs.path (Path) import std.io (Error, Read, Write, WriteInternal) -import std.libc.bsd.socket (self as const) if bsd -import std.libc.linux.socket (self as const) if linux -import std.libc.mac.socket (self as const) if mac +import std.libc.freebsd (self as libc) if freebsd +import std.libc.linux (self as libc) if linux +import std.libc.mac (self as libc) if mac import std.net.ip (IpAddress) import std.string (ToString) import std.time (Duration, ToInstant) @@ -680,27 +680,27 @@ class pub Socket { # Sets the value of the `IP_TTL` option. fn pub mut ttl=(value: Int) -> Result[Nil, Error] { - set_option(const.IPPROTO_IP, const.IP_TTL, value) + set_option(libc.IPPROTO_IP, libc.IP_TTL, value) } # Sets the value of the `IPV6_V6ONLY` option. fn pub mut only_ipv6=(value: Bool) -> Result[Nil, Error] { - set_option(const.IPPROTO_IPV6, const.IPV6_V6ONLY, value.to_int) + set_option(libc.IPPROTO_IPV6, libc.IPV6_V6ONLY, value.to_int) } # Sets the value of the `TCP_NODELAY` option. fn pub mut no_delay=(value: Bool) -> Result[Nil, Error] { - set_option(const.IPPROTO_TCP, const.TCP_NODELAY, value.to_int) + set_option(libc.IPPROTO_TCP, libc.TCP_NODELAY, value.to_int) } # Returns `true` if the `TCP_NODELAY` option is set. fn pub no_delay? -> Bool { - get_option(const.IPPROTO_TCP, const.TCP_NODELAY) != 0 + get_option(libc.IPPROTO_TCP, libc.TCP_NODELAY) != 0 } # Sets the value of the `SO_BROADCAST` option. fn pub mut broadcast=(value: Bool) -> Result[Nil, Error] { - set_option(const.SOL_SOCKET, const.SO_BROADCAST, value.to_int) + set_option(libc.SOL_SOCKET, libc.SO_BROADCAST, value.to_int) } # Sets the value of the `SO_LINGER` option. @@ -725,8 +725,8 @@ class pub Socket { let res = setsockopt( @socket.inner, - const.SOL_SOCKET as Int32, - const.SO_LINGER as Int32, + libc.SOL_SOCKET as Int32, + libc.SO_LINGER as Int32, (mut linger) as Pointer[UInt8], 8 as Int32, ) @@ -741,8 +741,8 @@ class pub Socket { let size = 8 as Int32 let res = getsockopt( @socket.inner, - const.SOL_SOCKET as Int32, - const.SO_LINGER as Int32, + libc.SOL_SOCKET as Int32, + libc.SO_LINGER as Int32, (mut linger) as Pointer[UInt8], mut size, ) @@ -761,22 +761,22 @@ class pub Socket { # Sets the value of the `SO_RCVBUF` option. fn pub mut receive_buffer_size=(value: Int) -> Result[Nil, Error] { - set_option(const.SOL_SOCKET, const.SO_RCVBUF, value.to_int) + set_option(libc.SOL_SOCKET, libc.SO_RCVBUF, value.to_int) } # Sets the value of the `SO_SNDBUF` option. fn pub mut send_buffer_size=(value: Int) -> Result[Nil, Error] { - set_option(const.SOL_SOCKET, const.SO_SNDBUF, value.to_int) + set_option(libc.SOL_SOCKET, libc.SO_SNDBUF, value.to_int) } # Sets the value of the `SO_KEEPALIVE` option. fn pub mut keepalive=(value: Bool) -> Result[Nil, Error] { - set_option(const.SOL_SOCKET, const.SO_KEEPALIVE, value.to_int) + set_option(libc.SOL_SOCKET, libc.SO_KEEPALIVE, value.to_int) } # Sets the value of the `SO_REUSEADDR` option. fn pub mut reuse_address=(value: Bool) -> Result[Nil, Error] { - set_option(const.SOL_SOCKET, const.SO_REUSEADDR, value.to_int) + set_option(libc.SOL_SOCKET, libc.SO_REUSEADDR, value.to_int) } # Sets the value of the `SO_REUSEPORT` option. @@ -784,7 +784,7 @@ class pub Socket { # Not all platforms may support this option, in which case the supplied # argument will be ignored. fn pub mut reuse_port=(value: Bool) -> Result[Nil, Error] { - set_option(const.SOL_SOCKET, const.SO_REUSEPORT, value.to_int) + set_option(libc.SOL_SOCKET, libc.SO_REUSEPORT, value.to_int) } # Shuts down the reading half of this socket. @@ -1774,12 +1774,12 @@ class pub UnixSocket { # Sets the value of the `SO_RCVBUF` option. fn pub mut receive_buffer_size=(value: Int) -> Result[Nil, Error] { - set_option(const.SOL_SOCKET, const.SO_RCVBUF, value.to_int) + set_option(libc.SOL_SOCKET, libc.SO_RCVBUF, value.to_int) } # Sets the value of the `SO_SNDBUF` option. fn pub mut send_buffer_size=(value: Int) -> Result[Nil, Error] { - set_option(const.SOL_SOCKET, const.SO_SNDBUF, value.to_int) + set_option(libc.SOL_SOCKET, libc.SO_SNDBUF, value.to_int) } # Shuts down the reading half of this socket. diff --git a/std/test/std/test_io.inko b/std/test/std/test_io.inko index 3e9b05d32..3f1f63889 100644 --- a/std/test/std/test_io.inko +++ b/std/test/std/test_io.inko @@ -4,9 +4,9 @@ import std.io ( Buffer, BufferedReader, BufferedWriter, Error, INVALID_DATA, READ_BUFFER_SIZE, Read, UNEXPECTED_EOF, WRITE_BUFFER_SIZE, Write, ) -import std.libc.bsd.errors if bsd -import std.libc.linux.errors if linux -import std.libc.mac.errors if mac +import std.libc.freebsd (self as libc) if freebsd +import std.libc.linux (self as libc) if linux +import std.libc.mac (self as libc) if mac import std.test (Tests) class Reader { @@ -95,38 +95,38 @@ impl Write for Writer { fn pub tests(t: mut Tests) { t.test('Error.from_os_error', fn (t) { - t.equal(Error.from_os_error(errors.EPERM), Error.PermissionDenied) - t.equal(Error.from_os_error(errors.ENOENT), Error.NotFound) - t.equal(Error.from_os_error(errors.EINTR), Error.Interrupted) - t.equal(Error.from_os_error(errors.EAGAIN), Error.WouldBlock) - t.equal(Error.from_os_error(errors.ENOMEM), Error.OutOfMemory) - t.equal(Error.from_os_error(errors.EACCES), Error.PermissionDenied) - t.equal(Error.from_os_error(errors.EBUSY), Error.ResourceBusy) - t.equal(Error.from_os_error(errors.EEXIST), Error.AlreadyExists) - t.equal(Error.from_os_error(errors.ENOTDIR), Error.NotADirectory) - t.equal(Error.from_os_error(errors.EISDIR), Error.IsADirectory) - t.equal(Error.from_os_error(errors.EINVAL), Error.InvalidArgument) - t.equal(Error.from_os_error(errors.EFBIG), Error.FileTooLarge) - t.equal(Error.from_os_error(errors.ENOSPC), Error.StorageFull) - t.equal(Error.from_os_error(errors.ESPIPE), Error.InvalidSeek) - t.equal(Error.from_os_error(errors.EROFS), Error.ReadOnlyFilesystem) - t.equal(Error.from_os_error(errors.EPIPE), Error.BrokenPipe) - t.equal(Error.from_os_error(errors.EDEADLK), Error.Deadlock) - t.equal(Error.from_os_error(errors.ENAMETOOLONG), Error.InvalidFileName) - t.equal(Error.from_os_error(errors.ENOTEMPTY), Error.DirectoryNotEmpty) - t.equal(Error.from_os_error(errors.ETIME), Error.TimedOut) - t.equal(Error.from_os_error(errors.EADDRINUSE), Error.AddressInUse) - t.equal(Error.from_os_error(errors.EADDRNOTAVAIL), Error.AddressUnavailable) - t.equal(Error.from_os_error(errors.ENETDOWN), Error.NetworkDown) - t.equal(Error.from_os_error(errors.ENETUNREACH), Error.NetworkDown) - t.equal(Error.from_os_error(errors.ECONNABORTED), Error.ConnectionAborted) - t.equal(Error.from_os_error(errors.ECONNRESET), Error.ConnectionReset) - t.equal(Error.from_os_error(errors.EISCONN), Error.AlreadyConnected) - t.equal(Error.from_os_error(errors.ENOTCONN), Error.NotConnected) - t.equal(Error.from_os_error(errors.ETIMEDOUT), Error.TimedOut) - t.equal(Error.from_os_error(errors.ECONNREFUSED), Error.ConnectionRefused) - t.equal(Error.from_os_error(errors.EHOSTUNREACH), Error.HostUnreachable) - t.equal(Error.from_os_error(errors.EINPROGRESS), Error.InProgress) + t.equal(Error.from_os_error(libc.EPERM), Error.PermissionDenied) + t.equal(Error.from_os_error(libc.ENOENT), Error.NotFound) + t.equal(Error.from_os_error(libc.EINTR), Error.Interrupted) + t.equal(Error.from_os_error(libc.EAGAIN), Error.WouldBlock) + t.equal(Error.from_os_error(libc.ENOMEM), Error.OutOfMemory) + t.equal(Error.from_os_error(libc.EACCES), Error.PermissionDenied) + t.equal(Error.from_os_error(libc.EBUSY), Error.ResourceBusy) + t.equal(Error.from_os_error(libc.EEXIST), Error.AlreadyExists) + t.equal(Error.from_os_error(libc.ENOTDIR), Error.NotADirectory) + t.equal(Error.from_os_error(libc.EISDIR), Error.IsADirectory) + t.equal(Error.from_os_error(libc.EINVAL), Error.InvalidArgument) + t.equal(Error.from_os_error(libc.EFBIG), Error.FileTooLarge) + t.equal(Error.from_os_error(libc.ENOSPC), Error.StorageFull) + t.equal(Error.from_os_error(libc.ESPIPE), Error.InvalidSeek) + t.equal(Error.from_os_error(libc.EROFS), Error.ReadOnlyFilesystem) + t.equal(Error.from_os_error(libc.EPIPE), Error.BrokenPipe) + t.equal(Error.from_os_error(libc.EDEADLK), Error.Deadlock) + t.equal(Error.from_os_error(libc.ENAMETOOLONG), Error.InvalidFileName) + t.equal(Error.from_os_error(libc.ENOTEMPTY), Error.DirectoryNotEmpty) + t.equal(Error.from_os_error(libc.ETIME), Error.TimedOut) + t.equal(Error.from_os_error(libc.EADDRINUSE), Error.AddressInUse) + t.equal(Error.from_os_error(libc.EADDRNOTAVAIL), Error.AddressUnavailable) + t.equal(Error.from_os_error(libc.ENETDOWN), Error.NetworkDown) + t.equal(Error.from_os_error(libc.ENETUNREACH), Error.NetworkDown) + t.equal(Error.from_os_error(libc.ECONNABORTED), Error.ConnectionAborted) + t.equal(Error.from_os_error(libc.ECONNRESET), Error.ConnectionReset) + t.equal(Error.from_os_error(libc.EISCONN), Error.AlreadyConnected) + t.equal(Error.from_os_error(libc.ENOTCONN), Error.NotConnected) + t.equal(Error.from_os_error(libc.ETIMEDOUT), Error.TimedOut) + t.equal(Error.from_os_error(libc.ECONNREFUSED), Error.ConnectionRefused) + t.equal(Error.from_os_error(libc.EHOSTUNREACH), Error.HostUnreachable) + t.equal(Error.from_os_error(libc.EINPROGRESS), Error.InProgress) t.equal(Error.from_os_error(INVALID_DATA), Error.InvalidData) t.equal(Error.from_os_error(UNEXPECTED_EOF), Error.EndOfInput) t.equal(Error.from_os_error(999), Error.Other(999))