Source file src/os/timeout_test.go

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

View as plain text