public async ValueTask <(Subchannel Subchannel, BalancerAddress Address, Action <CompletionContext> OnComplete)> PickAsync(PickContext context, bool waitForReady, CancellationToken cancellationToken) { SubchannelPicker?previousPicker = null; // Wait for a valid picker. When the client state changes a new picker will be returned. // Cancellation will break out of the loop. Typically cancellation will come from a // deadline specified for a call being exceeded. while (true) { var currentPicker = await GetPickerAsync(previousPicker, cancellationToken).ConfigureAwait(false); ConnectionManagerLog.PickStarted(Logger); var result = currentPicker.Pick(context); switch (result.Type) { case PickResultType.Complete: var subchannel = result.Subchannel !; var address = subchannel.CurrentAddress; if (address != null) { ConnectionManagerLog.PickResultSuccessful(Logger, subchannel.Id, address); return(subchannel, address, result.Complete); } else { ConnectionManagerLog.PickResultSubchannelNoCurrentAddress(Logger, subchannel.Id); previousPicker = currentPicker; } break; case PickResultType.Queue: ConnectionManagerLog.PickResultQueued(Logger); previousPicker = currentPicker; break; case PickResultType.Fail: if (waitForReady) { ConnectionManagerLog.PickResultFailureWithWaitForReady(Logger, result.Status); previousPicker = currentPicker; } else { ConnectionManagerLog.PickResultFailure(Logger, result.Status); throw new RpcException(result.Status); } break; case PickResultType.Drop: // Use metadata on the exception to signal the request was dropped. // Metadata is checked by retry. If request was dropped then it isn't retried. var metadata = new Metadata { new Metadata.Entry(GrpcProtocolConstants.DropRequestTrailer, bool.TrueString) }; throw new RpcException(result.Status, metadata); default: throw new InvalidOperationException($"Unexpected pick result type: {result.Type}"); } } }