1// Copyright 2018 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// +build !plan9,!windows,!js
6
7package runtime_test
8
9import (
10	"os/exec"
11	"syscall"
12	"testing"
13	"time"
14)
15
16// Issue #27250. Spurious wakeups to pthread_cond_timedwait_relative_np
17// shouldn't cause semasleep to retry with the same timeout which would
18// cause indefinite spinning.
19func TestSpuriousWakeupsNeverHangSemasleep(t *testing.T) {
20	if *flagQuick {
21		t.Skip("-quick")
22	}
23
24	exe, err := buildTestProg(t, "testprog")
25	if err != nil {
26		t.Fatal(err)
27	}
28
29	start := time.Now()
30	cmd := exec.Command(exe, "After1")
31	if err := cmd.Start(); err != nil {
32		t.Fatalf("Failed to start command: %v", err)
33	}
34	doneCh := make(chan error, 1)
35	go func() {
36		doneCh <- cmd.Wait()
37	}()
38
39	// With the repro running, we can continuously send to it
40	// a non-terminal signal such as SIGIO, to spuriously
41	// wakeup pthread_cond_timedwait_relative_np.
42	unfixedTimer := time.NewTimer(2 * time.Second)
43	for {
44		select {
45		case <-time.After(200 * time.Millisecond):
46			// Send the pesky signal that toggles spinning
47			// indefinitely if #27520 is not fixed.
48			cmd.Process.Signal(syscall.SIGIO)
49
50		case <-unfixedTimer.C:
51			t.Error("Program failed to return on time and has to be killed, issue #27520 still exists")
52			cmd.Process.Signal(syscall.SIGKILL)
53			return
54
55		case err := <-doneCh:
56			if err != nil {
57				t.Fatalf("The program returned but unfortunately with an error: %v", err)
58			}
59			if time.Since(start) < 100*time.Millisecond {
60				t.Fatalf("The program stopped too quickly.")
61			}
62			return
63		}
64	}
65}
66