Commit 42257a26 authored by Keith Randall's avatar Keith Randall

runtime: in semasleep, subtract time spent so far from timeout

When pthread_cond_timedwait_relative_np gets a spurious wakeup
(due to a signal, typically), we used to retry with the same
relative timeout. That's incorrect, we should lower the timeout
by the time we've spent in this function so far.

In the worst case, signals come in and cause spurious wakeups
faster than the timeout, causing semasleep to never time out.

Also fix nacl and netbsd while we're here. They have similar issues.

Fixes #27520

Change-Id: I6601e120e44a4b8ef436eef75a1e7c8cf1d39e39
Reviewed-on: https://go-review.googlesource.com/133655
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent 204cc14b
...@@ -34,6 +34,10 @@ func semacreate(mp *m) { ...@@ -34,6 +34,10 @@ func semacreate(mp *m) {
//go:nosplit //go:nosplit
func semasleep(ns int64) int32 { func semasleep(ns int64) int32 {
var start int64
if ns >= 0 {
start = nanotime()
}
mp := getg().m mp := getg().m
pthread_mutex_lock(&mp.mutex) pthread_mutex_lock(&mp.mutex)
for { for {
...@@ -43,8 +47,13 @@ func semasleep(ns int64) int32 { ...@@ -43,8 +47,13 @@ func semasleep(ns int64) int32 {
return 0 return 0
} }
if ns >= 0 { if ns >= 0 {
spent := nanotime() - start
if spent >= ns {
pthread_mutex_unlock(&mp.mutex)
return -1
}
var t timespec var t timespec
t.set_nsec(ns) t.set_nsec(ns - spent)
err := pthread_cond_timedwait_relative_np(&mp.cond, &mp.mutex, &t) err := pthread_cond_timedwait_relative_np(&mp.cond, &mp.mutex, &t)
if err == _ETIMEDOUT { if err == _ETIMEDOUT {
pthread_mutex_unlock(&mp.mutex) pthread_mutex_unlock(&mp.mutex)
......
...@@ -197,23 +197,23 @@ func semacreate(mp *m) { ...@@ -197,23 +197,23 @@ func semacreate(mp *m) {
//go:nosplit //go:nosplit
func semasleep(ns int64) int32 { func semasleep(ns int64) int32 {
var ret int32 var ret int32
systemstack(func() { systemstack(func() {
_g_ := getg() _g_ := getg()
if nacl_mutex_lock(_g_.m.waitsemalock) < 0 { if nacl_mutex_lock(_g_.m.waitsemalock) < 0 {
throw("semasleep") throw("semasleep")
} }
var ts timespec
if ns >= 0 {
end := ns + nanotime()
ts.tv_sec = end / 1e9
ts.tv_nsec = int32(end % 1e9)
}
for _g_.m.waitsemacount == 0 { for _g_.m.waitsemacount == 0 {
if ns < 0 { if ns < 0 {
if nacl_cond_wait(_g_.m.waitsema, _g_.m.waitsemalock) < 0 { if nacl_cond_wait(_g_.m.waitsema, _g_.m.waitsemalock) < 0 {
throw("semasleep") throw("semasleep")
} }
} else { } else {
var ts timespec
end := ns + nanotime()
ts.tv_sec = end / 1e9
ts.tv_nsec = int32(end % 1e9)
r := nacl_cond_timed_wait_abs(_g_.m.waitsema, _g_.m.waitsemalock, &ts) r := nacl_cond_timed_wait_abs(_g_.m.waitsema, _g_.m.waitsemalock, &ts)
if r == -_ETIMEDOUT { if r == -_ETIMEDOUT {
nacl_mutex_unlock(_g_.m.waitsemalock) nacl_mutex_unlock(_g_.m.waitsemalock)
......
...@@ -126,15 +126,9 @@ func semacreate(mp *m) { ...@@ -126,15 +126,9 @@ func semacreate(mp *m) {
//go:nosplit //go:nosplit
func semasleep(ns int64) int32 { func semasleep(ns int64) int32 {
_g_ := getg() _g_ := getg()
var deadline int64
// Compute sleep deadline.
var tsp *timespec
var ts timespec
if ns >= 0 { if ns >= 0 {
var nsec int32 deadline = nanotime() + ns
ts.set_sec(timediv(ns, 1000000000, &nsec))
ts.set_nsec(nsec)
tsp = &ts
} }
for { for {
...@@ -147,18 +141,21 @@ func semasleep(ns int64) int32 { ...@@ -147,18 +141,21 @@ func semasleep(ns int64) int32 {
} }
// Sleep until unparked by semawakeup or timeout. // Sleep until unparked by semawakeup or timeout.
ret := lwp_park(_CLOCK_MONOTONIC, _TIMER_RELTIME, tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil) var tsp *timespec
if ret == _ETIMEDOUT { var ts timespec
if ns >= 0 {
wait := deadline - nanotime()
if wait <= 0 {
return -1 return -1
} else if ret == _EINTR && ns >= 0 { }
// Avoid sleeping forever if we keep getting
// interrupted (for example by the profiling
// timer). It would be if tsp upon return had the
// remaining time to sleep, but this is good enough.
var nsec int32 var nsec int32
ns /= 2 ts.set_sec(timediv(wait, 1000000000, &nsec))
ts.set_sec(timediv(ns, 1000000000, &nsec))
ts.set_nsec(nsec) ts.set_nsec(nsec)
tsp = &ts
}
ret := lwp_park(_CLOCK_MONOTONIC, _TIMER_RELTIME, tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil)
if ret == _ETIMEDOUT {
return -1
} }
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment