0

I`m working on implementing a get method for cache. This method will return to caller if a maximum wait time has passed(in my case 100ms for tests).

My issue is that the exception NEVER reaches the catch, after the timer triggered the event.

Please help me understand why? (I read that events are executed on the same thread, so that should`t be the issue)

public static T Get<T>(string key, int? maxMilisecondsForResponse = null)
{
        var result = default(T);

        try
        {
            // Return default if time expired
            if (maxMilisecondsForResponse.HasValue)
            {
                var timer = new System.Timers.Timer(maxMilisecondsForResponse.Value);
                timer.Elapsed += OnTimerElapsed;
                timer.AutoReset = false;
                timer.Enabled = true;   // start the timer
            }

            var externalCache = new CacheServiceClient(BindingName);

            Thread.Sleep(3000);  // just for testing
        }
        catch (Exception ex)   
        {
            // why is the exception not caught here?
        }

        return result;
}

private static void OnTimerElapsed(object source, System.Timers.ElapsedEventArgs e)
{
    throw new Exception("Timer elapsed");
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Pacurar Stefan
  • 235
  • 4
  • 9

3 Answers3

2

From MSDN

The Timer component catches and suppresses all exceptions thrown by event handlers for the Elapsed event. This behavior is subject to change in future releases of the .NET Framework.

And continues

Note, however, that this is not true of event handlers that execute asynchronously and include the await operator (in C#) or the Await operator (in Visual Basic). Exceptions thrown in these event handlers are propagated back to the calling thread.

Please take a look Exception Handling (Task Parallel Library)

An applied example below:

public class Program
{
    static void Main()
    {
        Console.WriteLine("Begin");
        Get<string>("key", 1000);
        Console.WriteLine("End");
    }

    public static T Get<T>(string key, int? maxMilisecondsForResponse = null)
    {
        var result = default(T);

        try
        {
            var task = Task.Run(async () =>
            {
                await Task.Delay(maxMilisecondsForResponse.Value);
                throw new Exception("Timer elapsed");
            });
            task.Wait();
        }
        catch (Exception ex)   
        {
            // why the exception is not catched here?
            Console.WriteLine(ex);
        }

        return result;
    }
}
Thus Spoke Nomad
  • 2,372
  • 4
  • 17
  • 34
  • 1
    While true, this is not relevant to the question (because in any case, even if exception were not supressed - it could not reach that catch block). – Evk Dec 24 '17 at 14:36
  • @Evk Yes, but also you can handle it from [AppDomain.UnhandledException](https://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx) event. Also I'm offering to use `async-await` which is more useful. – Thus Spoke Nomad Dec 24 '17 at 14:38
  • In what sense more useful? Even with async\await you won't be able to catch that exception in catch block OP wants. I mean your answer doesn't really answers the question "My issue is that the exception NEVER reach to the catch, Please help me understand why". – Evk Dec 24 '17 at 14:55
  • @Evk OK, provided an example. – Thus Spoke Nomad Dec 24 '17 at 15:06
  • Your "applied example " is really bad, has nothing to do with what I want to do – Pacurar Stefan Dec 28 '17 at 15:12
2

The timer fires on it's own thread. You can read more about it in this answer.

The answer to your question is to use async methods that can be cancelled. Then you can use a cancellation token source and do it the proper way instead of homebrewing a solution with timers.

You can find a good overview here.

For example:

cts = new CancellationTokenSource();  

cts.CancelAfter(2500);  

await Task.Delay(10000, cts.Token);

This would cancel the waiting task after 2500 (of 10000) because it took too long. Obviously you need to insert your own logic in a task instead of just waiting.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
0

The timer is being executed in the own thread but you can't catch the exception at the caller level. So, it is not a good approach to use timer in this case and you can change it by creating the Task operation.

var result = default(T);
CacheServiceClient externalCache;
if (!Task.Run(() =>
{
    externalCache = new CacheServiceClient(BindingName);
    return externalCache;
}).Wait(100))//Wait for the 100 ms to complete operation.
{
    throw new Exception("Task is not completed !");
}
// Do something
return result;
lucky
  • 12,734
  • 4
  • 24
  • 46