mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
nanosleep has to store the finish time in the restart block
nanosleep has to count time that a thread spent in the stopped state. PiperOrigin-RevId: 376258641
This commit is contained in:
@@ -458,25 +458,6 @@ func NewTimer(clock Clock, listener TimerListener) *Timer {
|
||||
return t
|
||||
}
|
||||
|
||||
// After waits for the duration to elapse according to clock and then sends a
|
||||
// notification on the returned channel. The timer is started immediately and
|
||||
// will fire exactly once. The second return value is the start time used with
|
||||
// the duration.
|
||||
//
|
||||
// Callers must call Timer.Destroy.
|
||||
func After(clock Clock, duration time.Duration) (*Timer, Time, <-chan struct{}) {
|
||||
notifier, tchan := NewChannelNotifier()
|
||||
t := NewTimer(clock, notifier)
|
||||
now := clock.Now()
|
||||
|
||||
t.Swap(Setting{
|
||||
Enabled: true,
|
||||
Period: 0,
|
||||
Next: now.Add(duration),
|
||||
})
|
||||
return t, now, tchan
|
||||
}
|
||||
|
||||
// init initializes Timer state that is not preserved across save/restore. If
|
||||
// init has already been called, calling it again is a no-op.
|
||||
//
|
||||
|
||||
@@ -180,21 +180,21 @@ func Time(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
|
||||
//
|
||||
// +stateify savable
|
||||
type clockNanosleepRestartBlock struct {
|
||||
c ktime.Clock
|
||||
duration time.Duration
|
||||
rem hostarch.Addr
|
||||
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, clockNanosleepFor(t, n.c, n.duration, n.rem)
|
||||
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, ts linux.Timespec) error {
|
||||
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)
|
||||
|
||||
@@ -202,43 +202,22 @@ func clockNanosleepUntil(t *kernel.Task, c ktime.Clock, ts linux.Timespec) error
|
||||
timer.Swap(ktime.Setting{
|
||||
Period: 0,
|
||||
Enabled: true,
|
||||
Next: ktime.FromTimespec(ts),
|
||||
Next: end,
|
||||
})
|
||||
|
||||
err := t.BlockWithTimer(nil, tchan)
|
||||
|
||||
timer.Destroy()
|
||||
|
||||
// Did we just block until the timeout happened?
|
||||
if err == syserror.ETIMEDOUT {
|
||||
return nil
|
||||
}
|
||||
|
||||
return syserror.ConvertIntr(err, syserror.ERESTARTNOHAND)
|
||||
}
|
||||
|
||||
// clockNanosleepFor blocks for a specified duration.
|
||||
//
|
||||
// If blocking is interrupted, the syscall is restarted with the remaining
|
||||
// duration timeout.
|
||||
func clockNanosleepFor(t *kernel.Task, c ktime.Clock, dur time.Duration, rem hostarch.Addr) error {
|
||||
timer, start, tchan := ktime.After(c, dur)
|
||||
|
||||
err := t.BlockWithTimer(nil, tchan)
|
||||
|
||||
after := c.Now()
|
||||
|
||||
timer.Destroy()
|
||||
|
||||
switch err {
|
||||
case syserror.ETIMEDOUT:
|
||||
// Slept for entire timeout.
|
||||
return nil
|
||||
case syserror.ErrInterrupted:
|
||||
// Interrupted.
|
||||
remaining := dur - after.Sub(start)
|
||||
if remaining < 0 {
|
||||
remaining = time.Duration(0)
|
||||
remaining := end.Sub(c.Now())
|
||||
if remaining <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy out remaining time.
|
||||
@@ -248,14 +227,16 @@ func clockNanosleepFor(t *kernel.Task, c ktime.Clock, dur time.Duration, rem hos
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Arrange for a restart with the remaining duration.
|
||||
t.SetSyscallRestartBlock(&clockNanosleepRestartBlock{
|
||||
c: c,
|
||||
duration: remaining,
|
||||
rem: rem,
|
||||
})
|
||||
return syserror.ERESTART_RESTARTBLOCK
|
||||
if needRestartBlock {
|
||||
// Arrange for a restart with the remaining duration.
|
||||
t.SetSyscallRestartBlock(&clockNanosleepRestartBlock{
|
||||
c: c,
|
||||
end: end,
|
||||
rem: rem,
|
||||
})
|
||||
return syserror.ERESTART_RESTARTBLOCK
|
||||
}
|
||||
return syserror.ERESTARTNOHAND
|
||||
default:
|
||||
panic(fmt.Sprintf("Impossible BlockWithTimer error %v", err))
|
||||
}
|
||||
@@ -278,7 +259,8 @@ func Nanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
|
||||
// Just like linux, we cap the timeout with the max number that int64 can
|
||||
// represent which is roughly 292 years.
|
||||
dur := time.Duration(ts.ToNsecCapped()) * time.Nanosecond
|
||||
return 0, nil, clockNanosleepFor(t, t.Kernel().MonotonicClock(), dur, rem)
|
||||
c := t.Kernel().MonotonicClock()
|
||||
return 0, nil, clockNanosleepUntil(t, c, c.Now().Add(dur), rem, true)
|
||||
}
|
||||
|
||||
// ClockNanosleep implements linux syscall clock_nanosleep(2).
|
||||
@@ -312,11 +294,11 @@ func ClockNanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
|
||||
}
|
||||
|
||||
if flags&linux.TIMER_ABSTIME != 0 {
|
||||
return 0, nil, clockNanosleepUntil(t, c, req)
|
||||
return 0, nil, clockNanosleepUntil(t, c, ktime.FromTimespec(req), 0, false)
|
||||
}
|
||||
|
||||
dur := time.Duration(req.ToNsecCapped()) * time.Nanosecond
|
||||
return 0, nil, clockNanosleepFor(t, c, dur, rem)
|
||||
return 0, nil, clockNanosleepUntil(t, c, c.Now().Add(dur), rem, true)
|
||||
}
|
||||
|
||||
// Gettimeofday implements linux syscall gettimeofday(2).
|
||||
|
||||
@@ -123,6 +123,55 @@ void SleepIgnoreStopped(absl::Duration d) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SigstopTest, RestartSyscall) {
|
||||
pid_t pid;
|
||||
constexpr absl::Duration kStopDelay = absl::Seconds(5);
|
||||
constexpr absl::Duration kSleepDelay = absl::Seconds(15);
|
||||
constexpr absl::Duration kStartupDelay = absl::Seconds(5);
|
||||
constexpr absl::Duration kErrorDelay = absl::Seconds(3);
|
||||
|
||||
const DisableSave ds; // Timing-related.
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
struct timespec ts = {.tv_sec = kSleepDelay / absl::Seconds(1)};
|
||||
auto start = absl::Now();
|
||||
TEST_CHECK(nanosleep(&ts, nullptr) == 0);
|
||||
auto finish = absl::Now();
|
||||
// Check that time spent stopped is counted as time spent sleeping.
|
||||
TEST_CHECK(finish - start >= kSleepDelay);
|
||||
TEST_CHECK(finish - start < kSleepDelay + kErrorDelay);
|
||||
_exit(kChildMainThreadExitCode);
|
||||
}
|
||||
ASSERT_THAT(pid, SyscallSucceeds());
|
||||
|
||||
// Wait for the child subprocess to start sleeping before stopping it.
|
||||
absl::SleepFor(kStartupDelay);
|
||||
ASSERT_THAT(kill(pid, SIGSTOP), SyscallSucceeds());
|
||||
int status;
|
||||
EXPECT_THAT(RetryEINTR(waitpid)(pid, &status, WUNTRACED),
|
||||
SyscallSucceedsWithValue(pid));
|
||||
EXPECT_TRUE(WIFSTOPPED(status));
|
||||
EXPECT_EQ(SIGSTOP, WSTOPSIG(status));
|
||||
|
||||
// Sleep for shorter than the sleep in the child subprocess.
|
||||
absl::SleepFor(kStopDelay);
|
||||
ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, WNOHANG),
|
||||
SyscallSucceedsWithValue(0));
|
||||
|
||||
// Resume the child.
|
||||
ASSERT_THAT(kill(pid, SIGCONT), SyscallSucceeds());
|
||||
|
||||
EXPECT_THAT(RetryEINTR(waitpid)(pid, &status, WCONTINUED),
|
||||
SyscallSucceedsWithValue(pid));
|
||||
EXPECT_TRUE(WIFCONTINUED(status));
|
||||
|
||||
// Expect it to die.
|
||||
ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds());
|
||||
ASSERT_TRUE(WIFEXITED(status));
|
||||
ASSERT_EQ(WEXITSTATUS(status), kChildMainThreadExitCode);
|
||||
}
|
||||
|
||||
void RunChild() {
|
||||
// Start another thread that attempts to call exit_group with a different
|
||||
// error code, in order to verify that SIGSTOP stops this thread as well.
|
||||
|
||||
Reference in New Issue
Block a user