...
Run Format

Source file test/chan/select3.go

Documentation: test/chan

  // run
  
  // Copyright 2010 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  // Test the semantics of the select statement
  // for basic empty/non-empty cases.
  
  package main
  
  import "time"
  
  const always = "function did not"
  const never = "function did"
  
  func unreachable() {
  	panic("control flow shouldn't reach here")
  }
  
  // Calls f and verifies that f always/never panics depending on signal.
  func testPanic(signal string, f func()) {
  	defer func() {
  		s := never
  		if recover() != nil {
  			s = always // f panicked
  		}
  		if s != signal {
  			panic(signal + " panic")
  		}
  	}()
  	f()
  }
  
  // Calls f and empirically verifies that f always/never blocks depending on signal.
  func testBlock(signal string, f func()) {
  	c := make(chan string)
  	go func() {
  		f()
  		c <- never // f didn't block
  	}()
  	go func() {
  		if signal == never {
  			// Wait a long time to make sure that we don't miss our window by accident on a slow machine.
  			time.Sleep(10 * time.Second)
  		} else {
  			// Wait as short a time as we can without false negatives.
  			// 10ms should be long enough to catch most failures.
  			time.Sleep(10 * time.Millisecond)
  		}
  		c <- always // f blocked always
  	}()
  	if <-c != signal {
  		panic(signal + " block")
  	}
  }
  
  func main() {
  	const async = 1 // asynchronous channels
  	var nilch chan int
  	closedch := make(chan int)
  	close(closedch)
  
  	// sending/receiving from a nil channel blocks
  	testBlock(always, func() {
  		nilch <- 7
  	})
  	testBlock(always, func() {
  		<-nilch
  	})
  
  	// sending/receiving from a nil channel inside a select is never selected
  	testPanic(never, func() {
  		select {
  		case nilch <- 7:
  			unreachable()
  		default:
  		}
  	})
  	testPanic(never, func() {
  		select {
  		case <-nilch:
  			unreachable()
  		default:
  		}
  	})
  
  	// sending to an async channel with free buffer space never blocks
  	testBlock(never, func() {
  		ch := make(chan int, async)
  		ch <- 7
  	})
  
  	// receiving from a closed channel never blocks
  	testBlock(never, func() {
  		for i := 0; i < 10; i++ {
  			if <-closedch != 0 {
  				panic("expected zero value when reading from closed channel")
  			}
  			if x, ok := <-closedch; x != 0 || ok {
  				println("closedch:", x, ok)
  				panic("expected 0, false from closed channel")
  			}
  		}
  	})
  
  	// sending to a closed channel panics.
  	testPanic(always, func() {
  		closedch <- 7
  	})
  
  	// receiving from a non-ready channel always blocks
  	testBlock(always, func() {
  		ch := make(chan int)
  		<-ch
  	})
  
  	// empty selects always block
  	testBlock(always, func() {
  		select {}
  	})
  
  	// selects with only nil channels always block
  	testBlock(always, func() {
  		select {
  		case <-nilch:
  			unreachable()
  		}
  	})
  	testBlock(always, func() {
  		select {
  		case nilch <- 7:
  			unreachable()
  		}
  	})
  	testBlock(always, func() {
  		select {
  		case <-nilch:
  			unreachable()
  		case nilch <- 7:
  			unreachable()
  		}
  	})
  
  	// selects with non-ready non-nil channels always block
  	testBlock(always, func() {
  		ch := make(chan int)
  		select {
  		case <-ch:
  			unreachable()
  		}
  	})
  
  	// selects with default cases don't block
  	testBlock(never, func() {
  		select {
  		default:
  		}
  	})
  	testBlock(never, func() {
  		select {
  		case <-nilch:
  			unreachable()
  		default:
  		}
  	})
  	testBlock(never, func() {
  		select {
  		case nilch <- 7:
  			unreachable()
  		default:
  		}
  	})
  
  	// selects with ready channels don't block
  	testBlock(never, func() {
  		ch := make(chan int, async)
  		select {
  		case ch <- 7:
  		default:
  			unreachable()
  		}
  	})
  	testBlock(never, func() {
  		ch := make(chan int, async)
  		ch <- 7
  		select {
  		case <-ch:
  		default:
  			unreachable()
  		}
  	})
  
  	// selects with closed channels behave like ordinary operations
  	testBlock(never, func() {
  		select {
  		case <-closedch:
  		}
  	})
  	testBlock(never, func() {
  		select {
  		case x := (<-closedch):
  			_ = x
  		}
  	})
  	testBlock(never, func() {
  		select {
  		case x, ok := (<-closedch):
  			_, _ = x, ok
  		}
  	})
  	testPanic(always, func() {
  		select {
  		case closedch <- 7:
  		}
  	})
  
  	// select should not get confused if it sees itself
  	testBlock(always, func() {
  		c := make(chan int)
  		select {
  		case c <- 1:
  		case <-c:
  		}
  	})
  }
  

View as plain text