[System.ServiceModel] Don't use DateTime.Now for measuring elapsed time
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / ClientRuntimeChannel.cs
index 909e0bcb24b517ea5f42f9e9e315a959327417af..df7a9e4cd9742a07869d9f9be7ada306ca16312e 100644 (file)
@@ -46,9 +46,7 @@ namespace System.ServiceModel.MonoInternal
        {
                ContractDescription Contract { get; }
 
-               OperationContext Context { set; }
-
-               object Process (MethodBase method, string operationName, object [] parameters);
+               object Process (MethodBase method, string operationName, object [] parameters, OperationContext context);
 
                IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState);
 
@@ -69,12 +67,13 @@ namespace System.ServiceModel.MonoInternal
                TimeSpan default_open_timeout, default_close_timeout;
                IChannel channel;
                IChannelFactory factory;
-               OperationContext context;
+               TimeSpan? operation_timeout = null;
+               ChannelFactory channel_factory;
 
                #region delegates
                readonly ProcessDelegate _processDelegate;
 
-               delegate object ProcessDelegate (MethodBase method, string operationName, object [] parameters);
+               delegate object ProcessDelegate (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context);
 
                readonly RequestDelegate requestDelegate;
 
@@ -89,6 +88,7 @@ namespace System.ServiceModel.MonoInternal
                        ChannelFactory channelFactory, EndpointAddress remoteAddress, Uri via)
                        : this (endpoint.CreateClientRuntime (null), endpoint.Contract, channelFactory.DefaultOpenTimeout, channelFactory.DefaultCloseTimeout, null, channelFactory.OpenedChannelFactory, endpoint.Binding.MessageVersion, remoteAddress, via)
                {
+                       channel_factory = channelFactory;
                }
 
                public ClientRuntimeChannel (ClientRuntime runtime, ContractDescription contract, TimeSpan openTimeout, TimeSpan closeTimeout, IChannel contextChannel, IChannelFactory factory, MessageVersion messageVersion, EndpointAddress remoteAddress, Uri via)
@@ -111,7 +111,6 @@ namespace System.ServiceModel.MonoInternal
 
                        // default values
                        AllowInitializationUI = true;
-                       OperationTimeout = TimeSpan.FromMinutes (1);
 
                        if (contextChannel != null)
                                channel = contextChannel;
@@ -149,10 +148,6 @@ namespace System.ServiceModel.MonoInternal
                        get { return channel as IDuplexChannel; }
                }
 
-               public OperationContext Context {
-                       set { context = value; }
-               }
-
                #region IClientChannel
 
                bool did_interactive_initialization;
@@ -329,8 +324,10 @@ namespace System.ServiceModel.MonoInternal
                        }
                }
 
-               [MonoTODO]
-               public TimeSpan OperationTimeout { get; set; }
+               public TimeSpan OperationTimeout {
+                       get { return this.operation_timeout ?? (channel_factory != null ? channel_factory.Endpoint.Binding.SendTimeout : DefaultCommunicationTimeouts.Instance.SendTimeout); }
+                       set { this.operation_timeout = value; }
+               }
 
                public IOutputSession OutputSession {
                        get {
@@ -386,7 +383,6 @@ namespace System.ServiceModel.MonoInternal
 
                protected override void OnClose (TimeSpan timeout)
                {
-                       DateTime start = DateTime.Now;
                        if (channel.State == CommunicationState.Opened)
                                channel.Close (timeout);
                }
@@ -445,36 +441,46 @@ namespace System.ServiceModel.MonoInternal
 
                public IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState)
                {
-                       if (context != null)
-                               throw new InvalidOperationException ("another operation is in progress");
-                       context = OperationContext.Current;
-
-                       return _processDelegate.BeginInvoke (method, operationName, parameters, callback, asyncState);
+                       var p = parameters;
+                       var retval = _processDelegate.BeginInvoke (method, operationName, true, ref p, OperationContext.Current, callback, asyncState);
+                       if (p != parameters)
+                               throw new InvalidOperationException ();
+                       return retval;
                }
 
                public object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result)
                {
-                               
                        if (result == null)
                                throw new ArgumentNullException ("result");
                        if (parameters == null)
                                throw new ArgumentNullException ("parameters");
-                       // FIXME: the method arguments should be verified to be 
-                       // identical to the arguments in the corresponding begin method.
-                       object asyncResult = _processDelegate.EndInvoke (result);
-                       context = null;
-                       return asyncResult;
+
+                       object[] p = parameters;
+                       var retval = _processDelegate.EndInvoke (ref p, result);
+                       if (p == parameters)
+                               return retval;
+
+                       Array.Copy (p, parameters, p.Length);
+                       return retval;
                }
 
