Skip to content

Commit e57350f

Browse files
author
Bryan C. Mills
committed
runtime: fix _cgo_yield usage with sysmon and on BSD
There are a few problems from change 35494, discovered during testing of change 37852. 1. I was confused about the usage of n.key in the sema variant, so we were looping on the wrong condition. The error was not caught by the TryBots (presumably due to missing TSAN coverage in the BSD and darwin builders?). 2. The sysmon goroutine sometimes skips notetsleep entirely, using direct usleep syscalls instead. In that case, we were not calling _cgo_yield, leading to missed signals under TSAN. 3. Some notetsleep calls have long finite timeouts. They should be broken up into smaller chunks with a yield at the end of each chunk. updates #18717 Change-Id: I91175af5dea3857deebc686f51a8a40f9d690bcc Reviewed-on: https://go-review.googlesource.com/37867 Run-TryBot: Bryan Mills <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 91563ce commit e57350f

File tree

5 files changed

+75
-10
lines changed

5 files changed

+75
-10
lines changed

misc/cgo/testsanitizers/test.bash

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -190,17 +190,12 @@ if test "$tsan" = "yes"; then
190190
fi
191191

192192
if test "$ok" = "true"; then
193-
# This test requires rebuilding os/user with -fsanitize=thread.
193+
# These tests require rebuilding os/user with -fsanitize=thread.
194194
testtsan tsan5.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan"
195-
196-
# This test requires rebuilding runtime/cgo with -fsanitize=thread.
197195
testtsan tsan6.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan"
198-
199-
# This test requires rebuilding runtime/cgo with -fsanitize=thread.
200196
testtsan tsan7.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan"
201-
202-
# This test requires rebuilding runtime/cgo with -fsanitize=thread.
203197
testtsan tsan10.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan"
198+
testtsan tsan11.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan"
204199
fi
205200
fi
206201

misc/cgo/testsanitizers/tsan11.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2017 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
// This program hung when run under the C/C++ ThreadSanitizer. TSAN defers
8+
// asynchronous signals until the signaled thread calls into libc. The runtime's
9+
// sysmon goroutine idles itself using direct usleep syscalls, so it could
10+
// run for an arbitrarily long time without triggering the libc interceptors.
11+
// See https://golang.org/issue/18717.
12+
13+
import (
14+
"os"
15+
"os/signal"
16+
"syscall"
17+
)
18+
19+
/*
20+
#cgo CFLAGS: -g -fsanitize=thread
21+
#cgo LDFLAGS: -g -fsanitize=thread
22+
23+
#include <signal.h>
24+
#include <stdio.h>
25+
#include <stdlib.h>
26+
#include <string.h>
27+
28+
static void raise_usr2(int signo) {
29+
raise(SIGUSR2);
30+
}
31+
32+
static void register_handler(int signo) {
33+
struct sigaction sa;
34+
memset(&sa, 0, sizeof(sa));
35+
sigemptyset(&sa.sa_mask);
36+
sa.sa_flags = SA_ONSTACK;
37+
sa.sa_handler = raise_usr2;
38+
39+
if (sigaction(SIGUSR1, &sa, NULL) != 0) {
40+
perror("failed to register SIGUSR1 handler");
41+
exit(EXIT_FAILURE);
42+
}
43+
}
44+
*/
45+
import "C"
46+
47+
func main() {
48+
ch := make(chan os.Signal)
49+
signal.Notify(ch, syscall.SIGUSR2)
50+
51+
C.register_handler(C.int(syscall.SIGUSR1))
52+
syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
53+
54+
<-ch
55+
}

src/runtime/lock_futex.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,14 @@ func notetsleep_internal(n *note, ns int64) bool {
185185

186186
deadline := nanotime() + ns
187187
for {
188+
if _cgo_yield != nil && ns > 10e6 {
189+
ns = 10e6
190+
}
188191
gp.m.blocked = true
189192
futexsleep(key32(&n.key), 0, ns)
193+
if _cgo_yield != nil {
194+
asmcgocall(_cgo_yield, nil)
195+
}
190196
gp.m.blocked = false
191197
if atomic.Load(key32(&n.key)) != 0 {
192198
break

src/runtime/lock_sema.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,9 @@ func notetsleep_internal(n *note, ns int64, gp *g, deadline int64) bool {
198198
if _cgo_yield == nil {
199199
semasleep(-1)
200200
} else {
201-
// Sleep for an arbitrary-but-moderate interval to poll libc interceptors.
201+
// Sleep in arbitrary-but-moderate intervals to poll libc interceptors.
202202
const ns = 10e6
203-
for atomic.Loaduintptr(&n.key) == 0 {
204-
semasleep(ns)
203+
for semasleep(ns) < 0 {
205204
asmcgocall(_cgo_yield, nil)
206205
}
207206
}
@@ -213,12 +212,18 @@ func notetsleep_internal(n *note, ns int64, gp *g, deadline int64) bool {
213212
for {
214213
// Registered. Sleep.
215214
gp.m.blocked = true
215+
if _cgo_yield != nil && ns > 10e6 {
216+
ns = 10e6
217+
}
216218
if semasleep(ns) >= 0 {
217219
gp.m.blocked = false
218220
// Acquired semaphore, semawakeup unregistered us.
219221
// Done.
220222
return true
221223
}
224+
if _cgo_yield != nil {
225+
asmcgocall(_cgo_yield, nil)
226+
}
222227
gp.m.blocked = false
223228
// Interrupted or timed out. Still registered. Semaphore not acquired.
224229
ns = deadline - nanotime()

src/runtime/proc.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3755,6 +3755,10 @@ func sysmon() {
37553755
}
37563756
unlock(&sched.lock)
37573757
}
3758+
// trigger libc interceptors if needed
3759+
if _cgo_yield != nil {
3760+
asmcgocall(_cgo_yield, nil)
3761+
}
37583762
// poll network if not polled for more than 10ms
37593763
lastpoll := int64(atomic.Load64(&sched.lastpoll))
37603764
now := nanotime()

0 commit comments

Comments
 (0)