Exemple #1
0
        /// <summary>
        /// Creates a waiting operation for this session.  Could be a remote cancellation request or a pending result request.
        /// </summary>
        /// <returns>Wait operation to wait on.</returns>
        public RpcWaitHandle CreateWaitHandle()
        {
            var return_wait = new RpcWaitHandle {
                ReturnResetEvent = new ManualResetEventSlim()
            };

            // Lock the id incrementation to prevent duplicates.
            lock (rpc_call_id_lock) {
                if (++rpc_call_id > ushort.MaxValue)
                {
                    rpc_call_id = 0;
                }
                return_wait.Id = (ushort)rpc_call_id;
            }

            // Add the wait to the outstanding wait dictionary for retrieval later.
            if (LocalWaitHandles.TryAdd(return_wait.Id, return_wait) == false)
            {
                throw new InvalidOperationException($"Id {return_wait.Id} already exists in the return_wait_handles dictionary.");
            }

            return(return_wait);
        }
Exemple #2
0
        /// <summary>
        /// Processes the incoming Rpc call from the recipient connection.
        /// </summary>
        /// <param name="message">Message containing the Rpc call.</param>
        /// <param name="message_type">Type of call this message is.</param>
        private void ProcessRpcCall(MqMessage message, RpcCallMessageType message_type)
        {
            // Execute the processing on the worker thread.
            Task.Run(() => {
                // Retrieve a serialization cache to work with.
                var serialization            = Session.SerializationCache.Get(message);
                ushort rec_message_return_id = 0;

                try {
                    // Skip Handler.Id & RpcMessageType
                    serialization.MessageReader.Skip(2);

                    // Determine if this call has a return value.
                    if (message_type == RpcCallMessageType.MethodCall)
                    {
                        rec_message_return_id = serialization.MessageReader.ReadUInt16();
                    }

                    // Read the string service name, method and number of arguments.
                    var rec_service_name   = serialization.MessageReader.ReadString();
                    var rec_method_name    = serialization.MessageReader.ReadString();
                    var rec_argument_count = serialization.MessageReader.ReadByte();

                    // Verify that the requested service exists.
                    if (Services.ContainsKey(rec_service_name) == false)
                    {
                        throw new Exception($"Service '{rec_service_name}' does not exist.");
                    }

                    // Get the service from the instance list.
                    var service = Services[rec_service_name];

                    // Get the actual method.  TODO: Might want to cache this for performance purposes.
                    var method_info       = service.GetType().GetMethod(rec_method_name);
                    var method_parameters = method_info.GetParameters();

                    // Determine if the last parameter is a cancellation token.
                    var last_param = method_info.GetParameters().LastOrDefault();

                    var cancellation_source = new CancellationTokenSource();

                    // Number used to increase the number of parameters if there is a cancellation token.
                    int cancellation_token_param = 0;
                    RpcWaitHandle cancellation_wait;

                    // If the past parameter is a cancellation token, setup a return wait for this call to allow for remote cancellation.
                    if (rec_message_return_id != 0 && last_param?.ParameterType == typeof(CancellationToken))
                    {
                        cancellation_wait = new RpcWaitHandle {
                            Token       = cancellation_source.Token,
                            TokenSource = cancellation_source,
                            Id          = rec_message_return_id
                        };

                        // Set the number to 1 to increase the parameter number by one.
                        cancellation_token_param = 1;

                        // Add it to the main list of ongoing operations.
                        RemoteWaitHandles.TryAdd(rec_message_return_id, cancellation_wait);
                    }

                    // Setup the parameters to pass to the invoked method.
                    object[] parameters = new object[rec_argument_count + cancellation_token_param];

                    // Determine if we have any parameters to pass to the invoked method.
                    if (rec_argument_count > 0)
                    {
                        serialization.PrepareDeserializeReader();


                        // Parse each parameter to the parameter list.
                        for (int i = 0; i < rec_argument_count; i++)
                        {
                            parameters[i] = serialization.DeserializeFromReader(method_parameters[i].ParameterType, i);
                        }
                    }

                    // Add the cancellation token to the parameters.
                    if (cancellation_token_param > 0)
                    {
                        parameters[parameters.Length - 1] = cancellation_source.Token;
                    }


                    object return_value;
                    try {
                        // Invoke the requested method.
                        return_value = method_info.Invoke(service, parameters);
                    } catch (Exception ex) {
                        // Determine if this method was waited on.  If it was and an exception was thrown,
                        // Let the recipient session know an exception was thrown.
                        if (rec_message_return_id != 0 && ex.InnerException?.GetType() != typeof(OperationCanceledException))
                        {
                            SendRpcException(serialization, ex, rec_message_return_id);
                        }
                        return;
                    } finally {
                        // Remove the cancellation wait if it exists.
                        LocalWaitHandles.TryRemove(rec_message_return_id, out cancellation_wait);
                    }


                    // Determine what to do with the return value.
                    if (message_type == RpcCallMessageType.MethodCall)
                    {
                        // Reset the stream.
                        serialization.Stream.SetLength(0);

                        // Write the Rpc call type and the id.
                        serialization.MessageWriter.Clear();
                        serialization.MessageWriter.Write(Id);
                        serialization.MessageWriter.Write((byte)RpcCallMessageType.MethodReturn);
                        serialization.MessageWriter.Write(rec_message_return_id);

                        // Serialize the return value and add it to the stream.

                        serialization.SerializeToWriter(return_value, 0);

                        // Send the return value message to the recipient.
                        Session.Send(serialization.MessageWriter.ToMessage(true));
                    }

                    // Return the serialization to the cache to be reused.
                    Session.SerializationCache.Put(serialization);
                } catch (Exception ex) {
                    // If an exception occurred, notify the recipient connection.
                    SendRpcException(serialization, ex, rec_message_return_id);
                    Session.SerializationCache.Put(serialization);
                }
            });
        }