1

Say we're using Socket.BeginReceive and passing in a callback method:

    var state = new StateObject() { workSocket = socket };
    var ret = socket.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(BeginReceiveCallback), state);

...

    private void BeginReceiveCallback(IAsyncResult ar)
    {
        StateObject state = (StateObject) ar.AsyncState;
        int bytes = state.workSocket.EndReceive(ar); //workSocket is the Socket receiving
        ...
    }

If an exception is encountered in the callback and not handled there - where will it go? How/can I catch it?

Mr. Boy
  • 60,845
  • 93
  • 320
  • 589
  • This is a normal method. The exception will be thrown by `EndReceive` and will be propagated to `BeginReceiveCallback`'s caller, surely...? – canton7 Feb 10 '20 at 10:52
  • 2
    Instead of using the `BeginReceive` and `EndReceive` methods, you could instead use [TaskFactory.FromAsync](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskfactory.fromasync?redirectedfrom=MSDN&view=netframework-4.8#overloads) to convert it into a `Task` you can await – MindSwipe Feb 10 '20 at 10:55
  • @canton7 the callback is called but the calling method doesn't block waiting for it... so you can't put a `try{ BeginReceive(...)` block – Mr. Boy Feb 10 '20 at 10:55
  • 1
    @MindSwipe I could but that's not the question - I might be working on an existing codebase or just curious – Mr. Boy Feb 10 '20 at 10:55
  • 1
    @Mr.Boy `Socket.EndReceive` *does* block? From the Remarks, "*The EndReceive method will block until data is available*". Maybe you need more context, i.e. where `BeginReceiveCallback` is called from – canton7 Feb 10 '20 at 10:56
  • The exceptions will be thrown in the callback method. For example when receiving data from a socket and it’s closed unexpectedly it will throw in the callback. There doesn’t need to be any data available. – Sami Kuhmonen Feb 10 '20 at 10:56
  • Perhaps here you can find an answer https://stackoverflow.com/questions/5383310/catch-an-exception-thrown-by-an-async-void-method – Dani Toker Feb 10 '20 at 10:57
  • I know, that's why I posted it as a comment, not as an answer. I'm actually quite interested in the answer to this, I left the comment to future viewers of this Q&A who could be searching for something akin to that. – MindSwipe Feb 10 '20 at 10:57
  • @canton7 yes but `BeginReceive` doesn't. My callback is called when data is ready to receive... `BeginReceiveCallback` is called from some thread pool thread not my normal callstack – Mr. Boy Feb 10 '20 at 10:57
  • I expanded my code to make it clearer, it is called as a callback from `BeginReceive` not directly my me – Mr. Boy Feb 10 '20 at 11:05
  • The answer depends on the exception and what you plan on doing with it however, you'll have to catch the expected exceptions within the callback method and them propagate them elsewhere whether that be another callback etc. The reason for this is that the callback is async and can potentially be run under a new thread thus the exception needs to be caught on the same thread is is thrown and then from there you can send it where ever you like. – Kieran Devlin Feb 10 '20 at 11:13

1 Answers1

2

Doing a bit of reading, I believe this is the answer

The Exceptions get thrown when you call EndReceive. The Begin.../End... pair of methods work like this:

  1. Begin gets called, and returns immediately
  2. The callback gets started in a separate Thread by the runtime
  3. Inside the callback, the actual work gets done blocking the Thread. This work is done by invoking the End... method

So, the End... method is actually doing the work. So, if an exception gets thrown, you can catch it there.

Here is some code demonstrating this with comments. If you want to just try it out here is a Dotnetfiddle with this code edited to work on Dotnetfiddle:

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

namespace BeginEndInvokeTest
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            // This is just here for setup
            var caller = new AsyncDemo.AsyncMethodCaller(AsyncDemo.Test);

            // This is your 'socket.BeginReceive' call
            var ar = caller.BeginInvoke(3000, AsyncCallback, caller);

            // Wait so the program doesn't exit prematurely
            await Task.Delay(5000);
        }

        static void AsyncCallback(IAsyncResult ar)
        {
            var caller = (AsyncDemo.AsyncMethodCaller) ar.AsyncState;
            try
            {
                // If our exception wouldn't be thrown here (which is impossible), 
                // the program would print "No exception was thrown"
                caller.EndInvoke(ar);
                Console.WriteLine("No exception was thrown");
            }
            catch (Exception)
            {
                Console.WriteLine("Exception encountered");
            }
        }
    }

    public class AsyncDemo
    {
        public static string Test(int callDuration)
        {
            // Simply write something to the console, simulate work 
            // and throw an exception
            Console.WriteLine("Test method begins");
            Thread.Sleep(callDuration);
            throw new Exception("Testing");
        }

        public delegate string AsyncMethodCaller(int callDuration);
    }
}

So in short, you can only catch the Exceptions at the End... call, nowhere else.


Edit to address where does the exception go when it isn't caught.

Honestly, I have no idea where it goes. Further testing and trial n' error gave me nothing. It seems like the whole runtime just crashes. When I didn't catch the exception I get a console out with a stack trace that shows where the exception was thrown (inside the Test method, as expected), alongside something I've never seen before: Unhandled Exception: System.Exception: Testing.
There is also a second stack trace saying :

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper(Message reqMsg, Boolean bProxyCase)
   at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(Object NotUsed, MessageData& msgData)

...

So, yeah, it seems like the runtime crashes when you don't catch it.

Source: I cobbled this answer together using this Microsoft API documentation. Some further info can be found here as Calling synchronous methods asynchronously.

MindSwipe
  • 7,193
  • 24
  • 47
  • The obvious follow-up is then "what happens if you do not catch an exception in this case, and why?" - oh you edited to address this as I was writing the comment! – Mr. Boy Feb 10 '20 at 11:48
  • @Mr.Boy I update my answer a literal split second after you posted your comment, sorry bout that. Although, I don't know why... – MindSwipe Feb 10 '20 at 11:49