Merge pull request #565 from xushiwei/x

library: os, syscall
This commit is contained in:
xushiwei
2024-07-26 14:59:03 +08:00
committed by GitHub
9 changed files with 380 additions and 84 deletions

View File

@@ -14,8 +14,8 @@ jobs:
test:
strategy:
matrix:
# os: [macos-latest, ubuntu-latest]
os: [macos-latest]
os: [macos-latest, ubuntu-latest]
# os: [macos-latest]
llvm: [18]
runs-on: ${{ matrix.os }}
steps:

View File

@@ -9,85 +9,85 @@ import (
)
const (
E2BIG Errno = Errno(syscall.E2BIG)
EACCES Errno = Errno(syscall.EACCES)
EADDRINUSE Errno = Errno(syscall.EADDRINUSE)
EADDRNOTAVAIL Errno = Errno(syscall.EADDRNOTAVAIL)
EAFNOSUPPORT Errno = Errno(syscall.EAFNOSUPPORT)
EAGAIN Errno = Errno(syscall.EAGAIN)
EALREADY Errno = Errno(syscall.EALREADY)
EBADF Errno = Errno(syscall.EBADF)
EBUSY Errno = Errno(syscall.EBUSY)
ECANCELED Errno = Errno(syscall.ECANCELED)
ECONNABORTED Errno = Errno(syscall.ECONNABORTED)
ECONNREFUSED Errno = Errno(syscall.ECONNREFUSED)
ECONNRESET Errno = Errno(syscall.ECONNRESET)
EDESTADDRREQ Errno = Errno(syscall.EDESTADDRREQ)
EEXIST Errno = Errno(syscall.EEXIST)
EFAULT Errno = Errno(syscall.EFAULT)
EFBIG Errno = Errno(syscall.EFBIG)
EHOSTUNREACH Errno = Errno(syscall.EHOSTUNREACH)
EINTR Errno = Errno(syscall.EINTR)
EINVAL Errno = Errno(syscall.EINVAL)
EIO Errno = Errno(syscall.EIO)
EISCONN Errno = Errno(syscall.EISCONN)
EISDIR Errno = Errno(syscall.EISDIR)
ELOOP Errno = Errno(syscall.ELOOP)
EMFILE Errno = Errno(syscall.EMFILE)
EMSGSIZE Errno = Errno(syscall.EMSGSIZE)
ENAMETOOLONG Errno = Errno(syscall.ENAMETOOLONG)
ENETDOWN Errno = Errno(syscall.ENETDOWN)
ENETUNREACH Errno = Errno(syscall.ENETUNREACH)
ENFILE Errno = Errno(syscall.ENFILE)
ENOBUFS Errno = Errno(syscall.ENOBUFS)
ENODEV Errno = Errno(syscall.ENODEV)
ENOENT Errno = Errno(syscall.ENOENT)
ENOMEM Errno = Errno(syscall.ENOMEM)
ENOPROTOOPT Errno = Errno(syscall.ENOPROTOOPT)
ENOSPC Errno = Errno(syscall.ENOSPC)
ENOSYS Errno = Errno(syscall.ENOSYS)
ENOTCONN Errno = Errno(syscall.ENOTCONN)
ENOTDIR Errno = Errno(syscall.ENOTDIR)
ENOTEMPTY Errno = Errno(syscall.ENOTEMPTY)
ENOTSOCK Errno = Errno(syscall.ENOTSOCK)
ENOTSUP Errno = Errno(syscall.ENOTSUP)
EOVERFLOW Errno = Errno(syscall.EOVERFLOW)
EPERM Errno = Errno(syscall.EPERM)
EPIPE Errno = Errno(syscall.EPIPE)
EPROTO Errno = Errno(syscall.EPROTO)
EPROTONOSUPPORT Errno = Errno(syscall.EPROTONOSUPPORT)
EPROTOTYPE Errno = Errno(syscall.EPROTOTYPE)
ERANGE Errno = Errno(syscall.ERANGE)
EROFS Errno = Errno(syscall.EROFS)
ESHUTDOWN Errno = Errno(syscall.ESHUTDOWN)
ESPIPE Errno = Errno(syscall.ESPIPE)
ESRCH Errno = Errno(syscall.ESRCH)
ETIMEDOUT Errno = Errno(syscall.ETIMEDOUT)
ETXTBSY Errno = Errno(syscall.ETXTBSY)
EXDEV Errno = Errno(syscall.EXDEV)
ENXIO Errno = Errno(syscall.ENXIO)
EMLINK Errno = Errno(syscall.EMLINK)
EHOSTDOWN Errno = Errno(syscall.EHOSTDOWN)
ENOTTY Errno = Errno(syscall.ENOTTY)
EFTYPE Errno = Errno(syscall.EFTYPE)
EILSEQ Errno = Errno(syscall.EILSEQ)
ESOCKTNOSUPPORT Errno = Errno(syscall.ESOCKTNOSUPPORT)
E2BIG = Errno(syscall.E2BIG)
EACCES = Errno(syscall.EACCES)
EADDRINUSE = Errno(syscall.EADDRINUSE)
EADDRNOTAVAIL = Errno(syscall.EADDRNOTAVAIL)
EAFNOSUPPORT = Errno(syscall.EAFNOSUPPORT)
EAGAIN = Errno(syscall.EAGAIN)
EALREADY = Errno(syscall.EALREADY)
EBADF = Errno(syscall.EBADF)
EBUSY = Errno(syscall.EBUSY)
ECANCELED = Errno(syscall.ECANCELED)
ECONNABORTED = Errno(syscall.ECONNABORTED)
ECONNREFUSED = Errno(syscall.ECONNREFUSED)
ECONNRESET = Errno(syscall.ECONNRESET)
EDESTADDRREQ = Errno(syscall.EDESTADDRREQ)
EEXIST = Errno(syscall.EEXIST)
EFAULT = Errno(syscall.EFAULT)
EFBIG = Errno(syscall.EFBIG)
EHOSTUNREACH = Errno(syscall.EHOSTUNREACH)
EINTR = Errno(syscall.EINTR)
EINVAL = Errno(syscall.EINVAL)
EIO = Errno(syscall.EIO)
EISCONN = Errno(syscall.EISCONN)
EISDIR = Errno(syscall.EISDIR)
ELOOP = Errno(syscall.ELOOP)
EMFILE = Errno(syscall.EMFILE)
EMSGSIZE = Errno(syscall.EMSGSIZE)
ENAMETOOLONG = Errno(syscall.ENAMETOOLONG)
ENETDOWN = Errno(syscall.ENETDOWN)
ENETUNREACH = Errno(syscall.ENETUNREACH)
ENFILE = Errno(syscall.ENFILE)
ENOBUFS = Errno(syscall.ENOBUFS)
ENODEV = Errno(syscall.ENODEV)
ENOENT = Errno(syscall.ENOENT)
ENOMEM = Errno(syscall.ENOMEM)
ENOPROTOOPT = Errno(syscall.ENOPROTOOPT)
ENOSPC = Errno(syscall.ENOSPC)
ENOSYS = Errno(syscall.ENOSYS)
ENOTCONN = Errno(syscall.ENOTCONN)
ENOTDIR = Errno(syscall.ENOTDIR)
ENOTEMPTY = Errno(syscall.ENOTEMPTY)
ENOTSOCK = Errno(syscall.ENOTSOCK)
ENOTSUP = Errno(syscall.ENOTSUP)
EOVERFLOW = Errno(syscall.EOVERFLOW)
EPERM = Errno(syscall.EPERM)
EPIPE = Errno(syscall.EPIPE)
EPROTO = Errno(syscall.EPROTO)
EPROTONOSUPPORT = Errno(syscall.EPROTONOSUPPORT)
EPROTOTYPE = Errno(syscall.EPROTOTYPE)
ERANGE = Errno(syscall.ERANGE)
EROFS = Errno(syscall.EROFS)
ESHUTDOWN = Errno(syscall.ESHUTDOWN)
ESPIPE = Errno(syscall.ESPIPE)
ESRCH = Errno(syscall.ESRCH)
ETIMEDOUT = Errno(syscall.ETIMEDOUT)
ETXTBSY = Errno(syscall.ETXTBSY)
EXDEV = Errno(syscall.EXDEV)
ENXIO = Errno(syscall.ENXIO)
EMLINK = Errno(syscall.EMLINK)
EHOSTDOWN = Errno(syscall.EHOSTDOWN)
ENOTTY = Errno(syscall.ENOTTY)
//EFTYPE = Errno(syscall.EFTYPE)
EILSEQ = Errno(syscall.EILSEQ)
ESOCKTNOSUPPORT = Errno(syscall.ESOCKTNOSUPPORT)
)
const (
EAI_ADDRFAMILY Errno = Errno(net.EAI_ADDRFAMILY)
EAI_AGAIN Errno = Errno(net.EAI_AGAIN)
EAI_BADFLAGS Errno = Errno(net.EAI_BADFLAGS)
EAI_BADHINTS Errno = Errno(net.EAI_BADHINTS)
EAI_FAIL Errno = Errno(net.EAI_FAIL)
EAI_FAMILY Errno = Errno(net.EAI_FAMILY)
EAI_MEMORY Errno = Errno(net.EAI_MEMORY)
EAI_NODATA Errno = Errno(net.EAI_NODATA)
EAI_NONAME Errno = Errno(net.EAI_NONAME)
EAI_OVERFLOW Errno = Errno(net.EAI_OVERFLOW)
EAI_PROTOCOL Errno = Errno(net.EAI_PROTOCOL)
EAI_SERVICE Errno = Errno(net.EAI_SERVICE)
EAI_SOCKTYPE Errno = Errno(net.EAI_SOCKTYPE)
EAI_ADDRFAMILY = Errno(net.EAI_ADDRFAMILY)
EAI_AGAIN = Errno(net.EAI_AGAIN)
EAI_BADFLAGS = Errno(net.EAI_BADFLAGS)
EAI_BADHINTS = Errno(net.EAI_BADHINTS)
EAI_FAIL = Errno(net.EAI_FAIL)
EAI_FAMILY = Errno(net.EAI_FAMILY)
EAI_MEMORY = Errno(net.EAI_MEMORY)
EAI_NODATA = Errno(net.EAI_NODATA)
EAI_NONAME = Errno(net.EAI_NONAME)
EAI_OVERFLOW = Errno(net.EAI_OVERFLOW)
EAI_PROTOCOL = Errno(net.EAI_PROTOCOL)
EAI_SERVICE = Errno(net.EAI_SERVICE)
EAI_SOCKTYPE = Errno(net.EAI_SOCKTYPE)
)
const (

View File

@@ -0,0 +1,32 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build dragonfly || freebsd || netbsd
package os
import (
"syscall"
)
// blockUntilWaitable attempts to block until a call to p.Wait will
// succeed immediately, and reports whether it has done so.
// It does not actually call p.Wait.
func (p *Process) blockUntilWaitable() (bool, error) {
var errno syscall.Errno
for {
_, errno = wait6(_P_PID, p.Pid, syscall.WEXITED|syscall.WNOWAIT)
if errno != syscall.EINTR {
break
}
}
// TODO(xsw):
// runtime.KeepAlive(p)
if errno == syscall.ENOSYS {
return false, nil
} else if errno != 0 {
return false, NewSyscallError("wait6", errno)
}
return true, nil
}

View File

@@ -0,0 +1,55 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// We used to used this code for Darwin, but according to issue #19314
// waitid returns if the process is stopped, even when using WEXITED.
//go:build linux
package os
import (
"syscall"
_ "unsafe"
"github.com/goplus/llgo/c"
)
const _P_PID = 1
// int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
//
//go:linkname waitid C.waitid
func waitid(idtype, id uintptr, infop *uint64, options c.Int) c.Int
// blockUntilWaitable attempts to block until a call to p.Wait will
// succeed immediately, and reports whether it has done so.
// It does not actually call p.Wait.
func (p *Process) blockUntilWaitable() (bool, error) {
// The waitid system call expects a pointer to a siginfo_t,
// which is 128 bytes on all Linux systems.
// On darwin/amd64, it requires 104 bytes.
// We don't care about the values it returns.
var siginfo [16]uint64
psig := &siginfo[0]
var e syscall.Errno
for {
e = syscall.Errno(waitid(_P_PID, uintptr(p.Pid), psig, syscall.WEXITED|syscall.WNOWAIT))
if e != syscall.EINTR {
break
}
}
// TODO(xsw):
// runtime.KeepAlive(p)
if e != 0 {
// waitid has been available since Linux 2.6.9, but
// reportedly is not available in Ubuntu on Windows.
// See issue 16610.
if e == syscall.ENOSYS {
return false, nil
}
return false, NewSyscallError("waitid", e)
}
return true, nil
}

View File

@@ -11,6 +11,8 @@ package syscall
import (
"runtime"
"unsafe"
"github.com/goplus/llgo/c"
)
type SysProcAttr struct {
@@ -77,8 +79,8 @@ func init() {
// split the stack, or acquire mutexes). We can't call RawSyscall
// because it's not safe even for BSD-subsystem calls.
//
//go:norace
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
// func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
// Declare all variables at top in case any
// declarations require heap allocation (e.g., err1).
var (

View File

@@ -52,8 +52,6 @@ func runtime_AfterForkInChild()
// For the same reason compiler does not race instrument it.
// The calls to rawSyscall are okay because they are assembly
// functions that do not grow the stack.
//
//go:norace
func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err1 Errno) {
// Declare all variables at top in case any
// declarations require heap allocation (e.g., err1).

View File

@@ -6,6 +6,8 @@
package syscall
import "github.com/goplus/llgo/c"
// Linux unshare/clone/clone2/clone3 flags, architecture-independent,
// copied from linux/sched.h.
const (
@@ -119,8 +121,8 @@ func runtime_AfterForkInChild()
// The calls to RawSyscall are okay because they are assembly
// functions that do not grow the stack.
//
//go:norace
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
// func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
/* TODO(xsw):
// Set up and fork. This returns immediately in the parent or
// if there's an error.

View File

@@ -0,0 +1,105 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris
package syscall
import (
"sync"
"github.com/goplus/llgo/c/syscall"
)
// forkExecPipe atomically opens a pipe with O_CLOEXEC set on both file
// descriptors.
func forkExecPipe(p []int) error {
return Pipe2(p, syscall.O_CLOEXEC)
}
var (
// Guard the forking variable.
forkingLock sync.Mutex
// Number of goroutines currently forking, and thus the
// number of goroutines holding a conceptual write lock
// on ForkLock.
forking int
)
// TODO(xsw):
// hasWaitingReaders reports whether any goroutine is waiting
// to acquire a read lock on rw. It is defined in the sync package.
func hasWaitingReaders(rw *sync.RWMutex) bool {
panic("todo: syscall.hasWaitingReaders in sync package")
}
// acquireForkLock acquires a write lock on ForkLock.
// ForkLock is exported and we've promised that during a fork
// we will call ForkLock.Lock, so that no other threads create
// new fds that are not yet close-on-exec before we fork.
// But that forces all fork calls to be serialized, which is bad.
// But we haven't promised that serialization, and it is essentially
// undetectable by other users of ForkLock, which is good.
// Avoid the serialization by ensuring that ForkLock is locked
// at the first fork and unlocked when there are no more forks.
func acquireForkLock() {
forkingLock.Lock()
defer forkingLock.Unlock()
if forking == 0 {
// There is no current write lock on ForkLock.
ForkLock.Lock()
forking++
return
}
// ForkLock is currently locked for writing.
if hasWaitingReaders(&ForkLock) {
// ForkLock is locked for writing, and at least one
// goroutine is waiting to read from it.
// To avoid lock starvation, allow readers to proceed.
// The simple way to do this is for us to acquire a
// read lock. That will block us until all current
// conceptual write locks are released.
//
// Note that this case is unusual on modern systems
// with O_CLOEXEC and SOCK_CLOEXEC. On those systems
// the standard library should never take a read
// lock on ForkLock.
forkingLock.Unlock()
ForkLock.RLock()
ForkLock.RUnlock()
forkingLock.Lock()
// Readers got a chance, so now take the write lock.
if forking == 0 {
ForkLock.Lock()
}
}
forking++
}
// releaseForkLock releases the conceptual write lock on ForkLock
// acquired by acquireForkLock.
func releaseForkLock() {
forkingLock.Lock()
defer forkingLock.Unlock()
if forking <= 0 {
panic("syscall.releaseForkLock: negative count")
}
forking--
if forking == 0 {
// No more conceptual write locks.
ForkLock.Unlock()
}
}

View File

@@ -11,6 +11,108 @@
package syscall
import (
_ "unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/syscall"
)
// -----------------------------------------------------------------------------
type WaitStatus uint32
// Wait status is 7 bits at bottom, either 0 (exited),
// 0x7F (stopped), or a signal number that caused an exit.
// The 0x80 bit is whether there was a core dump.
// An extra number (exit code, signal causing a stop)
// is in the high bits. At least that's the idea.
// There are various irregularities. For example, the
// "continued" status is 0xFFFF, distinguishing itself
// from stopped via the core dump bit.
const (
mask = 0x7F
core = 0x80
exited = 0x00
stopped = 0x7F
shift = 8
)
func (w WaitStatus) Exited() bool { return w&mask == exited }
func (w WaitStatus) Signaled() bool { return w&mask != stopped && w&mask != exited }
func (w WaitStatus) Stopped() bool { return w&0xFF == stopped }
func (w WaitStatus) Continued() bool { return w == 0xFFFF }
func (w WaitStatus) CoreDump() bool { return w.Signaled() && w&core != 0 }
func (w WaitStatus) ExitStatus() int {
if !w.Exited() {
return -1
}
return int(w>>shift) & 0xFF
}
func (w WaitStatus) Signal() Signal {
if !w.Signaled() {
return -1
}
return Signal(w & mask)
}
func (w WaitStatus) StopSignal() Signal {
if !w.Stopped() {
return -1
}
return Signal(w>>shift) & 0xFF
}
/* TODO(xsw):
func (w WaitStatus) TrapCause() int {
if w.StopSignal() != SIGTRAP {
return -1
}
return int(w>>shift) >> 8
}
*/
func Wait4(pid int, wstatus *WaitStatus, options int, rusage *syscall.Rusage) (wpid int, err error) {
var status c.Int
wpid, err = wait4(pid, &status, options, rusage)
if wstatus != nil {
*wstatus = WaitStatus(status)
}
return
}
// -----------------------------------------------------------------------------
// int pipe2(int pipefd[2], int flags);
//
//go:linkname pipe2 C.pipe2
func pipe2(pipefd *[2]c.Int, flags c.Int) c.Int
func Pipe2(p []int, flags int) error {
if len(p) != 2 {
return Errno(syscall.EINVAL)
}
var pp [2]c.Int
ret := pipe2(&pp, c.Int(flags))
if ret == 0 {
p[0] = int(pp[0])
p[1] = int(pp[1])
return nil
}
return Errno(ret)
}
// -----------------------------------------------------------------------------
func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
panic("todo: syscall.Faccessat")
}
// -----------------------------------------------------------------------------