Delegates and exceptions

P

Pietje de kort

Hello all,

I want to implement Timeout behaviour in a class of mine. When a
method is called it should timeout after a few seconds. To do this
I've built a
System.Threading.Timer that calls a delegate method after x milli's,
the called
delegate throws an exception indicating timeout.
The big problem is my code will not catch the thrown exception because
it
is generated outside of normal program control. Is there any way to
catch the
exception thrown (other than hooking the UncaughtException event)?

Best regards Wouter

using System.Threading;

public class Test
{
public Test(){};

public DoTimeoutAction()
{
Timer timer = new Timer(new TimerCallback(Timedout),null,
1000,Timeout.Infinite);
Console.WriteLine("Doing something");
Thread.Sleep(5000); // will cause timeout to occur
Console.WriteLine("Done doing something");
timer.Dispose(); // stop timer
}

private void Timedout(object state)
{
throw new ApplicationException("Timeout occured.");
}

static void Main()
{
Test t = new Test();
try
{
t.DoTimeoutAction();
}catch(ApplicationException e)
{
Console.WriteLine(e.Message);
}
Console.WriteLine("Done!");
Console.ReadLine();
}
}
 
N

Nicholas Paldino [.NET/C# MVP]

Pietje,

You have to go about this the opposite way actually. The first thing
you need to do is indicate somewhere if the operation completed or not. You
can use a boolean flag for this. Once you have that, you should expose it
as a property (public or private, it is up to you). You need to place a
lock section around the access of this variable.

After that, in your method, you call your operation asynchronously in
another thread (through the thread pool or a separate thread). In the code
that is performing the work, when complete, set the flag to true.

In the code that calls out to the other thread, after the asynchronous
call is made, you can call the static Sleep method to sleep for the amount
of time you want to wait. If the flag is not set when execution resumes,
you can throw your exception.

Of course, this means that if your operation completes, you have to wait
the same amount of time. In order to get around this, replace the boolean
flag with a WaitHandle, such as an event. In your operation, instead of
setting the boolean flag, set the Event. Also, instead of having the thread
put to sleep, you wait on the handle. Before you wait on the handle, you
set your timer to call back a method and set the event when it calls back.
The flag comes in useful here in the timer method, where you will set it to
true. This way, when the call to WaitOne on the Event is complete, you will
know if it timed out or not.

Hope this helps.
 
F

Fred Mellender

I believe the problem is that the timer and its timeout function run on a
separate thread from the one that started the timer up (see the help page on
Threading.Timer). Because there is no way to specify an exception handler
on that (system created) thread, you cannot catch an exception thrown in the
timeout thread. If possible you might try to place your exception-catch
logic in the timeout function itself.

Perhaps another way:

Have a separate thread ("controller") start the "calculator" on its on
thread. Then in controller, do a
calculator.Join(TimeSpan). In statements following the Join, the
controller can check to see that the calculator thread has completed
normally (via Thread.IsAlive, or a bool set by calculator itself). If not,
controller can put out a message, kill calculator, or do another join to
give it more time.
 
P

Pietje de kort

Hi Nicholas,

that's exactly what I found on some MSDN site!
Here's my example code. The actual work is done
in the DoAction method which signals it's done.

Best regards, Wouter van Vugt (not pietje ;) )

using System;
using System.Collections;

using System.IO;
using System.Threading;

namespace TestApp
{
public class TimeoutTest
{
private AutoResetEvent autoEvent;
private int timeout;

public TimeoutTest(int timeout)
{
this.timeout = timeout;
this.autoEvent = new AutoResetEvent(false);
}

public void DoTimeoutAction()
{
Console.WriteLine("Delegating request to work item.");
ThreadPool.QueueUserWorkItem(new WaitCallback(DoAction),null);
Console.WriteLine("Waiting for work item to complete.");
int index = WaitHandle.WaitAny(new AutoResetEvent[]{autoEvent},timeout,false);

if (index == WaitHandle.WaitTimeout)
throw new ApplicationException("Timedout");
Console.WriteLine("Work item completed!");
}

private void DoAction(object state)
{
Console.WriteLine("DL Doing Work...");
Thread.Sleep(5000);
Console.WriteLine("DL Done doing work.");
autoEvent.Set();
}

static void Main()
{
try
{
TimeoutTest t = new TimeoutTest(10000);
t.DoTimeoutAction();
}
catch(ApplicationException e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top