github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/os/fifo_test.go (about) 1 // Copyright 2015 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 //go:build darwin || dragonfly || freebsd || (linux && !android) || netbsd || openbsd 6 7 package os_test 8 9 import ( 10 "errors" 11 "internal/syscall/unix" 12 "internal/testenv" 13 "io/fs" 14 "os" 15 "path/filepath" 16 "strconv" 17 "sync" 18 "syscall" 19 "testing" 20 ) 21 22 func TestFifoEOF(t *testing.T) { 23 t.Parallel() 24 25 dir := t.TempDir() 26 fifoName := filepath.Join(dir, "fifo") 27 if err := syscall.Mkfifo(fifoName, 0600); err != nil { 28 t.Fatal(err) 29 } 30 31 // Per https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html#tag_16_357_03: 32 // 33 // - “If O_NONBLOCK is clear, an open() for reading-only shall block the 34 // calling thread until a thread opens the file for writing. An open() for 35 // writing-only shall block the calling thread until a thread opens the file 36 // for reading.” 37 // 38 // In order to unblock both open calls, we open the two ends of the FIFO 39 // simultaneously in separate goroutines. 40 41 rc := make(chan *os.File, 1) 42 go func() { 43 r, err := os.Open(fifoName) 44 if err != nil { 45 t.Error(err) 46 } 47 rc <- r 48 }() 49 50 w, err := os.OpenFile(fifoName, os.O_WRONLY, 0) 51 if err != nil { 52 t.Error(err) 53 } 54 55 r := <-rc 56 if t.Failed() { 57 if r != nil { 58 r.Close() 59 } 60 if w != nil { 61 w.Close() 62 } 63 return 64 } 65 66 testPipeEOF(t, r, w) 67 } 68 69 // Issue #59545. 70 func TestNonPollable(t *testing.T) { 71 if testing.Short() { 72 t.Skip("skipping test with tight loops in short mode") 73 } 74 75 // We need to open a non-pollable file. 76 // This is almost certainly Linux-specific, 77 // but if other systems have non-pollable files, 78 // we can add them here. 79 const nonPollable = "/dev/net/tun" 80 81 f, err := os.OpenFile(nonPollable, os.O_RDWR, 0) 82 if err != nil { 83 if errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission) || testenv.SyscallIsNotSupported(err) { 84 t.Skipf("can't open %q: %v", nonPollable, err) 85 } 86 t.Fatal(err) 87 } 88 f.Close() 89 90 // On a Linux laptop, before the problem was fixed, 91 // this test failed about 50% of the time with this 92 // number of iterations. 93 // It takes about 1/2 second when it passes. 94 const attempts = 20000 95 96 start := make(chan bool) 97 var wg sync.WaitGroup 98 wg.Add(1) 99 defer wg.Wait() 100 go func() { 101 defer wg.Done() 102 close(start) 103 for i := 0; i < attempts; i++ { 104 f, err := os.OpenFile(nonPollable, os.O_RDWR, 0) 105 if err != nil { 106 t.Error(err) 107 return 108 } 109 if err := f.Close(); err != nil { 110 t.Error(err) 111 return 112 } 113 } 114 }() 115 116 dir := t.TempDir() 117 <-start 118 for i := 0; i < attempts; i++ { 119 name := filepath.Join(dir, strconv.Itoa(i)) 120 if err := syscall.Mkfifo(name, 0o600); err != nil { 121 t.Fatal(err) 122 } 123 // The problem only occurs if we use O_NONBLOCK here. 124 rd, err := os.OpenFile(name, os.O_RDONLY|syscall.O_NONBLOCK, 0o600) 125 if err != nil { 126 t.Fatal(err) 127 } 128 wr, err := os.OpenFile(name, os.O_WRONLY|syscall.O_NONBLOCK, 0o600) 129 if err != nil { 130 t.Fatal(err) 131 } 132 const msg = "message" 133 if _, err := wr.Write([]byte(msg)); err != nil { 134 if errors.Is(err, syscall.EAGAIN) || errors.Is(err, syscall.ENOBUFS) { 135 t.Logf("ignoring write error %v", err) 136 rd.Close() 137 wr.Close() 138 continue 139 } 140 t.Fatalf("write to fifo %d failed: %v", i, err) 141 } 142 if _, err := rd.Read(make([]byte, len(msg))); err != nil { 143 if errors.Is(err, syscall.EAGAIN) || errors.Is(err, syscall.ENOBUFS) { 144 t.Logf("ignoring read error %v", err) 145 rd.Close() 146 wr.Close() 147 continue 148 } 149 t.Fatalf("read from fifo %d failed; %v", i, err) 150 } 151 if err := rd.Close(); err != nil { 152 t.Fatal(err) 153 } 154 if err := wr.Close(); err != nil { 155 t.Fatal(err) 156 } 157 } 158 } 159 160 // Issue 60211. 161 func TestOpenFileNonBlocking(t *testing.T) { 162 exe, err := os.Executable() 163 if err != nil { 164 t.Skipf("can't find executable: %v", err) 165 } 166 f, err := os.OpenFile(exe, os.O_RDONLY|syscall.O_NONBLOCK, 0666) 167 if err != nil { 168 t.Fatal(err) 169 } 170 defer f.Close() 171 nonblock, err := unix.IsNonblock(int(f.Fd())) 172 if err != nil { 173 t.Fatal(err) 174 } 175 if !nonblock { 176 t.Errorf("file opened with O_NONBLOCK but in blocking mode") 177 } 178 } 179 180 func TestNewFileNonBlocking(t *testing.T) { 181 var p [2]int 182 if err := syscall.Pipe(p[:]); err != nil { 183 t.Fatal(err) 184 } 185 if err := syscall.SetNonblock(p[0], true); err != nil { 186 t.Fatal(err) 187 } 188 f := os.NewFile(uintptr(p[0]), "pipe") 189 nonblock, err := unix.IsNonblock(p[0]) 190 if err != nil { 191 t.Fatal(err) 192 } 193 if !nonblock { 194 t.Error("pipe blocking after NewFile") 195 } 196 fd := f.Fd() 197 if fd != uintptr(p[0]) { 198 t.Errorf("Fd returned %d, want %d", fd, p[0]) 199 } 200 nonblock, err = unix.IsNonblock(p[0]) 201 if err != nil { 202 t.Fatal(err) 203 } 204 if !nonblock { 205 t.Error("pipe blocking after Fd") 206 } 207 }