Fixed C # exception in try / catch block, freeze detection

Purpose: To provide a timeout to external code that is unchecked. If my application causes the other side to freeze, I want to know it immediately. Let's say it's a very complex unit test. If a test client used my app and returned it in 500ms - it passed, if not - it failed.

So yes, I'm not going to be gentle on the client, this is a crash test site. I wait 500ms, then I don't care what else the client is waiting for, I want to roll its exception immediately, but THEN SEND THIS as a test result.

Perhaps as one of the many test results.

So now, here's the code:

    Console.Write("Testing");
    try {
        for (int j = 0; j < 3; j++) {
            Console.Write(".");
            for (int i = 0; i < 256; i++) {
                using (var cts = new CancellationTokenSource(500)) {
                    var token = cts.Token;
                    token.Register(() => throw new TimeoutException("FREEZE DETECTED"));
                    clients.Add(LDSCheckAsync().Result);
                }
            }
            clients.ForEach(i => i.Dispose());
            clients.Clear();
        }
        Console.WriteLine("OK.");
    }
    catch {
        clients.ForEach(i => i?.Dispose());
        Console.WriteLine("FAIL!");
    }

      

I have developed a test for failure. But I am not getting the "FAIL" message, I am getting the INCORRECT EXCEPTION.

Why and how to do it right. Before you even try to write a comment on it, please really think that I can't use IfCancellationRequested

or something similar here. The function under test would normally exit immediately, but since some network errors may delay by 30 seconds. It is completely unknown to me when it occurs and even more unknown why. The test I am doing should help me diagnose it. THE FREEZER CANNOT BE CANCELED. I know it. I want to convert the freeze to a nice catchable exception and then catch it and then log it. But when try

/ catch

doesn't work as expected (yes, even outside VS), I'm helpless.

Since he answered ...

I made a small handy tool to make my life easier the next time I need to check the freeze code:

using System.Threading;
using System.Threading.Tasks;

namespace Woof.DebugEx {

    public class FreezeTest {

        private readonly Action Subject;
        private int Timeout;
        private bool IsDone;

        public FreezeTest(Action subject) => Subject = subject;

        public void Test(int timeout) {
            Timeout = timeout;
            var tested = new Task(Tested, null, TaskCreationOptions.LongRunning);
            var watchdog = new Task(WatchDog, null, TaskCreationOptions.LongRunning);
            tested.Start();
            watchdog.Start();
            Task.WaitAny(tested, watchdog);
            if (!IsDone) throw new TimeoutException("FREEZE DETECTED.");
        }

        private void Tested(object state) {
            Subject();
            IsDone = true;
        }

        private void WatchDog(object state) => Thread.Sleep(Timeout);

    }

}

      

Now my test snippet looks like this:

    Console.Write("Testing");
    try {
        for (int j = 0; j < 8; j++) {
            Console.Write(".");
            for (int i = 0; i < 16; i++) {
                new FreezeTest(() => {
                    clients.Add(LDSCheckAsync().Result);
                }).Test(100);
            }
            clients.ForEach(i => i.Close());
            clients.Clear();
            Thread.Sleep(1000);
        }
        Console.WriteLine("OK.");
    }
    catch {
        clients.ForEach(i => i?.Dispose());
        Console.WriteLine("FAIL!");
    }

      

+3


source to share


1 answer


You can do it like this:

for (int i = 0; i < 256; i++) {
    var check = LDSCheckAsync();
    var idx = Task.WaitAny(Task.Delay(500), check);
    if (idx == 0) {
        throw new TimeoutException("FREEZE DETECTED");
    }
    else if (idx == 1) {
         var result = check.Result;
         // do something with it
    }
}

      

The reason your current approach fails is because the continuation CancellationToken.Register

is being executed on a different thread, and therefore your catch block cannot catch it (and it cannot run on the current thread, since it is blocked by the call Result

).



Note that Task.WaitAny

it will not throw any exceptions even if your task fails, so you should expect an exception to be thrown when checking the result. Also keep in mind that after you have selected the timeout exception, your main task is still working and may potentially not work in the future, which could crash your application (depending on the version of the framework I remember). So it's a good idea to register a continuation for your main task and try the exception there, even if you don't care about it, the result is at this point. Something like that:

if (idx == 0) {
    check.ContinueWith(t =>
    {
         // observe, maybe do something like logging it
         var e = t.Exception;
    }, TaskContinuationOptions.OnlyOnFaulted);
    throw new TimeoutException("FREEZE DETECTED");
}

      

+8


source







All Articles