3

If I execute the following code on an Android device with no network connectivity, the await call will never complete nor will any exceptions be thrown by ConnectAsync even if Close or Dispose, etc. are invoked on the socket object (the catch handlers in this sample will never be reached, in other words):

            CancellationToken token = _tokenSource.Token;
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                using (token.Register(OnCancel))
                {
                    await _socket.ConnectAsync("176.222.222.222", 1234);
                }
            }
            catch(ObjectDisposedException)
            {
            }
            catch (Exception)
            {
            }

However, this is not the behavior on Windows or Linux: on those platforms, ConnectAsync will immediately toss a "System.Net.Sockets.SocketException (10065): A socket operation was attempted to an unreachable host." exception if they're disconnected from any network, and if I choose to call either Close or Dispose on the socket object whilst it's attempting to connect when network is available, I'll get an exception about the I/O attempt being aborted. This is all expected behavior.

Is there any reason short of a bug in Mono that'd cause this Android-specific behavior? I have a working example here: https://github.com/kwende/MqttNetExampleIssues.

Edit:

It's possible my example isn't clear enough. Here is an example block of code using a timeout that's much simpler to read. The effect is the same, but perhaps this is a bit clearer. In the event of a timeout, the Register call should result in OnCancel being called, which should Close/Dispose of the socket. Doing so does nothing as far as continuation is concerned: the await never returns. I'm limited to button click events to show this off as I'm in Xamarin. The full code is available here: https://github.com/kwende/MqttNetExampleIssues/blob/main/Android/AndroidHang2/AndroidHang2/AndroidHang2/MainPage.xaml.cs. It can be downloaded and ran to show off the issue:

    private void OnCancel()
    {
        _socket?.Close(); 
        _socket?.Dispose();

        _socket = null; 
    }

    private async void Button_Clicked(object sender, EventArgs e)
    {
        await Task.Run(async () =>
        {
            using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
            {
                using(cancellationTokenSource.Token.Register(OnCancel))
                {
                    _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                    try
                    {
                        await _socket.ConnectAsync("172.222.222.222", 1234);
                    }
                    catch(Exception ex)
                    {
                        // if your network is off, the timeout will occur, OnCancel will be called,
                        // close and dispose will be called, but this will never get hit. 
                    }
                }
            }
        }); 
    }
Ben
  • 314
  • 2
  • 11
  • Perhaps not directly an answer to your question, but consider working around the problem using a timeout: https://stackoverflow.com/q/25500649/199364 – ToolmakerSteve May 26 '22 at 20:21
  • Thanks. The problem is that there seems to be an actual hang, so it's a resource leak. Calling dispose/close on the socket doesn't actually seem to release all the resources. So, any sort of timeout/cancellation just leaves the await hanging. – Ben May 26 '22 at 20:24
  • Correct. That's the underlying issue. My GitHub repo shows that off. It _never returns_ and _no exception_ is thrown from ConnectAsync. – Ben May 26 '22 at 20:36
  • @ToolmakerSteve, it's worth noting: the synchronous version of Connect will fail as expected (exceptions will get tossed/caught), so it's strictly the async version that fails in the way I describe. – Ben May 26 '22 at 20:38
  • Maybe I'm blind, but nowhere do I see you **set a timeout** on that cancellationtoken. IMPORTANT: Not referring to any timeout on http, but on the cancellationtoken itself. As seen in answer on the SO post I linked. – ToolmakerSteve May 26 '22 at 20:42
  • You aren't blind, but it's part of running the app. There's a button click event that calls .Cancel() explicitly on the token source. https://github.com/kwende/MqttNetExampleIssues/blob/5f21c585998a9489abce07956aaf8339da4f3fcb/Android/AndroidForeverHang/AndroidForeverHang/MainPage.xaml.cs#L139 If I tap that whilst the connect is hanging, no exception is ever thrown. – Ben May 26 '22 at 20:49
  • @ToolmakerSteve I updated my issue with a, hopefully, clearer example of what I'm encountering. Hopefully this makes it easier to understand what it is I'm reporting. Thanks. – Ben May 26 '22 at 21:31
  • 1
    Most comments removed above. Summary of private chat: Confirmed UI thread not blocked. Moved code to background thread just in case. Explicit cancel of task does not result in task cancelled exception, which it should. OP found a related open issue on github. OP created a new issue for this case. – ToolmakerSteve May 27 '22 at 00:41

0 Answers0