From 99121d6ff2b02f3d16b791eb103bb9f9e8b96475 Mon Sep 17 00:00:00 2001 From: Tetralux Date: Sat, 26 Oct 2019 22:35:36 +0000 Subject: Implement core:thread and core:sync on Unix using pthreads Also do some cleanup and refactoring of the thread, sync and time APIs. - remove 'semaphore_release' because 'post' and 'wait' is easier to understand - change 'semaphore_wait' to '*_wait_for' to match Condition - pthreads can be given a stack, but doing so requires the user to set up the guard pages manually. BE WARNED. The alignment requirements of the stack are also platform-dependant; it may need to be page size aligned on some systems. Unclear which systems, however. See 'os.get_page_size', and 'mem.make_aligned'. HOWEVER: I was unable to get custom stacks with guard pages working reliably, so while you can do it, the API does not support it. - add 'os.get_page_size', 'mem.make_aligned', and 'mem.new_aligned'. - removed thread return values because windows and linux are not consistent; windows returns 'i32' and pthreads return 'void*'; besides which, if you really wanted to communicate how the thread exited, you probably wouldn't do it with the thread's exit code. - fixed 'thread.is_done' on Windows; it didn't report true immediately after calling 'thread.join'. - moved time related stuff out of 'core:os' to 'core:time'. - add 'mem.align_backward' - fixed default allocator alignment The heap on Windows, and calloc on Linux, both have no facility to request alignment. It's a bit of hack, but the heap_allocator now overallocates; `size + alignment` bytes, and aligns things to at least 2. It does both of these things to ensure that there is at least two bytes before the payload, which it uses to store how much padding it needed to insert in order to fulfil the alignment requested. - make conditions more sane by matching the Windows behaviour. The fact that they were signalled now lingers until a thread tries to wait, causing them to just pass by uninterrupted, without sleeping or locking the underlying mutex, as it would otherwise need to do. This means that a thread no longer has to be waiting in order to be signalled, which avoids timing bugs that causes deadlocks that are hard to debug and fix. See the comment on the `sync.Condition.flag` field. - add thread priority: `thread.create(worker_proc, .High)` --- core/os/os_linux.odin | 213 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 143 insertions(+), 70 deletions(-) (limited to 'core/os/os_linux.odin') diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index dc3cdde93..cf03dac71 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -15,37 +15,138 @@ Syscall :: distinct int; INVALID_HANDLE :: ~Handle(0); -ERROR_NONE: Errno : 0; -EPERM: Errno : 1; -ENOENT: Errno : 2; -EINTR: Errno : 4; -EIO: Errno : 5; -ENXIO: Errno : 6; -EBADF: Errno : 9; -EAGAIN: Errno : 11; -EWOULDBLOCK: Errno : EAGAIN; -ENOMEM: Errno : 12; -EACCES: Errno : 13; -EFAULT: Errno : 14; -EEXIST: Errno : 17; -ENODEV: Errno : 19; -ENOTDIR: Errno : 20; -EISDIR: Errno : 21; -EINVAL: Errno : 22; -ENFILE: Errno : 23; -EMFILE: Errno : 24; -ETXTBSY: Errno : 26; -EFBIG: Errno : 27; -ENOSPC: Errno : 28; -ESPIPE: Errno : 29; -EROFS: Errno : 30; -EPIPE: Errno : 32; -ENAMETOOLONG: Errno : 36; -ELOOP: Errno : 40; -EOVERFLOW: Errno : 75; -EDESTADDRREQ: Errno : 89; -EOPNOTSUPP: Errno : 95; -EDQUOT: Errno : 122; +ERROR_NONE: Errno : 0; +EPERM: Errno : 1; +ENOENT: Errno : 2; +ESRCH: Errno : 3; +EINTR: Errno : 4; +EIO: Errno : 5; +ENXIO: Errno : 6; +EBADF: Errno : 9; +EAGAIN: Errno : 11; +ENOMEM: Errno : 12; +EACCES: Errno : 13; +EFAULT: Errno : 14; +EEXIST: Errno : 17; +ENODEV: Errno : 19; +ENOTDIR: Errno : 20; +EISDIR: Errno : 21; +EINVAL: Errno : 22; +ENFILE: Errno : 23; +EMFILE: Errno : 24; +ETXTBSY: Errno : 26; +EFBIG: Errno : 27; +ENOSPC: Errno : 28; +ESPIPE: Errno : 29; +EROFS: Errno : 30; +EPIPE: Errno : 32; + +EDEADLK: Errno : 35; /* Resource deadlock would occur */ +ENAMETOOLONG: Errno : 36; /* File name too long */ +ENOLCK: Errno : 37; /* No record locks available */ + +ENOSYS: Errno : 38; /* Invalid system call number */ + +ENOTEMPTY: Errno : 39; /* Directory not empty */ +ELOOP: Errno : 40; /* Too many symbolic links encountered */ +EWOULDBLOCK: Errno : EAGAIN; /* Operation would block */ +ENOMSG: Errno : 42; /* No message of desired type */ +EIDRM: Errno : 43; /* Identifier removed */ +ECHRNG: Errno : 44; /* Channel number out of range */ +EL2NSYNC: Errno : 45; /* Level 2 not synchronized */ +EL3HLT: Errno : 46; /* Level 3 halted */ +EL3RST: Errno : 47; /* Level 3 reset */ +ELNRNG: Errno : 48; /* Link number out of range */ +EUNATCH: Errno : 49; /* Protocol driver not attached */ +ENOCSI: Errno : 50; /* No CSI structure available */ +EL2HLT: Errno : 51; /* Level 2 halted */ +EBADE: Errno : 52; /* Invalid exchange */ +EBADR: Errno : 53; /* Invalid request descriptor */ +EXFULL: Errno : 54; /* Exchange full */ +ENOANO: Errno : 55; /* No anode */ +EBADRQC: Errno : 56; /* Invalid request code */ +EBADSLT: Errno : 57; /* Invalid slot */ +EDEADLOCK: Errno : EDEADLK; +EBFONT: Errno : 59; /* Bad font file format */ +ENOSTR: Errno : 60; /* Device not a stream */ +ENODATA: Errno : 61; /* No data available */ +ETIME: Errno : 62; /* Timer expired */ +ENOSR: Errno : 63; /* Out of streams resources */ +ENONET: Errno : 64; /* Machine is not on the network */ +ENOPKG: Errno : 65; /* Package not installed */ +EREMOTE: Errno : 66; /* Object is remote */ +ENOLINK: Errno : 67; /* Link has been severed */ +EADV: Errno : 68; /* Advertise error */ +ESRMNT: Errno : 69; /* Srmount error */ +ECOMM: Errno : 70; /* Communication error on send */ +EPROTO: Errno : 71; /* Protocol error */ +EMULTIHOP: Errno : 72; /* Multihop attempted */ +EDOTDOT: Errno : 73; /* RFS specific error */ +EBADMSG: Errno : 74; /* Not a data message */ +EOVERFLOW: Errno : 75; /* Value too large for defined data type */ +ENOTUNIQ: Errno : 76; /* Name not unique on network */ +EBADFD: Errno : 77; /* File descriptor in bad state */ +EREMCHG: Errno : 78; /* Remote address changed */ +ELIBACC: Errno : 79; /* Can not access a needed shared library */ +ELIBBAD: Errno : 80; /* Accessing a corrupted shared library */ +ELIBSCN: Errno : 81; /* .lib section in a.out corrupted */ +ELIBMAX: Errno : 82; /* Attempting to link in too many shared libraries */ +ELIBEXEC: Errno : 83; /* Cannot exec a shared library directly */ +EILSEQ: Errno : 84; /* Illegal byte sequence */ +ERESTART: Errno : 85; /* Interrupted system call should be restarted */ +ESTRPIPE: Errno : 86; /* Streams pipe error */ +EUSERS: Errno : 87; /* Too many users */ +ENOTSOCK: Errno : 88; /* Socket operation on non-socket */ +EDESTADDRREQ: Errno : 89; /* Destination address required */ +EMSGSIZE: Errno : 90; /* Message too long */ +EPROTOTYPE: Errno : 91; /* Protocol wrong type for socket */ +ENOPROTOOPT: Errno : 92; /* Protocol not available */ +EPROTONOSUPPORT: Errno : 93; /* Protocol not supported */ +ESOCKTNOSUPPORT: Errno : 94; /* Socket type not supported */ +EOPNOTSUPP: Errno : 95; /* Operation not supported on transport endpoint */ +EPFNOSUPPORT: Errno : 96; /* Protocol family not supported */ +EAFNOSUPPORT: Errno : 97; /* Address family not supported by protocol */ +EADDRINUSE: Errno : 98; /* Address already in use */ +EADDRNOTAVAIL: Errno : 99; /* Cannot assign requested address */ +ENETDOWN: Errno : 100; /* Network is down */ +ENETUNREACH: Errno : 101; /* Network is unreachable */ +ENETRESET: Errno : 102; /* Network dropped connection because of reset */ +ECONNABORTED: Errno : 103; /* Software caused connection abort */ +ECONNRESET: Errno : 104; /* Connection reset by peer */ +ENOBUFS: Errno : 105; /* No buffer space available */ +EISCONN: Errno : 106; /* Transport endpoint is already connected */ +ENOTCONN: Errno : 107; /* Transport endpoint is not connected */ +ESHUTDOWN: Errno : 108; /* Cannot send after transport endpoint shutdown */ +ETOOMANYREFS: Errno : 109; /* Too many references: cannot splice */ +ETIMEDOUT: Errno : 110; /* Connection timed out */ +ECONNREFUSED: Errno : 111; /* Connection refused */ +EHOSTDOWN: Errno : 112; /* Host is down */ +EHOSTUNREACH: Errno : 113; /* No route to host */ +EALREADY: Errno : 114; /* Operation already in progress */ +EINPROGRESS: Errno : 115; /* Operation now in progress */ +ESTALE: Errno : 116; /* Stale file handle */ +EUCLEAN: Errno : 117; /* Structure needs cleaning */ +ENOTNAM: Errno : 118; /* Not a XENIX named type file */ +ENAVAIL: Errno : 119; /* No XENIX semaphores available */ +EISNAM: Errno : 120; /* Is a named type file */ +EREMOTEIO: Errno : 121; /* Remote I/O error */ +EDQUOT: Errno : 122; /* Quota exceeded */ + +ENOMEDIUM: Errno : 123; /* No medium found */ +EMEDIUMTYPE: Errno : 124; /* Wrong medium type */ +ECANCELED: Errno : 125; /* Operation Canceled */ +ENOKEY: Errno : 126; /* Required key not available */ +EKEYEXPIRED: Errno : 127; /* Key has expired */ +EKEYREVOKED: Errno : 128; /* Key has been revoked */ +EKEYREJECTED: Errno : 129; /* Key was rejected by service */ + +/* for robust mutexes */ +EOWNERDEAD: Errno : 130; /* Owner died */ +ENOTRECOVERABLE: Errno : 131; /* State not recoverable */ + +ERFKILL: Errno : 132; /* Operation not possible due to RF-kill */ + +EHWPOISON: Errno : 133; /* Memory page has hardware error */ O_RDONLY :: 0x00000; O_WRONLY :: 0x00001; @@ -152,22 +253,6 @@ X_OK :: 1; // Test for execute permission W_OK :: 2; // Test for write permission R_OK :: 4; // Test for read permission -TimeSpec :: struct { - tv_sec : i64, /* seconds */ - tv_nsec : i64, /* nanoseconds */ -}; - -CLOCK_REALTIME :: 0; -CLOCK_MONOTONIC :: 1; -CLOCK_PROCESS_CPUTIME_ID :: 2; -CLOCK_THREAD_CPUTIME_ID :: 3; -CLOCK_MONOTONIC_RAW :: 4; -CLOCK_REALTIME_COARSE :: 5; -CLOCK_MONOTONIC_COARSE :: 6; -CLOCK_BOOTTIME :: 7; -CLOCK_REALTIME_ALARM :: 8; -CLOCK_BOOTTIME_ALARM :: 9; - SYS_GETTID: Syscall : 186; foreign libc { @@ -180,6 +265,7 @@ foreign libc { @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int ---; @(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 ---; @(link_name="gettid") _unix_gettid :: proc() -> u64 ---; + @(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---; @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---; @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^Stat) -> int ---; @(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---; @@ -190,10 +276,6 @@ foreign libc { @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---; @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---; - @(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) ---; - @(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> int ---; - @(link_name="sleep") _unix_sleep :: proc(seconds: u64) -> int ---; - @(link_name="exit") _unix_exit :: proc(status: int) -> ! ---; } foreign dl { @@ -349,25 +431,6 @@ exit :: proc(code: int) -> ! { _unix_exit(code); } -clock_gettime :: proc(clock_id: u64) -> TimeSpec { - ts : TimeSpec; - _unix_clock_gettime(clock_id, &ts); - return ts; -} - -sleep :: proc(seconds: u64) -> int { - - return _unix_sleep(seconds); -} - -nanosleep :: proc(nanoseconds: i64) -> int { - assert(nanoseconds <= 999999999); - requested, remaining : TimeSpec; - requested = TimeSpec{tv_nsec = nanoseconds}; - - return _unix_nanosleep(&requested, &remaining); -} - current_thread_id :: proc "contextless" () -> int { return syscall(SYS_GETTID); } @@ -393,6 +456,16 @@ 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; +} + _alloc_command_line_arguments :: proc() -> []string { res := make([]string, len(runtime.args__)); -- cgit v1.2.3