mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Use Task blocking timer for nanosleep(2).
kernel/time.Timer allocation is expensive and not sync.Poolable (since time.Timer only supports notification through a channel, requiring a goroutine to receive from the channel, and sync.Pool doesn't invoke any kind of cleanup on discarded items in the pool so it would leak timer goroutines). Using the existing Task.blockingTimer for nanosleep(), and applicable cases in clock_nanosleep(), at least avoids Timer allocation in common cases. PiperOrigin-RevId: 406248394
This commit is contained in:
@@ -175,73 +175,6 @@ func Time(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
|
||||
return uintptr(r), nil, nil
|
||||
}
|
||||
|
||||
// clockNanosleepRestartBlock encapsulates the state required to restart
|
||||
// clock_nanosleep(2) via restart_syscall(2).
|
||||
//
|
||||
// +stateify savable
|
||||
type clockNanosleepRestartBlock struct {
|
||||
c ktime.Clock
|
||||
end ktime.Time
|
||||
rem hostarch.Addr
|
||||
}
|
||||
|
||||
// Restart implements kernel.SyscallRestartBlock.Restart.
|
||||
func (n *clockNanosleepRestartBlock) Restart(t *kernel.Task) (uintptr, error) {
|
||||
return 0, clockNanosleepUntil(t, n.c, n.end, n.rem, true)
|
||||
}
|
||||
|
||||
// clockNanosleepUntil blocks until a specified time.
|
||||
//
|
||||
// If blocking is interrupted, the syscall is restarted with the original
|
||||
// arguments.
|
||||
func clockNanosleepUntil(t *kernel.Task, c ktime.Clock, end ktime.Time, rem hostarch.Addr, needRestartBlock bool) error {
|
||||
notifier, tchan := ktime.NewChannelNotifier()
|
||||
timer := ktime.NewTimer(c, notifier)
|
||||
|
||||
// Turn on the timer.
|
||||
timer.Swap(ktime.Setting{
|
||||
Period: 0,
|
||||
Enabled: true,
|
||||
Next: end,
|
||||
})
|
||||
|
||||
err := t.BlockWithTimer(nil, tchan)
|
||||
|
||||
timer.Destroy()
|
||||
|
||||
switch {
|
||||
case linuxerr.Equals(linuxerr.ETIMEDOUT, err):
|
||||
// Slept for entire timeout.
|
||||
return nil
|
||||
case err == linuxerr.ErrInterrupted:
|
||||
// Interrupted.
|
||||
remaining := end.Sub(c.Now())
|
||||
if remaining <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy out remaining time.
|
||||
if rem != 0 {
|
||||
timeleft := linux.NsecToTimespec(remaining.Nanoseconds())
|
||||
if err := copyTimespecOut(t, rem, &timeleft); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if needRestartBlock {
|
||||
// Arrange for a restart with the remaining duration.
|
||||
t.SetSyscallRestartBlock(&clockNanosleepRestartBlock{
|
||||
c: c,
|
||||
end: end,
|
||||
rem: rem,
|
||||
})
|
||||
return linuxerr.ERESTART_RESTARTBLOCK
|
||||
}
|
||||
return linuxerr.ERESTARTNOHAND
|
||||
default:
|
||||
panic(fmt.Sprintf("Impossible BlockWithTimer error %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// Nanosleep implements linux syscall Nanosleep(2).
|
||||
func Nanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
addr := args[0].Pointer()
|
||||
@@ -279,10 +212,12 @@ func ClockNanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
// Only allow clock constants also allowed by Linux.
|
||||
// Only allow clock constants also allowed by Linux. (CLOCK_TAI is
|
||||
// unimplemented.)
|
||||
if clockID > 0 {
|
||||
if clockID != linux.CLOCK_REALTIME &&
|
||||
clockID != linux.CLOCK_MONOTONIC &&
|
||||
clockID != linux.CLOCK_BOOTTIME &&
|
||||
clockID != linux.CLOCK_PROCESS_CPUTIME_ID {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
@@ -301,6 +236,74 @@ func ClockNanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
|
||||
return 0, nil, clockNanosleepUntil(t, c, c.Now().Add(dur), rem, true)
|
||||
}
|
||||
|
||||
// clockNanosleepUntil blocks until a specified time.
|
||||
//
|
||||
// If blocking is interrupted, the syscall is restarted with the original
|
||||
// arguments.
|
||||
func clockNanosleepUntil(t *kernel.Task, c ktime.Clock, end ktime.Time, rem hostarch.Addr, needRestartBlock bool) error {
|
||||
var err error
|
||||
if c == t.Kernel().MonotonicClock() {
|
||||
err = t.BlockWithDeadline(nil, true, end)
|
||||
} else {
|
||||
notifier, tchan := ktime.NewChannelNotifier()
|
||||
timer := ktime.NewTimer(c, notifier)
|
||||
timer.Swap(ktime.Setting{
|
||||
Period: 0,
|
||||
Enabled: true,
|
||||
Next: end,
|
||||
})
|
||||
err = t.BlockWithTimer(nil, tchan)
|
||||
timer.Destroy()
|
||||
}
|
||||
|
||||
switch {
|
||||
case linuxerr.Equals(linuxerr.ETIMEDOUT, err):
|
||||
// Slept for entire timeout.
|
||||
return nil
|
||||
case err == linuxerr.ErrInterrupted:
|
||||
// Interrupted.
|
||||
remaining := end.Sub(c.Now())
|
||||
if remaining <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy out remaining time.
|
||||
if rem != 0 {
|
||||
timeleft := linux.NsecToTimespec(remaining.Nanoseconds())
|
||||
if err := copyTimespecOut(t, rem, &timeleft); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if needRestartBlock {
|
||||
// Arrange for a restart with the remaining duration.
|
||||
t.SetSyscallRestartBlock(&clockNanosleepRestartBlock{
|
||||
c: c,
|
||||
end: end,
|
||||
rem: rem,
|
||||
})
|
||||
return linuxerr.ERESTART_RESTARTBLOCK
|
||||
}
|
||||
return linuxerr.ERESTARTNOHAND
|
||||
default:
|
||||
panic(fmt.Sprintf("Impossible BlockWithTimer error %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// clockNanosleepRestartBlock encapsulates the state required to restart
|
||||
// clock_nanosleep(2) via restart_syscall(2).
|
||||
//
|
||||
// +stateify savable
|
||||
type clockNanosleepRestartBlock struct {
|
||||
c ktime.Clock
|
||||
end ktime.Time
|
||||
rem hostarch.Addr
|
||||
}
|
||||
|
||||
// Restart implements kernel.SyscallRestartBlock.Restart.
|
||||
func (n *clockNanosleepRestartBlock) Restart(t *kernel.Task) (uintptr, error) {
|
||||
return 0, clockNanosleepUntil(t, n.c, n.end, n.rem, true)
|
||||
}
|
||||
|
||||
// Gettimeofday implements linux syscall gettimeofday(2).
|
||||
func Gettimeofday(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
tv := args[0].Pointer()
|
||||
|
||||
Reference in New Issue
Block a user