Source file test/chan/select3.go

     1  // run
     2  
     3  // Copyright 2010 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  // Test the semantics of the select statement
     8  // for basic empty/non-empty cases.
     9  
    10  package main
    11  
    12  import "time"
    13  
    14  const always = "function did not"
    15  const never = "function did"
    16  
    17  func unreachable() {
    18  	panic("control flow shouldn't reach here")
    19  }
    20  
    21  // Calls f and verifies that f always/never panics depending on signal.
    22  func testPanic(signal string, f func()) {
    23  	defer func() {
    24  		s := never
    25  		if recover() != nil {
    26  			s = always // f panicked
    27  		}
    28  		if s != signal {
    29  			panic(signal + " panic")
    30  		}
    31  	}()
    32  	f()
    33  }
    34  
    35  // Calls f and empirically verifies that f always/never blocks depending on signal.
    36  func testBlock(signal string, f func()) {
    37  	c := make(chan string)
    38  	go func() {
    39  		f()
    40  		c <- never // f didn't block
    41  	}()
    42  	go func() {
    43  		if signal == never {
    44  			// Wait a long time to make sure that we don't miss our window by accident on a slow machine.
    45  			time.Sleep(10 * time.Second)
    46  		} else {
    47  			// Wait as short a time as we can without false negatives.
    48  			// 10ms should be long enough to catch most failures.
    49  			time.Sleep(10 * time.Millisecond)
    50  		}
    51  		c <- always // f blocked always
    52  	}()
    53  	if <-c != signal {
    54  		panic(signal + " block")
    55  	}
    56  }
    57  
    58  func main() {
    59  	const async = 1 // asynchronous channels
    60  	var nilch chan int
    61  	closedch := make(chan int)
    62  	close(closedch)
    63  
    64  	// sending/receiving from a nil channel blocks
    65  	testBlock(always, func() {
    66  		nilch <- 7
    67  	})
    68  	testBlock(always, func() {
    69  		<-nilch
    70  	})
    71  
    72  	// sending/receiving from a nil channel inside a select is never selected
    73  	testPanic(never, func() {
    74  		select {
    75  		case nilch <- 7:
    76  			unreachable()
    77  		default:
    78  		}
    79  	})
    80  	testPanic(never, func() {
    81  		select {
    82  		case <-nilch:
    83  			unreachable()
    84  		default:
    85  		}
    86  	})
    87  
    88  	// sending to an async channel with free buffer space never blocks
    89  	testBlock(never, func() {
    90  		ch := make(chan int, async)
    91  		ch <- 7
    92  	})
    93  
    94  	// receiving from a closed channel never blocks
    95  	testBlock(never, func() {
    96  		for i := 0; i < 10; i++ {
    97  			if <-closedch != 0 {
    98  				panic("expected zero value when reading from closed channel")
    99  			}
   100  			if x, ok := <-closedch; x != 0 || ok {
   101  				println("closedch:", x, ok)
   102  				panic("expected 0, false from closed channel")
   103  			}
   104  		}
   105  	})
   106  
   107  	// sending to a closed channel panics.
   108  	testPanic(always, func() {
   109  		closedch <- 7
   110  	})
   111  
   112  	// receiving from a non-ready channel always blocks
   113  	testBlock(always, func() {
   114  		ch := make(chan int)
   115  		<-ch
   116  	})
   117  
   118  	// empty selects always block
   119  	testBlock(always, func() {
   120  		select {}
   121  	})
   122  
   123  	// selects with only nil channels always block
   124  	testBlock(always, func() {
   125  		select {
   126  		case <-nilch:
   127  			unreachable()
   128  		}
   129  	})
   130  	testBlock(always, func() {
   131  		select {
   132  		case nilch <- 7:
   133  			unreachable()
   134  		}
   135  	})
   136  	testBlock(always, func() {
   137  		select {
   138  		case <-nilch:
   139  			unreachable()
   140  		case nilch <- 7:
   141  			unreachable()
   142  		}
   143  	})
   144  
   145  	// selects with non-ready non-nil channels always block
   146  	testBlock(always, func() {
   147  		ch := make(chan int)
   148  		select {
   149  		case <-ch:
   150  			unreachable()
   151  		}
   152  	})
   153  
   154  	// selects with default cases don't block
   155  	testBlock(never, func() {
   156  		select {
   157  		default:
   158  		}
   159  	})
   160  	testBlock(never, func() {
   161  		select {
   162  		case <-nilch:
   163  			unreachable()
   164  		default:
   165  		}
   166  	})
   167  	testBlock(never, func() {
   168  		select {
   169  		case nilch <- 7:
   170  			unreachable()
   171  		default:
   172  		}
   173  	})
   174  
   175  	// selects with ready channels don't block
   176  	testBlock(never, func() {
   177  		ch := make(chan int, async)
   178  		select {
   179  		case ch <- 7:
   180  		default:
   181  			unreachable()
   182  		}
   183  	})
   184  	testBlock(never, func() {
   185  		ch := make(chan int, async)
   186  		ch <- 7
   187  		select {
   188  		case <-ch:
   189  		default:
   190  			unreachable()
   191  		}
   192  	})
   193  
   194  	// selects with closed channels behave like ordinary operations
   195  	testBlock(never, func() {
   196  		select {
   197  		case <-closedch:
   198  		}
   199  	})
   200  	testBlock(never, func() {
   201  		select {
   202  		case x := (<-closedch):
   203  			_ = x
   204  		}
   205  	})
   206  	testBlock(never, func() {
   207  		select {
   208  		case x, ok := (<-closedch):
   209  			_, _ = x, ok
   210  		}
   211  	})
   212  	testPanic(always, func() {
   213  		select {
   214  		case closedch <- 7:
   215  		}
   216  	})
   217  
   218  	// select should not get confused if it sees itself
   219  	testBlock(always, func() {
   220  		c := make(chan int)
   221  		select {
   222  		case c <- 1:
   223  		case <-c:
   224  		}
   225  	})
   226  }
   227  

View as plain text