3
\$\begingroup\$

I have a WPF Control Panel app, where I'm trying to stay close to an MVVM architecture. The control panel (the "the CP") is for a WCF service ("the Scheduler"), hosted in a Windows Service ("the Host"). My main concern, and topic for review here, is how I am avoiding blocking the CP responsiveness for too long. The Host control operations sometimes take longer than 3 seconds, which is long for a UI.

Because the start and stop methods are typed void, and I have been advised to steer well clear of using async void methods, instead I use a method I derived from an example somewhere:

private void ExecuteWithTimeout(Action task, int? timeout = null)
{
 timeout = timeout ?? HostServiceConstants.ServiceControlTimeout;
 var source = new CancellationTokenSource();
 var timedTask = Task.Factory.StartNew(task, source.Token).ContinueWith((t) =>
 {
 if (t.Exception != null)
 {
 t.Exception.Handle(ex =>
 {
 var frame = new StackFrame(1);
 var methodName = frame.GetMethod().Name;
 _logger.Warn("'ExecuteWithTimeout' exception from: '{0}': {1}", methodName, ex.Message);
 return true;
 });
 }
 });
 bool done = timedTask.Wait(timeout.Value);
 if (!done)
 {
 source.Cancel();
 var frame = new StackFrame(1);
 var methodName = frame.GetMethod().Name;
 _logger.Warn("'{0}' timed out after {1} milliseconds.", methodName, timeout);
 }
}

This is called from the viewmodel, when a Command is invoked from the view:

void ExecuteStartHostCommand()
{
 Exception exception = null;
 ExecuteWithTimeout(() =>
 {
 try
 {
 _host.Start();
 }
 catch (Exception ex)
 {
 exception = ex;
 }
 });
 if (exception != null)
 {
 _logger.Error("Exception caught while trying to execute 'StartHostCommand': " + exception.Message, exception);
 if (exception is HostServiceException)
 {
 MessageBoxFacility.ServiceControlError((HostServiceException)exception);
 }
 else
 {
 throw exception;
 }
 }
}

Here, _host.Start() is a call to the model, which in turn uses a System.ServiceProcess.ServiceController to start the Host. My logging is showing such calls time out quite often, after 3000ms, but then said logging goes on to sow that the Host stopped anyway, as expected. I would like to avoid the kludgy looking ExecuteWithTimeout and rather use await, but that would involved using some async void methods, which I have been advised to avoid.

The code I have shown here is still quite experimental, and I have little experience controlling long running tasks so many steps away from the UI, so please ignore smaller discrepancies etc.

svick
24.5k4 gold badges53 silver badges89 bronze badges
asked Mar 14, 2015 at 7:58
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

async void methods should generally be avoided, yes, but this looks like it's one of the cases where they make sense.


var frame = new StackFrame(1);
var methodName = frame.GetMethod().Name;
_logger.Warn("'ExecuteWithTimeout' exception from: '{0}': {1}", methodName, ex.Message);

I don't see how is methodName useful here, it's always going to be Handle.


var frame = new StackFrame(1);
var methodName = frame.GetMethod().Name;
_logger.Warn("'{0}' timed out after {1} milliseconds.", methodName, timeout);

I think [CallerMemberName] would be simpler here.


throw exception;

This means that the stack trace of the exception is going to be rewritten. If you don't want to wrap the exception in another exception, you can use ExceptionDispatchInfo to preserve the stack.

answered Apr 10, 2015 at 22:14
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.