protected AsyncResults AddToResponseQueue(Guid requestId) { AsyncResults asyncResults = new AsyncResults(requestId); callBack.AddPendingTask(asyncResults); return(asyncResults); }
public void AddPendingTask(AsyncResults outstandingTask) { lock (tasks) { if (tasks.ContainsKey(outstandingTask.Id)) { throw new ArgumentException( "Cannot add a second task with the same transactionID as a previous one: + " + outstandingTask.Id, "transactionId"); } tasks.Add(outstandingTask.Id, outstandingTask); } }
private Task <TResult> GetAsynchronousResponse <TResult>(AsyncResults taskCompletionSource, CancellationToken cancellationToken) { return(TaskFactoryHelper <TResult> .StartNew( () => { object obj; while (!(taskCompletionSource.TryTake(out obj, (int)TimeSpan.FromMinutes(2).TotalMilliseconds, cancellationToken))) { if (obj is TResult) { return (TResult)obj; } } throw new AbandonedMutexException("We did not receive of reply from the server after 2 minutes for transaction " + taskCompletionSource.Id); }, BackgroundTaskScheduler.OnBackground(), cancellationToken)); }
/// <summary> /// Wires up the given callbacks with the internal implementation of network callbacks. Note this should be called only once /// per asyncResult, otherwise data will be lost. /// </summary> /// <typeparam name="TResult">The type of data contract returned when the operation completes or fails.</typeparam> /// <param name="userCallback">The callback to wire up.</param> /// <param name="asyncResults">The <see cref="AsyncResults"/> associated with the current call.</param> protected void SetupCallback <TResult>(Callback <TResult> userCallback, AsyncResults asyncResults) { Task <TResult> result = GetAsynchronousResponse <TResult>(asyncResults, userCallback.CancellationToken); result.Pipeline( finished => userCallback.OnCompleted(finished.Result), userCallback.CompletedScheduler).OnCancelled( canceled => { CancelTransaction(asyncResults.Id); userCallback.OnCanceled(); }, // bug here - not being called on cancel userCallback.CanceledScheduler).OnError( exception => userCallback.OnError(exception) , userCallback.ErrorScheduler); // bug here - not being called on cancel*/ }
/// <summary> /// Provides a synchronous wait version of a server request and callback. /// If the caller thread is UI thread, it executes asynchronously. /// If the caller thread is not UI thread, it blocks the caller thread. /// </summary> /// <typeparam name="T">The type expected via callback.</typeparam> /// <typeparam name="TResponse"></typeparam> /// <typeparam name="TRequest"></typeparam> /// <param name="request"></param> /// <param name="svcGatewayMethod"></param> /// <param name="timeout">The time to wait before giving up on the service response.</param> /// <returns></returns> /// <remarks>Note that this does not currently support progress reporting. If the service provides progress /// reports this will need to be amended.</remarks> protected TResponse ServerRequest <TResponse, TRequest>(TRequest request, SvcGatewayDelegate <TRequest, TResponse> svcGatewayMethod, TimeSpan timeout) where TResponse : ServiceResponse where TRequest : ICommonRequest { AsyncResults asyncResults = AddToResponseQueue(request.RequestID); TimeSpan defaultTimeout = TimeSpan.FromMinutes(2); if (defaultTimeout < timeout) { timeout = defaultTimeout; } if (UIThreadSingleton.IsCreated && UIThreadSingleton.IsCallFromUIThread) { Task <TResponse> callCompleted = TaskFactoryHelper <TResponse> .StartNew( () => ExecServerRequest(request, svcGatewayMethod, timeout, asyncResults), BackgroundTaskScheduler.OnBackground()); while (!callCompleted.IsFinishedRunning()) { Application.DoEvents(); } if (callCompleted.Status != TaskStatus.RanToCompletion) { if (callCompleted.Exception != null) { AggregateException aggregateException = callCompleted.Exception.Flatten(); if (aggregateException.InnerException != null) { throw aggregateException.InnerException; } } } return(callCompleted.Result); } else { return(ExecServerRequest(request, svcGatewayMethod, timeout, asyncResults)); } }
private ProgressReportingTask <TResult, TProgress> GetAsynchronousResponseWithProgress <TResult, TProgress>(AsyncResults taskCompletionSource, CancellationToken cancellationToken) { ProgressReportingTask <TResult, TProgress> progressReportingTask = new ProgressReportingTask <TResult, TProgress>( progress => { object obj; while (taskCompletionSource.TryTake(out obj, (int)TimeSpan.FromMinutes(2).TotalMilliseconds, cancellationToken)) { progress.MakeProgress((TProgress)obj); if (obj is TProgress) { progress.MakeProgress((TProgress)obj); } else if (obj is TResult) { return((TResult)obj); } else { throw new ArgumentException("Cannot process result type of " + taskCompletionSource.GetConsumingEnumerable(cancellationToken).First().GetType()); } } throw new AbandonedMutexException("We did not receive of reply from the server after 2 minutes for transaction " + taskCompletionSource.Id); }, cancellationToken, TaskCreationOptions.None); progressReportingTask.Start(BackgroundTaskScheduler.OnBackground()); Task tt = progressReportingTask; tt.OnCancelled( canceled => { Trace.WriteLine("Got some more cancels"); }, UITaskScheduler.InvokeAsync()); // bug here - not being called on cancel return(progressReportingTask); }
/// <summary> /// Wires up the given callbacks with the internal implementation of network callbacks. Note this should be called only once /// per asyncResult, otherwise data will be lost. /// </summary> /// <typeparam name="TProgress">The type of data contract that is returned to show progress.</typeparam> /// <typeparam name="TResult">The type of data contract returned when the operation completes or fails.</typeparam> /// <param name="userCallback">The callback to wire up.</param> /// <param name="asyncResults">The <see cref="AsyncResults"/> associated with the current call.</param> protected void SetupCallback <TProgress, TResult>(Callback <TProgress, TResult> userCallback, AsyncResults asyncResults) { ProgressReportingTask <TResult, TProgress> responseWithProgress = GetAsynchronousResponseWithProgress <TResult, TProgress>(asyncResults, userCallback.CancellationToken); ((Task)responseWithProgress).OnCancelled( canceled => { CancelTransaction(asyncResults.Id); userCallback.OnCanceled(); }, userCallback.CanceledScheduler); // bug here - not being called on cancel Task <TResult> result = responseWithProgress.OnProgress( progressData => userCallback.OnProgress(progressData), userCallback.ProgressScheduler); result.Pipeline( finished => { if (finished.Result is IFaulted && ((IFaulted)finished.Result).IsFaulted) { throw new AggregateException(((IFaulted)finished.Result).Message); } userCallback.OnCompleted(finished.Result); }, userCallback.CompletedScheduler) .OnError( exception => userCallback.OnError(exception) , userCallback.ErrorScheduler); }
private TResponse ExecServerRequest <TResponse, TRequest>(TRequest request, SvcGatewayDelegate <TRequest, TResponse> svcGatewayMethod, TimeSpan timeout, AsyncResults asyncResults) where TResponse : ServiceResponse where TRequest : ICommonRequest { TResponse response = svcGatewayMethod(request); if (response.ResponseCode != ResponseCode.Succeeded) { return(response); } object serviceResponse; if (asyncResults.TryTake(out serviceResponse, timeout)) { if (serviceResponse is IFaulted && ((IFaulted)serviceResponse).IsFaulted) { throw new AggregateException(((IFaulted)serviceResponse).Message); } response = (TResponse)serviceResponse; if (response != null && response.ResponseCode != ResponseCode.Succeeded && !string.IsNullOrWhiteSpace(response.ErrorMessage)) { throw new ServerException(response.ErrorMessage); } return(response); } string timeOutString = TimeSpan.FromMinutes(2) > timeout ? timeout.TotalSeconds + "seconds" : "2 minutes"; throw new TimeoutException("We did not receive of reply from the server after " + timeOutString + " for transaction " + asyncResults.Id); }