Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions internal/vm/fiber.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,11 @@ func (vm *VM) registerFiber() {
return dupFiberStorage(f.storage)
})
// Fiber.scheduler / Fiber.set_scheduler hold the current thread's fiber
// scheduler (MRI keeps it in thread->scheduler, rb_fiber_scheduler_set). rbgo
// runs every fiber blocking, so nothing consults the scheduler yet; setting
// one is validated and stored so the hook is observable, as ruby/spec pins.
// scheduler (MRI keeps it in thread->scheduler, rb_fiber_scheduler_set).
// Fiber.scheduler is rb_fiber_scheduler_get (scheduler.c): it hands back
// whatever was installed, whichever fiber asks. It is fiberSchedulerCurrent
// (rb_fiber_scheduler_current) that the blocking-operation hooks consult, and
// that one answers only while a non-blocking fiber is running.
sdef("scheduler", func(vm *VM, _ object.Value, _ []object.Value, _ *Proc) object.Value {
if s := vm.currentThread.scheduler; s != nil {
return s
Expand Down Expand Up @@ -341,6 +343,25 @@ func (vm *VM) registerFiber() {
})
}

// fiberSchedulerCurrent is rb_fiber_scheduler_current
// (rb_fiber_scheduler_current_for_threadptr, scheduler.c): the thread's
// scheduler, but only while the running fiber is non-blocking. MRI keeps a
// counter th->blocking that fiber_switch raises on entering a blocking fiber and
// lowers on leaving one (cont.c), so `th->blocking == 0` is exactly "the fiber
// running right now is non-blocking" — which is what this reads directly.
// A nil result means "no scheduler for this operation", and the caller must then
// take its ordinary blocking path.
func (vm *VM) fiberSchedulerCurrent() object.Value {
t := vm.currentThread
if t == nil || t.scheduler == nil {
return nil
}
if f := vm.currentFiber; f == nil || f.blocking {
return nil
}
return t.scheduler
}

// fiberKwargs returns the trailing Hash of a Fiber.new call — its keyword
// bundle. Fiber.new takes no positional arguments, so a trailing Hash is always
// the keywords.
Expand Down
20 changes: 20 additions & 0 deletions internal/vm/filestat_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,23 @@ func statSys(fi fs.FileInfo) statFields {
hasSys: true,
}
}

// lockFdFn, flockFn and closeFdFn are the POSIX seam behind File#flock. A
// buffer-backed stream has no descriptor of its own, but flock(2) is a property
// of an open file description, so one is opened on the file's path and held for
// as long as the lock is. They are function variables so a whitebox test can
// drive the retry and Errno mapping without taking a real lock.
//
// File::LOCK_SH/EX/NB/UN carry the same values on Linux and BSD as the constants
// rbgo defines, so the operation is passed through unchanged.
var (
lockFdFn = func(path string) (int, error) { return unix.Open(path, unix.O_RDONLY, 0) }
flockFn = func(fd, op int) error { return unix.Flock(fd, op) }
closeFdFn = func(fd int) error { return unix.Close(fd) }
)

// flockAgainErrs are the errno values flock(2) reports for "another open file
// description holds this lock", which rb_file_flock retries on rather than
// raising. EACCES is in the list because some systems report it in place of
// EWOULDBLOCK.
var flockAgainErrs = []error{syscall.EWOULDBLOCK, syscall.EAGAIN, syscall.EACCES}
13 changes: 13 additions & 0 deletions internal/vm/filestat_wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,16 @@ func statSys(fi fs.FileInfo) statFields {
hasSys: true,
}
}

// lockFdFn / flockFn / closeFdFn are unsupported on wasm: there is no flock(2), so
// File#flock reports the operation as unsupported. They exist only to satisfy the
// seam the shared io.go code references.
var (
lockFdFn = func(string) (int, error) { return 0, errors.ErrUnsupported }
flockFn = func(int, int) error { return errors.ErrUnsupported }
closeFdFn = func(int) error { return nil }
)

// flockAgainErrs is empty here: with no flock(2) there is no "would block" to
// retry on, and every call reports the operation as unsupported instead.
var flockAgainErrs []error
13 changes: 13 additions & 0 deletions internal/vm/filestat_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,16 @@ func statSys(fi fs.FileInfo) statFields {
func statOwned(*FileStat) bool { return true }
func statGrpowned(*FileStat) bool { return false }
func statPerm(fi fs.FileInfo) int64 { return int64(fi.Mode().Perm()) &^ 0o022 }

// lockFdFn / flockFn / closeFdFn are unsupported on Windows: there is no flock(2), so
// File#flock reports the operation as unsupported. They exist only to satisfy the
// seam the shared io.go code references.
var (
lockFdFn = func(string) (int, error) { return 0, errors.ErrUnsupported }
flockFn = func(int, int) error { return errors.ErrUnsupported }
closeFdFn = func(int) error { return nil }
)

// flockAgainErrs is empty here: with no flock(2) there is no "would block" to
// retry on, and every call reports the operation as unsupported instead.
var flockAgainErrs []error
Loading
Loading