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

View as plain text