Go Select Implementation
- The select mechanism in Go is an important and unique feature. Current select mechanism implementation by the official https://github.com/golang/go/blob/master/src/runtime/select.go.
There are several steps to execute a select block:
- Evaluate all involved channels and values to be potentially sent, from top to bottom and left to right.
- Randomise the case orders for polling (the default branch is treated as a special case). The corresponding channels of the orders may be duplicate. The default branch is always put at the last position.
- sort all involved channels to avoid deadlock in the next step. No duplicate channels are in the first N channels of the sorted result, where N is the number of involved channels in the select block. Below, the sorted lock orders mean the the first N ones.
- lock all involved channels by the sorted lock orders in last step.
- poll each cases in the select block by the randomised case orders:
- if the corresponding channel operation is a send-value-to-closed-channel operation, unlock all channels and panic. Go to step 12.
- if the corresponding channel operation is non-blocked, perform the channel operation and unlock all channels, then execute the corresponding case body. The channel operation may wake up another blocked goroutine. Go to step 12.
- if the case is the default branch, then unlock all channels and execute default body. Go to step 12. (up to here, there must be no default branch and all case channel operations are blocked.)
- push (enqueue) the current goroutine (with the case channel operation information) into the receiving or sending goroutine queue of the involved channel in each case operation. The current goroutine may be pushed into the queues of a channel multiple times, for the involved channels in multiple cases may be the same one.
- block the current goroutine and unlock all channels by the sorted lock orders.
- ..., in blocked status, waiting other channel operations to wake up the current goroutine, ...
- the current goroutine is waken up by another channel operation in another goroutine. The other operation may be a channel close operation or a channel send/receive operation. If it is a channel send/receive operation, there must be a case channel receive/send operation cooperating with it. In the cooperation, the current goroutine will be dequeued from the receiving/sending goroutine queue of the channel.
- lock all involved channels by the sorted lock orders.
- dequeue the current goroutine from the receiving goroutine queue or sending goroutine queue of the involved channel in each case operation,
- if the current goroutine is waken up by a channel close operation, go to step 5.
- if the current goroutine is waken up by a channel send/receive operation, the corresponding case of the cooperating receive/send operation has already been found in the dequeuing process, so just unlock all channels by the sorted lock orders and execute the corresponding case body.
- done.