-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use clock_nanosleep(2) for scheduling instead of time.Timer (#7)
I suppose clock_nanosleep(2) handles susupend/resume.
- Loading branch information
Showing
6 changed files
with
175 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
//go:build freebsd | ||
|
||
package nanosleep | ||
|
||
import ( | ||
"syscall" | ||
"unsafe" | ||
|
||
"golang.org/x/sys/unix" | ||
) | ||
|
||
// Do the interface allocations only once for common | ||
// Errno values. | ||
var ( | ||
errEAGAIN error = syscall.EAGAIN | ||
errEINVAL error = syscall.EINVAL | ||
errENOENT error = syscall.ENOENT | ||
) | ||
|
||
// A copy of linux implementation, because [golang.org/x/sys/unix] doesn't have | ||
// it for FreeBSD. | ||
func clockNanosleep(clockid int32, flags int, request *unix.Timespec, | ||
remain *unix.Timespec, | ||
) error { | ||
_, _, e1 := unix.Syscall6(unix.SYS_CLOCK_NANOSLEEP, | ||
uintptr(clockid), uintptr(flags), | ||
uintptr(unsafe.Pointer(request)), | ||
uintptr(unsafe.Pointer(remain)), | ||
0, 0) | ||
if e1 != 0 { | ||
return errnoErr(e1) | ||
} | ||
return nil | ||
} | ||
|
||
// errnoErr returns common boxed Errno values, to prevent | ||
// allocations at runtime. | ||
func errnoErr(e syscall.Errno) error { | ||
switch e { | ||
case 0: | ||
return nil | ||
case unix.EAGAIN: | ||
return errEAGAIN | ||
case unix.EINVAL: | ||
return errEINVAL | ||
case unix.ENOENT: | ||
return errENOENT | ||
} | ||
return e | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
//go:build linux | ||
|
||
package nanosleep | ||
|
||
import "golang.org/x/sys/unix" | ||
|
||
func clockNanosleep(clockid int32, flags int, request *unix.Timespec, | ||
remain *unix.Timespec, | ||
) error { | ||
return unix.ClockNanosleep(clockid, flags, request, remain) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package nanosleep | ||
|
||
import ( | ||
"time" | ||
|
||
"golang.org/x/sys/unix" | ||
) | ||
|
||
// See https://www.reddit.com/r/golang/comments/jeqmtt/wake_up_at_time/ | ||
func SleepUntil(t time.Time) error { | ||
rqtp, err := unix.TimeToTimespec(t) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for { | ||
err := clockNanosleep(unix.CLOCK_REALTIME, unix.TIMER_ABSTIME, &rqtp, nil) | ||
if err != nil { | ||
if err == unix.EINTR { | ||
continue | ||
} | ||
return err | ||
} | ||
return nil | ||
} | ||
} | ||
|
||
func NewTimer(d time.Duration) *Timer { | ||
t := new(Timer) | ||
t.Reset(d) | ||
return t | ||
} | ||
|
||
type Timer struct { | ||
ch <-chan time.Time | ||
} | ||
|
||
func (self *Timer) run(t time.Time, c chan<- time.Time) { | ||
_ = SleepUntil(t) | ||
c <- time.Now() | ||
} | ||
|
||
func (self *Timer) C() <-chan time.Time { return self.ch } | ||
|
||
func (self *Timer) Reset(d time.Duration) bool { | ||
// It just starts a new goroutine without stopping existsing one, because Stop | ||
// does nothing. Yes, it's a goroutine leak, but I don't know how to stop | ||
// clock_nanosleep. | ||
wasRunning := self.Stop() | ||
c := make(chan time.Time, 1) | ||
self.ch = c | ||
go self.run(time.Now().Add(d), c) | ||
return wasRunning | ||
} | ||
|
||
// Stop does nothing, because I don't know how to stop clock_nanosleep. | ||
func (self *Timer) Stop() bool { return true } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package nanosleep | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestSleepUntil(t *testing.T) { | ||
startTime := time.Now() | ||
endTime := startTime.Add(time.Second) | ||
require.NoError(t, SleepUntil(endTime)) | ||
|
||
d := time.Since(startTime) | ||
assert.GreaterOrEqual(t, d, time.Second) | ||
assert.Less(t, d, 2*time.Second) | ||
} | ||
|
||
func TestNewTimer(t *testing.T) { | ||
startTime := time.Now() | ||
timer := NewTimer(time.Second) | ||
endTime := <-timer.C() | ||
|
||
d := time.Since(startTime) | ||
assert.GreaterOrEqual(t, d, time.Second) | ||
assert.Less(t, d, 2*time.Second) | ||
assert.GreaterOrEqual(t, endTime.Sub(startTime), time.Second) | ||
assert.Less(t, endTime.Sub(startTime), 2*time.Second) | ||
} | ||
|
||
func TestTimer_Reset(t *testing.T) { | ||
startTime := time.Now() | ||
timer := NewTimer(time.Second) | ||
|
||
time.Sleep(500 * time.Millisecond) | ||
timer.Reset(time.Second) | ||
endTime := <-timer.C() | ||
|
||
d := time.Since(startTime) | ||
assert.GreaterOrEqual(t, d, time.Second) | ||
assert.Less(t, d, 2*time.Second) | ||
assert.GreaterOrEqual(t, endTime.Sub(startTime), time.Second) | ||
assert.Less(t, endTime.Sub(startTime), 2*time.Second) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters