diff --git a/src/runtime/chan.go b/src/runtime/chan.go index e437798b09..9964c92878 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -432,11 +432,52 @@ func unlockAllStates(states []chanSelectState) { func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelOp) (uint32, bool) { mask := interrupt.Disable() + const selectNoIndex = ^uint32(0) + + // Fast path for non-blocking single-state selects, for example: + // + // select { + // case ch <- value: + // default: + // } + // + // Such selects can only lock at most one channel, so they don't need the + // global select lock. Also avoid lockAllStates/unlockAllStates here: they use + // channel.selectLocked, which is shared state protected by chanSelectLock. + if len(states) == 1 && len(ops) == 0 { + state := states[0] + selectIndex := selectNoIndex + selectOk := true + + if state.ch != nil { + if hasParallelism { + state.ch.lock.Lock() + } + + if state.value == nil { // chan receive + if received, ok := state.ch.tryRecv(recvbuf); received { + selectIndex = 0 + selectOk = ok + } + } else { // chan send + if state.ch.trySend(state.value) { + selectIndex = 0 + } + } + + if hasParallelism { + state.ch.lock.Unlock() + } + } + + interrupt.Restore(mask) + return selectIndex, selectOk + } + // Lock everything. chanSelectLock.Lock() lockAllStates(states) - const selectNoIndex = ^uint32(0) selectIndex := selectNoIndex selectOk := true