// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Futex is only available on DragonFly BSD, FreeBSD and Linux. // The race detector emits calls to split stack functions so it breaks // the test. //go:build (dragonfly || freebsd || linux) && !race // +build dragonfly freebsd linux // +build !race package runtime_test import ( "runtime" "sync" "sync/atomic" "testing" "time" ) type futexsleepTest struct { mtx uint32 ns int64 msg string ch chan *futexsleepTest } var futexsleepTests = []futexsleepTest{ beforeY2038: {mtx: 0, ns: 86400 * 1e9, msg: "before the year 2038"}, afterY2038: {mtx: 0, ns: (1<<31 + 100) * 1e9, msg: "after the year 2038"}, } const ( beforeY2038 = iota afterY2038 ) func TestFutexsleep(t *testing.T) { if runtime.GOMAXPROCS(0) > 1 { // futexsleep doesn't handle EINTR or other signals, // so spurious wakeups may happen. t.Skip("skipping; GOMAXPROCS>1") } start := time.Now() var wg sync.WaitGroup for i := range futexsleepTests { tt := &futexsleepTests[i] tt.mtx = 0 tt.ch = make(chan *futexsleepTest, 1) wg.Add(1) go func(tt *futexsleepTest) { runtime.Entersyscall() runtime.Futexsleep(&tt.mtx, 0, tt.ns) runtime.Exitsyscall() tt.ch <- tt wg.Done() }(tt) } loop: for { select { case tt := <-futexsleepTests[beforeY2038].ch: t.Errorf("futexsleep test %q finished early after %s", tt.msg, time.Since(start)) break loop case tt := <-futexsleepTests[afterY2038].ch: // Looks like FreeBSD 10 kernel has changed // the semantics of timedwait on userspace // mutex to make broken stuff look broken. switch { case runtime.GOOS == "freebsd" && runtime.GOARCH == "386": t.Log("freebsd/386 may not work correctly after the year 2038, see golang.org/issue/7194") default: t.Errorf("futexsleep test %q finished early after %s", tt.msg, time.Since(start)) break loop } case <-time.After(time.Second): break loop } } for i := range futexsleepTests { tt := &futexsleepTests[i] atomic.StoreUint32(&tt.mtx, 1) runtime.Futexwakeup(&tt.mtx, 1) } wg.Wait() }