public ValueTask <Task <IncomingResponseFrame>?> SendRequestAsync( OutgoingRequestFrame outgoingRequestFrame, bool oneway, bool synchronous, IInvocationObserver?observer, CancellationToken cancel) { cancel.ThrowIfCancellationRequested(); // // Increase the direct count to prevent the thread pool from being destroyed before // invokeAll is called. This will also throw if the object adapter has been deactivated. // _adapter.IncDirectCount(); IChildInvocationObserver?childObserver = null; int requestId = 0; // The CollocatedRequestHandler is an internal object so it's safe to lock (this) as long as our // code doesn't use the collocated request handler as a lock. The lock here is useful to ensure // the protocol trace and observer call is output or called in the same order as the request ID // is allocated. lock (_mutex) { if (!oneway) { requestId = ++_requestId; } if (observer != null) { childObserver = observer.GetCollocatedObserver(_adapter, requestId, outgoingRequestFrame.Size); childObserver?.Attach(); } if (_adapter.Communicator.TraceLevels.Protocol >= 1) { ProtocolTrace.TraceCollocatedFrame(_adapter.Communicator, (byte)Ice1Definitions.FrameType.Request, requestId, outgoingRequestFrame); } } Task <IncomingResponseFrame?> task; if (_adapter.TaskScheduler != null || !synchronous || oneway || _reference.InvocationTimeout > 0) { // Don't invoke from the user thread if async or invocation timeout is set. We also don't dispatch // oneway from the user thread to match the non-collocated behavior where the oneway synchronous // request returns as soon as it's sent over the transport. task = Task.Factory.StartNew(() => InvokeAllAsync(outgoingRequestFrame, requestId, cancel), cancel, TaskCreationOptions.None, _adapter.TaskScheduler ?? TaskScheduler.Default).Unwrap(); if (oneway) { childObserver?.Detach(); return(default);
public async Task <IncomingResponseFrame> SendRequestAsync( OutgoingRequestFrame outgoingRequest, bool oneway, bool synchronous, IInvocationObserver?observer, IProgress <bool> progress, CancellationToken cancel) { cancel.ThrowIfCancellationRequested(); IChildInvocationObserver?childObserver = null; int requestId = 0; // The CollocatedRequestHandler is an internal object so it's safe to lock (this) as long as our // code doesn't use the collocated request handler as a lock. The lock here is useful to ensure // the protocol trace and observer call is output or called in the same order as the request ID // is allocated. lock (_mutex) { if (!oneway) { requestId = ++_requestId; } if (observer != null) { childObserver = observer.GetCollocatedObserver(_adapter, requestId, outgoingRequest.Size); childObserver?.Attach(); } if (_adapter.Communicator.TraceLevels.Protocol >= 1) { ProtocolTrace.TraceFrame(_adapter.Communicator, requestId, outgoingRequest); } } Task <OutgoingResponseFrame> task; if (_adapter.TaskScheduler != null || !synchronous || oneway || cancel != CancellationToken.None) { // Don't invoke from the user thread if async or cancellation token is set. We also don't dispatch // oneway from the user thread to match the non-collocated behavior where the oneway synchronous // request returns as soon as it's sent over the transport. task = Task.Factory.StartNew(() => { progress.Report(false); return(DispatchAsync(outgoingRequest, requestId, cancel)); }, cancel, TaskCreationOptions.None, _adapter.TaskScheduler ?? TaskScheduler.Default).Unwrap(); if (oneway) { childObserver?.Detach(); return(IncomingResponseFrame.WithVoidReturnValue(outgoingRequest.Protocol, outgoingRequest.Encoding)); } } else // Optimization: directly call DispatchAsync { progress.Report(false); task = DispatchAsync(outgoingRequest, requestId, cancel); } Debug.Assert(!oneway); try { OutgoingResponseFrame outgoingResponseFrame = await task.WaitAsync(cancel).ConfigureAwait(false); var incomingResponse = new IncomingResponseFrame( outgoingRequest.Protocol, outgoingResponseFrame.Data.AsArraySegment(), _adapter.IncomingFrameSizeMax); if (_adapter.Communicator.TraceLevels.Protocol >= 1) { ProtocolTrace.TraceFrame(_adapter.Communicator, requestId, incomingResponse); } childObserver?.Reply(incomingResponse.Size); return(incomingResponse); } catch (Exception ex) { childObserver?.Failed(ex.GetType().FullName ?? "System.Exception"); throw; } finally { childObserver?.Detach(); } }
internal override async ValueTask <(long StreamId, IncomingFrame?Frame, bool Fin)> ReceiveAsync( CancellationToken cancel) { FrameType type; ArraySegment <byte> data; while (true) { // Read Slic frame header (type, data) = await ReceiveFrameAsync(CancellationToken.None).ConfigureAwait(false); switch (type) { case FrameType.Ping: { ValueTask task = PrepareAndSendFrameAsync(FrameType.Pong, null, CancellationToken.None); HeartbeatCallback !(); break; } case FrameType.Pong: { // TODO: setup a timer to expect pong frame response? break; } case FrameType.Stream: case FrameType.StreamLast: { (long streamId, int streamIdSize) = data.AsReadOnlySpan().ReadVarLong(); data = data.Slice(streamIdSize); IncomingFrame frame = ParseIce2Frame(data); if (Endpoint.Communicator.TraceLevels.Protocol >= 1) { ProtocolTrace.TraceFrame(Endpoint.Communicator, streamId, frame); } return(StreamId : streamId, Frame : frame, Fin : type == FrameType.StreamLast); } case FrameType.ResetStream: { var istr = new InputStream(data, Encoding); long streamId = istr.ReadVarLong(); long reason = istr.ReadVarLong(); return(StreamId : streamId, Frame : null, Fin : true); } case FrameType.Close: { var istr = new InputStream(data, Encoding); long code = istr.ReadVarLong(); // TODO: is this really useful? string reason = istr.ReadString(); throw new ConnectionClosedByPeerException(reason); } default: { throw new InvalidDataException($"unexpected Slic frame with frame type `{type}'"); } } } }