// Copyright 2018 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. //+build !nacl,!plan9,!windows,!js package runtime_test import ( "internal/testenv" "io/ioutil" "os" "os/exec" "path/filepath" "syscall" "testing" "time" ) // Issue #27250. Spurious wakeups to pthread_cond_timedwait_relative_np // shouldn't cause semasleep to retry with the same timeout which would // cause indefinite spinning. func TestSpuriousWakeupsNeverHangSemasleep(t *testing.T) { testenv.MustHaveGoBuild(t) tempDir, err := ioutil.TempDir("", "issue-27250") if err != nil { t.Fatalf("Failed to create the temp directory: %v", err) } defer os.RemoveAll(tempDir) repro := ` package main import "time" func main() { <-time.After(1 * time.Second) } ` mainPath := filepath.Join(tempDir, "main.go") if err := ioutil.WriteFile(mainPath, []byte(repro), 0644); err != nil { t.Fatalf("Failed to create temp file for repro.go: %v", err) } binaryPath := filepath.Join(tempDir, "binary") // Build the binary so that we can send the signal to its PID. out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", binaryPath, mainPath).CombinedOutput() if err != nil { t.Fatalf("Failed to compile the binary: err: %v\nOutput: %s\n", err, out) } if err := os.Chmod(binaryPath, 0755); err != nil { t.Fatalf("Failed to chmod binary: %v", err) } // Now run the binary. cmd := exec.Command(binaryPath) if err := cmd.Start(); err != nil { t.Fatalf("Failed to start command: %v", err) } doneCh := make(chan error, 1) go func() { doneCh <- cmd.Wait() }() // With the repro running, we can continuously send to it // a non-terminal signal such as SIGIO, to spuriously // wakeup pthread_cond_timedwait_relative_np. unfixedTimer := time.NewTimer(2 * time.Second) for { select { case <-time.After(200 * time.Millisecond): // Send the pesky signal that toggles spinning // indefinitely if #27520 is not fixed. cmd.Process.Signal(syscall.SIGIO) case <-unfixedTimer.C: t.Error("Program failed to return on time and has to be killed, issue #27520 still exists") cmd.Process.Signal(syscall.SIGKILL) return case err := <-doneCh: if err != nil { t.Fatalf("The program returned but unfortunately with an error: %v", err) } return } } }