Source file src/os/fifo_test.go

     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"
    14  	"io/fs"
    15  	"os"
    16  	"path/filepath"
    17  	"strconv"
    18  	"sync"
    19  	"syscall"
    20  	"testing"
    21  	"time"
    22  )
    23  
    24  func TestFifoEOF(t *testing.T) {
    25  	t.Parallel()
    26  
    27  	dir := t.TempDir()
    28  	fifoName := filepath.Join(dir, "fifo")
    29  	if err := syscall.Mkfifo(fifoName, 0600); err != nil {
    30  		t.Fatal(err)
    31  	}
    32  
    33  	// Per https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html#tag_16_357_03:
    34  	//
    35  	// - “If O_NONBLOCK is clear, an open() for reading-only shall block the
    36  	//   calling thread until a thread opens the file for writing. An open() for
    37  	//   writing-only shall block the calling thread until a thread opens the file
    38  	//   for reading.”
    39  	//
    40  	// In order to unblock both open calls, we open the two ends of the FIFO
    41  	// simultaneously in separate goroutines.
    42  
    43  	rc := make(chan *os.File, 1)
    44  	go func() {
    45  		r, err := os.Open(fifoName)
    46  		if err != nil {
    47  			t.Error(err)
    48  		}
    49  		rc <- r
    50  	}()
    51  
    52  	w, err := os.OpenFile(fifoName, os.O_WRONLY, 0)
    53  	if err != nil {
    54  		t.Error(err)
    55  	}
    56  
    57  	r := <-rc
    58  	if t.Failed() {
    59  		if r != nil {
    60  			r.Close()
    61  		}
    62  		if w != nil {
    63  			w.Close()
    64  		}
    65  		return
    66  	}
    67  
    68  	testPipeEOF(t, r, w)
    69  }
    70  
    71  // Issue #59545.
    72  func TestNonPollable(t *testing.T) {
    73  	if testing.Short() {
    74  		t.Skip("skipping test with tight loops in short mode")
    75  	}
    76  
    77  	// We need to open a non-pollable file.
    78  	// This is almost certainly Linux-specific,
    79  	// but if other systems have non-pollable files,
    80  	// we can add them here.
    81  	const nonPollable = "/dev/net/tun"
    82  
    83  	f, err := os.OpenFile(nonPollable, os.O_RDWR, 0)
    84  	if err != nil {
    85  		if errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission) || testenv.SyscallIsNotSupported(err) {
    86  			t.Skipf("can't open %q: %v", nonPollable, err)
    87  		}
    88  		t.Fatal(err)
    89  	}
    90  	f.Close()
    91  
    92  	// On a Linux laptop, before the problem was fixed,
    93  	// this test failed about 50% of the time with this
    94  	// number of iterations.
    95  	// It takes about 1/2 second when it passes.
    96  	const attempts = 20000
    97  
    98  	start := make(chan bool)
    99  	var wg sync.WaitGroup
   100  	wg.Add(1)
   101  	defer wg.Wait()
   102  	go func() {
   103  		defer wg.Done()
   104  		close(start)
   105  		for i := 0; i < attempts; i++ {
   106  			f, err := os.OpenFile(nonPollable, os.O_RDWR, 0)
   107  			if err != nil {
   108  				t.Error(err)
   109  				return
   110  			}
   111  			if err := f.Close(); err != nil {
   112  				t.Error(err)
   113  				return
   114  			}
   115  		}
   116  	}()
   117  
   118  	dir := t.TempDir()
   119  	<-start
   120  	for i := 0; i < attempts; i++ {
   121  		name := filepath.Join(dir, strconv.Itoa(i))
   122  		if err := syscall.Mkfifo(name, 0o600); err != nil {
   123  			t.Fatal(err)
   124  		}
   125  		// The problem only occurs if we use O_NONBLOCK here.
   126  		rd, err := os.OpenFile(name, os.O_RDONLY|syscall.O_NONBLOCK, 0o600)
   127  		if err != nil {
   128  			t.Fatal(err)
   129  		}
   130  		wr, err := os.OpenFile(name, os.O_WRONLY|syscall.O_NONBLOCK, 0o600)
   131  		if err != nil {
   132  			t.Fatal(err)
   133  		}
   134  		const msg = "message"
   135  		if _, err := wr.Write([]byte(msg)); err != nil {
   136  			if errors.Is(err, syscall.EAGAIN) || errors.Is(err, syscall.ENOBUFS) {
   137  				t.Logf("ignoring write error %v", err)
   138  				rd.Close()
   139  				wr.Close()
   140  				continue
   141  			}
   142  			t.Fatalf("write to fifo %d failed: %v", i, err)
   143  		}
   144  		if _, err := rd.Read(make([]byte, len(msg))); err != nil {
   145  			if errors.Is(err, syscall.EAGAIN) || errors.Is(err, syscall.ENOBUFS) {
   146  				t.Logf("ignoring read error %v", err)
   147  				rd.Close()
   148  				wr.Close()
   149  				continue
   150  			}
   151  			t.Fatalf("read from fifo %d failed; %v", i, err)
   152  		}
   153  		if err := rd.Close(); err != nil {
   154  			t.Fatal(err)
   155  		}
   156  		if err := wr.Close(); err != nil {
   157  			t.Fatal(err)
   158  		}
   159  	}
   160  }
   161  
   162  // Issue 60211.
   163  func TestOpenFileNonBlocking(t *testing.T) {
   164  	exe, err := os.Executable()
   165  	if err != nil {
   166  		t.Skipf("can't find executable: %v", err)
   167  	}
   168  	f, err := os.OpenFile(exe, os.O_RDONLY|syscall.O_NONBLOCK, 0666)
   169  	if err != nil {
   170  		t.Fatal(err)
   171  	}
   172  	defer f.Close()
   173  	nonblock, err := unix.IsNonblock(int(f.Fd()))
   174  	if err != nil {
   175  		t.Fatal(err)
   176  	}
   177  	if !nonblock {
   178  		t.Errorf("file opened with O_NONBLOCK but in blocking mode")
   179  	}
   180  }
   181  
   182  func TestNewFileNonBlocking(t *testing.T) {
   183  	var p [2]int
   184  	if err := syscall.Pipe(p[:]); err != nil {
   185  		t.Fatal(err)
   186  	}
   187  	if err := syscall.SetNonblock(p[0], true); err != nil {
   188  		t.Fatal(err)
   189  	}
   190  	f := os.NewFile(uintptr(p[0]), "pipe")
   191  	nonblock, err := unix.IsNonblock(p[0])
   192  	if err != nil {
   193  		t.Fatal(err)
   194  	}
   195  	defer f.Close()
   196  	if !nonblock {
   197  		t.Error("pipe blocking after NewFile")
   198  	}
   199  	fd := f.Fd()
   200  	if fd != uintptr(p[0]) {
   201  		t.Errorf("Fd returned %d, want %d", fd, p[0])
   202  	}
   203  	nonblock, err = unix.IsNonblock(p[0])
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  	if !nonblock {
   208  		t.Error("pipe blocking after Fd")
   209  	}
   210  }
   211  
   212  func TestFIFONonBlockingEOF(t *testing.T) {
   213  	fifoName := filepath.Join(t.TempDir(), "issue-66239-fifo")
   214  	if err := syscall.Mkfifo(fifoName, 0600); err != nil {
   215  		t.Fatalf("Error creating fifo: %v", err)
   216  	}
   217  
   218  	r, err := os.OpenFile(fifoName, os.O_RDONLY|syscall.O_NONBLOCK, os.ModeNamedPipe)
   219  	if err != nil {
   220  		t.Fatalf("Error opening fifo for read: %v", err)
   221  	}
   222  	defer r.Close()
   223  
   224  	w, err := os.OpenFile(fifoName, os.O_WRONLY, os.ModeNamedPipe)
   225  	if err != nil {
   226  		t.Fatalf("Error opening fifo for write: %v", err)
   227  	}
   228  	defer w.Close()
   229  
   230  	data := "Hello Gophers!"
   231  	if _, err := w.WriteString(data); err != nil {
   232  		t.Fatalf("Error writing to fifo: %v", err)
   233  	}
   234  
   235  	// Close the writer after a short delay to open a gap for the reader
   236  	// of FIFO to fall into polling. See https://go.dev/issue/66239#issuecomment-1987620476
   237  	time.AfterFunc(200*time.Millisecond, func() {
   238  		if err := w.Close(); err != nil {
   239  			t.Errorf("Error closing writer: %v", err)
   240  		}
   241  	})
   242  
   243  	buf := make([]byte, len(data))
   244  	n, err := io.ReadAtLeast(r, buf, len(data))
   245  	if n != len(data) || string(buf) != data || err != nil {
   246  		t.Errorf("ReadAtLeast: %v; got %q, want %q", err, buf, data)
   247  		return
   248  	}
   249  
   250  	// Loop reading from FIFO until EOF to ensure that the reader
   251  	// is not blocked infinitely, otherwise there is something wrong
   252  	// with the netpoller.
   253  	for {
   254  		_, err = r.Read(buf)
   255  		if errors.Is(err, io.EOF) {
   256  			break
   257  		}
   258  		if err != nil && !errors.Is(err, syscall.EAGAIN) {
   259  			t.Errorf("Error reading bytes from fifo: %v", err)
   260  			return
   261  		}
   262  	}
   263  }
   264  

View as plain text