-               public object Process (MethodBase method, string operationName, object [] parameters)
+               public object Process (MethodBase method, string operationName, object [] parameters, OperationContext context)
+               {
+                       var p = parameters;
+                       var retval = Process (method, operationName, false, ref p, context);
+                       if (p != parameters)
+                               throw new InvalidOperationException ();
+                       return retval;
+               }
+
+               object Process (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context)
                {
                        var previousContext = OperationContext.Current;
                        try {
                                // Inherit the context from the calling thread
-                               if (this.context != null) 
-                                       OperationContext.Current = this.context;
+                               OperationContext.Current = context;
 
-                               return DoProcess (method, operationName, parameters);
+                               return DoProcess (method, operationName, isAsync, ref parameters, context);
                        } catch (Exception ex) {
                                throw;
                        } finally {
@@ -483,7 +489,7 @@ namespace System.ServiceModel.MonoInternal
                        }
                }
 
-               object DoProcess (MethodBase method, string operationName, object [] parameters)
+               object DoProcess (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context)
                {
                        if (AllowInitializationUI)
                                DisplayInitializationUI ();
@@ -493,9 +499,9 @@ namespace System.ServiceModel.MonoInternal
                                Open ();
 
                        if (!od.IsOneWay)
-                               return Request (od, parameters);
+                               return Request (od, isAsync, ref parameters, context);
                        else {
-                               Output (od, parameters);
+                               Output (od, parameters, context);
                                return null;
                        }
                }
@@ -513,17 +519,17 @@ namespace System.ServiceModel.MonoInternal
                        return od;
                }
 
-               void Output (OperationDescription od, object [] parameters)
+               void Output (OperationDescription od, object [] parameters, OperationContext context)
                {
                        ClientOperation op = runtime.Operations [od.Name];
-                       Send (CreateRequest (op, parameters), OperationTimeout);
+                       Send (CreateRequest (op, parameters, context), OperationTimeout);
                }
 
-               object Request (OperationDescription od, object [] parameters)
+               object Request (OperationDescription od, bool isAsync, ref object [] parameters, OperationContext context)
                {
                        ClientOperation op = runtime.Operations [od.Name];
                        object [] inspections = new object [runtime.MessageInspectors.Count];
-                       Message req = CreateRequest (op, parameters);
+                       Message req = CreateRequest (op, parameters, context);
 
                        for (int i = 0; i < inspections.Length; i++)
                                inspections [i] = runtime.MessageInspectors [i].BeforeSendRequest (ref req, this);
@@ -560,10 +566,15 @@ namespace System.ServiceModel.MonoInternal
                        for (int i = 0; i < inspections.Length; i++)
                                runtime.MessageInspectors [i].AfterReceiveReply (ref res, inspections [i]);
 
-                       if (op.DeserializeReply)
-                               return op.Formatter.DeserializeReply (res, parameters);
-                       else
+                       if (!op.DeserializeReply)
                                return res;
+
+                       if (isAsync && od.EndMethod != null) {
+                               var endParams = od.EndMethod.GetParameters ();
+                               parameters = new object [endParams.Length - 1];
+                       }
+
+                       return op.Formatter.DeserializeReply (res, parameters);
                }
 
                #region Message-based Request() and Send()
@@ -581,9 +592,9 @@ namespace System.ServiceModel.MonoInternal
                        // FIXME: implement ConcurrencyMode check:
                        // if it is .Single && this instance for a callback channel && the operation is invoked inside service operation, then error.
 
-                       DateTime startTime = DateTime.Now;
+                       DateTime startTime = DateTime.UtcNow;
                        OutputChannel.Send (msg, timeout);
-                       return ((IDuplexChannel) channel).Receive (timeout - (DateTime.Now - startTime));
+                       return ((IDuplexChannel) channel).Receive (timeout - (DateTime.UtcNow - startTime));
                }
 
                internal IAsyncResult BeginRequest (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
@@ -615,7 +626,7 @@ namespace System.ServiceModel.MonoInternal
                }
                #endregion
 
-               Message CreateRequest (ClientOperation op, object [] parameters)
+               Message CreateRequest (ClientOperation op, object [] parameters, OperationContext context)
                {
                        MessageVersion version = message_version;
                        if (version == null)