Black Lives Matter. Support the Equal Justice Initiative.

Source file src/os/timeout_test.go

Documentation: os

     1  // Copyright 2017 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 !js && !plan9 && !windows
     6  // +build !js,!plan9,!windows
     7  
     8  package os_test
     9  
    10  import (
    11  	"fmt"
    12  	"io"
    13  	"math/rand"
    14  	"os"
    15  	"os/signal"
    16  	"runtime"
    17  	"sync"
    18  	"syscall"
    19  	"testing"
    20  	"time"
    21  )
    22  
    23  func TestNonpollableDeadline(t *testing.T) {
    24  	// On BSD systems regular files seem to be pollable,
    25  	// so just run this test on Linux.
    26  	if runtime.GOOS != "linux" {
    27  		t.Skipf("skipping on %s", runtime.GOOS)
    28  	}
    29  
    30  	f, err := os.CreateTemp("", "ostest")
    31  	if err != nil {
    32  		t.Fatal(err)
    33  	}
    34  	defer os.Remove(f.Name())
    35  	defer f.Close()
    36  	deadline := time.Now().Add(10 * time.Second)
    37  	if err := f.SetDeadline(deadline); err != os.ErrNoDeadline {
    38  		t.Errorf("SetDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
    39  	}
    40  	if err := f.SetReadDeadline(deadline); err != os.ErrNoDeadline {
    41  		t.Errorf("SetReadDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
    42  	}
    43  	if err := f.SetWriteDeadline(deadline); err != os.ErrNoDeadline {
    44  		t.Errorf("SetWriteDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
    45  	}
    46  }
    47  
    48  // noDeadline is a zero time.Time value, which cancels a deadline.
    49  var noDeadline time.Time
    50  
    51  var readTimeoutTests = []struct {
    52  	timeout time.Duration
    53  	xerrs   [2]error // expected errors in transition
    54  }{
    55  	// Tests that read deadlines work, even if there's data ready
    56  	// to be read.
    57  	{-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
    58  
    59  	{50 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
    60  }
    61  
    62  func TestReadTimeout(t *testing.T) {
    63  	t.Parallel()
    64  
    65  	r, w, err := os.Pipe()
    66  	if err != nil {
    67  		t.Fatal(err)
    68  	}
    69  	defer r.Close()
    70  	defer w.Close()
    71  
    72  	if _, err := w.Write([]byte("READ TIMEOUT TEST")); err != nil {
    73  		t.Fatal(err)
    74  	}
    75  
    76  	for i, tt := range readTimeoutTests {
    77  		if err := r.SetReadDeadline(time.Now().Add(tt.timeout)); err != nil {
    78  			t.Fatalf("#%d: %v", i, err)
    79  		}
    80  		var b [1]byte
    81  		for j, xerr := range tt.xerrs {
    82  			for {
    83  				n, err := r.Read(b[:])
    84  				if xerr != nil {
    85  					if !isDeadlineExceeded(err) {
    86  						t.Fatalf("#%d/%d: %v", i, j, err)
    87  					}
    88  				}
    89  				if err == nil {
    90  					time.Sleep(tt.timeout / 3)
    91  					continue
    92  				}
    93  				if n != 0 {
    94  					t.Fatalf("#%d/%d: read %d; want 0", i, j, n)
    95  				}
    96  				break
    97  			}
    98  		}
    99  	}
   100  }
   101  
   102  func TestReadTimeoutMustNotReturn(t *testing.T) {
   103  	t.Parallel()
   104  
   105  	r, w, err := os.Pipe()
   106  	if err != nil {
   107  		t.Fatal(err)
   108  	}
   109  	defer r.Close()
   110  	defer w.Close()
   111  
   112  	max := time.NewTimer(100 * time.Millisecond)
   113  	defer max.Stop()
   114  	ch := make(chan error)
   115  	go func() {
   116  		if err := r.SetDeadline(time.Now().Add(-5 * time.Second)); err != nil {
   117  			t.Error(err)
   118  		}
   119  		if err := r.SetWriteDeadline(time.Now().Add(-5 * time.Second)); err != nil {
   120  			t.Error(err)
   121  		}
   122  		if err := r.SetReadDeadline(noDeadline); err != nil {
   123  			t.Error(err)
   124  		}
   125  		var b [1]byte
   126  		_, err := r.Read(b[:])
   127  		ch <- err
   128  	}()
   129  
   130  	select {
   131  	case err := <-ch:
   132  		t.Fatalf("expected Read to not return, but it returned with %v", err)
   133  	case <-max.C:
   134  		w.Close()
   135  		err := <-ch // wait for tester goroutine to stop
   136  		if os.IsTimeout(err) {
   137  			t.Fatal(err)
   138  		}
   139  	}
   140  }
   141  
   142  var writeTimeoutTests = []struct {
   143  	timeout time.Duration
   144  	xerrs   [2]error // expected errors in transition
   145  }{
   146  	// Tests that write deadlines work, even if there's buffer
   147  	// space available to write.
   148  	{-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
   149  
   150  	{10 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
   151  }
   152  
   153  func TestWriteTimeout(t *testing.T) {
   154  	t.Parallel()
   155  
   156  	for i, tt := range writeTimeoutTests {
   157  		t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
   158  			r, w, err := os.Pipe()
   159  			if err != nil {
   160  				t.Fatal(err)
   161  			}
   162  			defer r.Close()
   163  			defer w.Close()
   164  
   165  			if err := w.SetWriteDeadline(time.Now().Add(tt.timeout)); err != nil {
   166  				t.Fatalf("%v", err)
   167  			}
   168  			for j, xerr := range tt.xerrs {
   169  				for {
   170  					n, err := w.Write([]byte("WRITE TIMEOUT TEST"))
   171  					if xerr != nil {
   172  						if !isDeadlineExceeded(err) {
   173  							t.Fatalf("%d: %v", j, err)
   174  						}
   175  					}
   176  					if err == nil {
   177  						time.Sleep(tt.timeout / 3)
   178  						continue
   179  					}
   180  					if n != 0 {
   181  						t.Fatalf("%d: wrote %d; want 0", j, n)
   182  					}
   183  					break
   184  				}
   185  			}
   186  		})
   187  	}
   188  }
   189  
   190  func TestWriteTimeoutMustNotReturn(t *testing.T) {
   191  	t.Parallel()
   192  
   193  	r, w, err := os.Pipe()
   194  	if err != nil {
   195  		t.Fatal(err)
   196  	}
   197  	defer r.Close()
   198  	defer w.Close()
   199  
   200  	max := time.NewTimer(100 * time.Millisecond)
   201  	defer max.Stop()
   202  	ch := make(chan error)
   203  	go func() {
   204  		if err := w.SetDeadline(time.Now().Add(-5 * time.Second)); err != nil {
   205  			t.Error(err)
   206  		}
   207  		if err := w.SetReadDeadline(time.Now().Add(-5 * time.Second)); err != nil {
   208  			t.Error(err)
   209  		}
   210  		if err := w.SetWriteDeadline(noDeadline); err != nil {
   211  			t.Error(err)
   212  		}
   213  		var b [1]byte
   214  		for {
   215  			if _, err := w.Write(b[:]); err != nil {
   216  				ch <- err
   217  				break
   218  			}
   219  		}
   220  	}()
   221  
   222  	select {
   223  	case err := <-ch:
   224  		t.Fatalf("expected Write to not return, but it returned with %v", err)
   225  	case <-max.C:
   226  		r.Close()
   227  		err := <-ch // wait for tester goroutine to stop
   228  		if os.IsTimeout(err) {
   229  			t.Fatal(err)
   230  		}
   231  	}
   232  }
   233  
   234  func timeoutReader(r *os.File, d, min, max time.Duration, ch chan<- error) {
   235  	var err error
   236  	defer func() { ch <- err }()
   237  
   238  	t0 := time.Now()
   239  	if err = r.SetReadDeadline(time.Now().Add(d)); err != nil {
   240  		return
   241  	}
   242  	b := make([]byte, 256)
   243  	var n int
   244  	n, err = r.Read(b)
   245  	t1 := time.Now()
   246  	if n != 0 || err == nil || !isDeadlineExceeded(err) {
   247  		err = fmt.Errorf("Read did not return (0, timeout): (%d, %v)", n, err)
   248  		return
   249  	}
   250  	if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() {
   251  		err = fmt.Errorf("Read took %s; expected %s", dt, d)
   252  		return
   253  	}
   254  }
   255  
   256  func TestReadTimeoutFluctuation(t *testing.T) {
   257  	t.Parallel()
   258  
   259  	r, w, err := os.Pipe()
   260  	if err != nil {
   261  		t.Fatal(err)
   262  	}
   263  	defer r.Close()
   264  	defer w.Close()
   265  
   266  	max := time.NewTimer(time.Second)
   267  	defer max.Stop()
   268  	ch := make(chan error)
   269  	go timeoutReader(r, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch)
   270  
   271  	select {
   272  	case <-max.C:
   273  		t.Fatal("Read took over 1s; expected 0.1s")
   274  	case err := <-ch:
   275  		if !isDeadlineExceeded(err) {
   276  			t.Fatal(err)
   277  		}
   278  	}
   279  }
   280  
   281  func timeoutWriter(w *os.File, d, min, max time.Duration, ch chan<- error) {
   282  	var err error
   283  	defer func() { ch <- err }()
   284  
   285  	t0 := time.Now()
   286  	if err = w.SetWriteDeadline(time.Now().Add(d)); err != nil {
   287  		return
   288  	}
   289  	var n int
   290  	for {
   291  		n, err = w.Write([]byte("TIMEOUT WRITER"))
   292  		if err != nil {
   293  			break
   294  		}
   295  	}
   296  	t1 := time.Now()
   297  	if err == nil || !isDeadlineExceeded(err) {
   298  		err = fmt.Errorf("Write did not return (any, timeout): (%d, %v)", n, err)
   299  		return
   300  	}
   301  	if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() {
   302  		err = fmt.Errorf("Write took %s; expected %s", dt, d)
   303  		return
   304  	}
   305  }
   306  
   307  func TestWriteTimeoutFluctuation(t *testing.T) {
   308  	t.Parallel()
   309  
   310  	r, w, err := os.Pipe()
   311  	if err != nil {
   312  		t.Fatal(err)
   313  	}
   314  	defer r.Close()
   315  	defer w.Close()
   316  
   317  	d := time.Second
   318  	max := time.NewTimer(d)
   319  	defer max.Stop()
   320  	ch := make(chan error)
   321  	go timeoutWriter(w, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch)
   322  
   323  	select {
   324  	case <-max.C:
   325  		t.Fatalf("Write took over %v; expected 0.1s", d)
   326  	case err := <-ch:
   327  		if !isDeadlineExceeded(err) {
   328  			t.Fatal(err)
   329  		}
   330  	}
   331  }
   332  
   333  func TestVariousDeadlines(t *testing.T) {
   334  	t.Parallel()
   335  	testVariousDeadlines(t)
   336  }
   337  
   338  func TestVariousDeadlines1Proc(t *testing.T) {
   339  	// Cannot use t.Parallel - modifies global GOMAXPROCS.
   340  	if testing.Short() {
   341  		t.Skip("skipping in short mode")
   342  	}
   343  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
   344  	testVariousDeadlines(t)
   345  }
   346  
   347  func TestVariousDeadlines4Proc(t *testing.T) {
   348  	// Cannot use t.Parallel - modifies global GOMAXPROCS.
   349  	if testing.Short() {
   350  		t.Skip("skipping in short mode")
   351  	}
   352  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
   353  	testVariousDeadlines(t)
   354  }
   355  
   356  type neverEnding byte
   357  
   358  func (b neverEnding) Read(p []byte) (int, error) {
   359  	for i := range p {
   360  		p[i] = byte(b)
   361  	}
   362  	return len(p), nil
   363  }
   364  
   365  func testVariousDeadlines(t *testing.T) {
   366  	type result struct {
   367  		n   int64
   368  		err error
   369  		d   time.Duration
   370  	}
   371  
   372  	handler := func(w *os.File, pasvch chan result) {
   373  		// The writer, with no timeouts of its own,
   374  		// sending bytes to clients as fast as it can.
   375  		t0 := time.Now()
   376  		n, err := io.Copy(w, neverEnding('a'))
   377  		dt := time.Since(t0)
   378  		pasvch <- result{n, err, dt}
   379  	}
   380  
   381  	for _, timeout := range []time.Duration{
   382  		1 * time.Nanosecond,
   383  		2 * time.Nanosecond,
   384  		5 * time.Nanosecond,
   385  		50 * time.Nanosecond,
   386  		100 * time.Nanosecond,
   387  		200 * time.Nanosecond,
   388  		500 * time.Nanosecond,
   389  		750 * time.Nanosecond,
   390  		1 * time.Microsecond,
   391  		5 * time.Microsecond,
   392  		25 * time.Microsecond,
   393  		250 * time.Microsecond,
   394  		500 * time.Microsecond,
   395  		1 * time.Millisecond,
   396  		5 * time.Millisecond,
   397  		100 * time.Millisecond,
   398  		250 * time.Millisecond,
   399  		500 * time.Millisecond,
   400  		1 * time.Second,
   401  	} {
   402  		numRuns := 3
   403  		if testing.Short() {
   404  			numRuns = 1
   405  			if timeout > 500*time.Microsecond {
   406  				continue
   407  			}
   408  		}
   409  		for run := 0; run < numRuns; run++ {
   410  			t.Run(fmt.Sprintf("%v-%d", timeout, run+1), func(t *testing.T) {
   411  				r, w, err := os.Pipe()
   412  				if err != nil {
   413  					t.Fatal(err)
   414  				}
   415  				defer r.Close()
   416  				defer w.Close()
   417  
   418  				pasvch := make(chan result)
   419  				go handler(w, pasvch)
   420  
   421  				tooLong := 5 * time.Second
   422  				max := time.NewTimer(tooLong)
   423  				defer max.Stop()
   424  				actvch := make(chan result)
   425  				go func() {
   426  					t0 := time.Now()
   427  					if err := r.SetDeadline(t0.Add(timeout)); err != nil {
   428  						t.Error(err)
   429  					}
   430  					n, err := io.Copy(io.Discard, r)
   431  					dt := time.Since(t0)
   432  					r.Close()
   433  					actvch <- result{n, err, dt}
   434  				}()
   435  
   436  				select {
   437  				case res := <-actvch:
   438  					if !isDeadlineExceeded(err) {
   439  						t.Logf("good client timeout after %v, reading %d bytes", res.d, res.n)
   440  					} else {
   441  						t.Fatalf("client Copy = %d, %v; want timeout", res.n, res.err)
   442  					}
   443  				case <-max.C:
   444  					t.Fatalf("timeout (%v) waiting for client to timeout (%v) reading", tooLong, timeout)
   445  				}
   446  
   447  				select {
   448  				case res := <-pasvch:
   449  					t.Logf("writer in %v wrote %d: %v", res.d, res.n, res.err)
   450  				case <-max.C:
   451  					t.Fatalf("timeout waiting for writer to finish writing")
   452  				}
   453  			})
   454  		}
   455  	}
   456  }
   457  
   458  func TestReadWriteDeadlineRace(t *testing.T) {
   459  	t.Parallel()
   460  
   461  	N := 1000
   462  	if testing.Short() {
   463  		N = 50
   464  	}
   465  
   466  	r, w, err := os.Pipe()
   467  	if err != nil {
   468  		t.Fatal(err)
   469  	}
   470  	defer r.Close()
   471  	defer w.Close()
   472  
   473  	var wg sync.WaitGroup
   474  	wg.Add(3)
   475  	go func() {
   476  		defer wg.Done()
   477  		tic := time.NewTicker(2 * time.Microsecond)
   478  		defer tic.Stop()
   479  		for i := 0; i < N; i++ {
   480  			if err := r.SetReadDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
   481  				break
   482  			}
   483  			if err := w.SetWriteDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
   484  				break
   485  			}
   486  			<-tic.C
   487  		}
   488  	}()
   489  	go func() {
   490  		defer wg.Done()
   491  		var b [1]byte
   492  		for i := 0; i < N; i++ {
   493  			_, err := r.Read(b[:])
   494  			if err != nil && !isDeadlineExceeded(err) {
   495  				t.Error("Read returned non-timeout error", err)
   496  			}
   497  		}
   498  	}()
   499  	go func() {
   500  		defer wg.Done()
   501  		var b [1]byte
   502  		for i := 0; i < N; i++ {
   503  			_, err := w.Write(b[:])
   504  			if err != nil && !isDeadlineExceeded(err) {
   505  				t.Error("Write returned non-timeout error", err)
   506  			}
   507  		}
   508  	}()
   509  	wg.Wait() // wait for tester goroutine to stop
   510  }
   511  
   512  // TestRacyRead tests that it is safe to mutate the input Read buffer
   513  // immediately after cancellation has occurred.
   514  func TestRacyRead(t *testing.T) {
   515  	t.Parallel()
   516  
   517  	r, w, err := os.Pipe()
   518  	if err != nil {
   519  		t.Fatal(err)
   520  	}
   521  	defer r.Close()
   522  	defer w.Close()
   523  
   524  	var wg sync.WaitGroup
   525  	defer wg.Wait()
   526  
   527  	go io.Copy(w, rand.New(rand.NewSource(0)))
   528  
   529  	r.SetReadDeadline(time.Now().Add(time.Millisecond))
   530  	for i := 0; i < 10; i++ {
   531  		wg.Add(1)
   532  		go func() {
   533  			defer wg.Done()
   534  
   535  			b1 := make([]byte, 1024)
   536  			b2 := make([]byte, 1024)
   537  			for j := 0; j < 100; j++ {
   538  				_, err := r.Read(b1)
   539  				copy(b1, b2) // Mutate b1 to trigger potential race
   540  				if err != nil {
   541  					if !isDeadlineExceeded(err) {
   542  						t.Error(err)
   543  					}
   544  					r.SetReadDeadline(time.Now().Add(time.Millisecond))
   545  				}
   546  			}
   547  		}()
   548  	}
   549  }
   550  
   551  // TestRacyWrite tests that it is safe to mutate the input Write buffer
   552  // immediately after cancellation has occurred.
   553  func TestRacyWrite(t *testing.T) {
   554  	t.Parallel()
   555  
   556  	r, w, err := os.Pipe()
   557  	if err != nil {
   558  		t.Fatal(err)
   559  	}
   560  	defer r.Close()
   561  	defer w.Close()
   562  
   563  	var wg sync.WaitGroup
   564  	defer wg.Wait()
   565  
   566  	go io.Copy(io.Discard, r)
   567  
   568  	w.SetWriteDeadline(time.Now().Add(time.Millisecond))
   569  	for i := 0; i < 10; i++ {
   570  		wg.Add(1)
   571  		go func() {
   572  			defer wg.Done()
   573  
   574  			b1 := make([]byte, 1024)
   575  			b2 := make([]byte, 1024)
   576  			for j := 0; j < 100; j++ {
   577  				_, err := w.Write(b1)
   578  				copy(b1, b2) // Mutate b1 to trigger potential race
   579  				if err != nil {
   580  					if !isDeadlineExceeded(err) {
   581  						t.Error(err)
   582  					}
   583  					w.SetWriteDeadline(time.Now().Add(time.Millisecond))
   584  				}
   585  			}
   586  		}()
   587  	}
   588  }
   589  
   590  // Closing a TTY while reading from it should not hang.  Issue 23943.
   591  func TestTTYClose(t *testing.T) {
   592  	// Ignore SIGTTIN in case we are running in the background.
   593  	signal.Ignore(syscall.SIGTTIN)
   594  	defer signal.Reset(syscall.SIGTTIN)
   595  
   596  	f, err := os.Open("/dev/tty")
   597  	if err != nil {
   598  		t.Skipf("skipping because opening /dev/tty failed: %v", err)
   599  	}
   600  
   601  	go func() {
   602  		var buf [1]byte
   603  		f.Read(buf[:])
   604  	}()
   605  
   606  	// Give the goroutine a chance to enter the read.
   607  	// It doesn't matter much if it occasionally fails to do so,
   608  	// we won't be testing what we want to test but the test will pass.
   609  	time.Sleep(time.Millisecond)
   610  
   611  	c := make(chan bool)
   612  	go func() {
   613  		defer close(c)
   614  		f.Close()
   615  	}()
   616  
   617  	select {
   618  	case <-c:
   619  	case <-time.After(time.Second):
   620  		t.Error("timed out waiting for close")
   621  	}
   622  
   623  	// On some systems the goroutines may now be hanging.
   624  	// There's not much we can do about that.
   625  }
   626  

View as plain text