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 := testenv.Executable(t)
   165  	f, err := os.OpenFile(exe, os.O_RDONLY|syscall.O_NONBLOCK, 0666)
   166  	if err != nil {
   167  		t.Fatal(err)
   168  	}
   169  	defer f.Close()
   170  	nonblock, err := unix.IsNonblock(int(f.Fd()))
   171  	if err != nil {
   172  		t.Fatal(err)
   173  	}
   174  	if !nonblock {
   175  		t.Errorf("file opened with O_NONBLOCK but in blocking mode")
   176  	}
   177  }
   178  
   179  func TestNewFileNonBlocking(t *testing.T) {
   180  	var p [2]int
   181  	if err := syscall.Pipe(p[:]); err != nil {
   182  		t.Fatal(err)
   183  	}
   184  	if err := syscall.SetNonblock(p[0], true); err != nil {
   185  		t.Fatal(err)
   186  	}
   187  	f := os.NewFile(uintptr(p[0]), "pipe")
   188  	nonblock, err := unix.IsNonblock(p[0])
   189  	if err != nil {
   190  		t.Fatal(err)
   191  	}
   192  	defer f.Close()
   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  }
   208  
   209  func TestFIFONonBlockingEOF(t *testing.T) {
   210  	fifoName := filepath.Join(t.TempDir(), "issue-66239-fifo")
   211  	if err := syscall.Mkfifo(fifoName, 0600); err != nil {
   212  		t.Fatalf("Error creating fifo: %v", err)
   213  	}
   214  
   215  	r, err := os.OpenFile(fifoName, os.O_RDONLY|syscall.O_NONBLOCK, os.ModeNamedPipe)
   216  	if err != nil {
   217  		t.Fatalf("Error opening fifo for read: %v", err)
   218  	}
   219  	defer r.Close()
   220  
   221  	w, err := os.OpenFile(fifoName, os.O_WRONLY, os.ModeNamedPipe)
   222  	if err != nil {
   223  		t.Fatalf("Error opening fifo for write: %v", err)
   224  	}
   225  	defer w.Close()
   226  
   227  	data := "Hello Gophers!"
   228  	if _, err := w.WriteString(data); err != nil {
   229  		t.Fatalf("Error writing to fifo: %v", err)
   230  	}
   231  
   232  	// Close the writer after a short delay to open a gap for the reader
   233  	// of FIFO to fall into polling. See https://go.dev/issue/66239#issuecomment-1987620476
   234  	time.AfterFunc(200*time.Millisecond, func() {
   235  		if err := w.Close(); err != nil {
   236  			t.Errorf("Error closing writer: %v", err)
   237  		}
   238  	})
   239  
   240  	buf := make([]byte, len(data))
   241  	n, err := io.ReadAtLeast(r, buf, len(data))
   242  	if n != len(data) || string(buf) != data || err != nil {
   243  		t.Errorf("ReadAtLeast: %v; got %q, want %q", err, buf, data)
   244  		return
   245  	}
   246  
   247  	// Loop reading from FIFO until EOF to ensure that the reader
   248  	// is not blocked infinitely, otherwise there is something wrong
   249  	// with the netpoller.
   250  	for {
   251  		_, err = r.Read(buf)
   252  		if errors.Is(err, io.EOF) {
   253  			break
   254  		}
   255  		if err != nil && !errors.Is(err, syscall.EAGAIN) {
   256  			t.Errorf("Error reading bytes from fifo: %v", err)
   257  			return
   258  		}
   259  	}
   260  }
   261  

View as plain text