| // 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 | |
| } | |
| } | |
| } |