Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't catch Boost.Asio exceptions inside coroutines #1559

Open
rkfg opened this issue Oct 28, 2016 · 19 comments
Open

Can't catch Boost.Asio exceptions inside coroutines #1559

rkfg opened this issue Oct 28, 2016 · 19 comments

Comments

@rkfg
Copy link

rkfg commented Oct 28, 2016

I've posted this to Boost and Boost.Asio mailing lists and they said it could be compiler's fault. So I repost here hoping someone would shed some light on the issue.

I have this little example of Boost.Asio code:

#include <iostream>
#include <stdexcept>
#define BOOST_COROUTINES_NO_DEPRECATION_WARNING
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>

using namespace std;
using namespace boost::asio;
using namespace boost::asio::ip;

io_service iosrv;

void test(yield_context yield) {
    tcp::socket m_socket(iosrv);
    try {
        cout << "1" << endl;
        tcp::resolver resolver(iosrv);
        cout << "2" << endl;
        auto endpoints = resolver.async_resolve(tcp::resolver::query("nonexistingdomain.com", "1111"), yield);
        cout << "3" << endl;
        async_connect(m_socket, endpoints, yield);
        cout << "4" << endl;
    } catch (const exception& e) {
        cout << "Caught " << e.what() << endl;
    } catch (...) {
        cout << "Caught whatever" << endl;
    }

}

int main(int argc, char* argv[]) {
    boost::asio::io_service::strand s(iosrv);
    spawn(s, test);
    iosrv.run();
}

It works as expected on Linux producing
1
2
Caught Host not found (authoritative)

but on Windows it crashes with:
1
2
terminate called after throwing an instance of 'boost::system::system_error' what(): Unknown error

Tested on Windows Server 2008, Windows 7 and Wine, everything is the same. I'm compiling on Linux for Windows with MXE on GCC 4.9.4, Boost version is 1.60.0. I'm really stuck at this point as nothing in Google looks a valid solution for the case and I already have a working application on Linux but it crashes on any network error on Windows.

I also tried to disable Asio's threads with #define BOOST_ASIO_DISABLE_THREADS but that resulted in Asio's inability to start a background thread for asynchronous name resolution (that exception is caught fine).

@starius
Copy link
Member

starius commented Oct 28, 2016

I can reproduce your error.

It can be related to exceptions handling. 32 bit and 64 bit targets use different exceptions handling. To debug this, can you build your application in 64-bit mode (target x86_64-w64-mingw32.static) and check how it works, please?

There is a feature request to support non-standard exception handlings.

See also gcc configure flags to change this. I am trying to build with --enable-dw2-exceptions.

@starius
Copy link
Member

starius commented Oct 28, 2016

--enable-dw2-exceptions did not help (same error, tested only in wine).

@starius
Copy link
Member

starius commented Oct 29, 2016

@starius
Copy link
Member

starius commented Oct 29, 2016

Same error with boost 1.62

@starius
Copy link
Member

starius commented Oct 29, 2016

By the way, an example of throwing from a coroutine works.

@rkfg
Copy link
Author

rkfg commented Oct 29, 2016

Thanks, I also tried a lot of combinations of thread models, exceptions, 32/64 bits, turned GCC optimizations on and off etc. In the end, nothing helped. And yes, regular exceptions (std::runtime_error, for instance) can be caught just fine, only asio's ones fall through. So it's not like it is completely broken but something between asio and MinGW just doesn't work right.

@rkfg
Copy link
Author

rkfg commented Oct 31, 2016

Forgot to mention, there's a subtle discrepancy between the program behavior on Linux and on Windows. When ran under gdb on Linux, the test program only spawns one thread it seems. On Windows it spawns three. I suspect that's happening because Boost is compiled as multithreaded in MXE (the exception is thrown in another thread and can't be caught in the handler set up in the main thread) but I was unable to build it singlethreaded, got some obscure error messages. Is there a way to build singlethreaded Boost in MXE?

@rkfg
Copy link
Author

rkfg commented Nov 1, 2016

Ok, for now I've come up with a simple yet effective solution, it's to use yield[ec] form that doesn't throw. After each async call I may use

if (ec) throw boost::system::system_error(ec);

which is effectively what's used in impl/spawn.hpp inside asio. For a reason unknown to me this works just as expected. I checked the thread id from which the asio exception is thrown and compared it to the main thread id and they seem to be equal. Also the terminate message says "Unknown error" which is not the same what() the actual exception should contain. Only happens in Wine, on Windows it says "Unknown host" or something alike.

@starius
Copy link
Member

starius commented Nov 1, 2016

Is there a way to build singlethreaded Boost in MXE?

I tried this:

diff --git a/src/boost.mk b/src/boost.mk
index 71e38fa..ed3c804 100644
--- a/src/boost.mk
+++ b/src/boost.mk
@@ -41,8 +41,7 @@ define $(PKG)_BUILD
         binary-format=pe \
         link=$(if $(BUILD_STATIC),static,shared) \
         target-os=windows \
-        threadapi=win32 \
-        threading=multi \
+        threading=single \
         variant=release \
         toolset=gcc-mxe \
         cxxflags=$(if $(findstring posix,$(MXE_GCC_THREADS)),-std=gnu++11,-std=gnu++98) \
@@ -63,21 +62,4 @@ define $(PKG)_BUILD

     # setup cmake toolchain
     echo 'set(Boost_THREADAPI "win32")' > '$(CMAKE_TOOLCHAIN_DIR)/$(PKG).cmake'
-
-    '$(TARGET)-g++' \
-        -W -Wall -Werror -ansi -U__STRICT_ANSI__ -pedantic \
-        '$(PWD)/src/$(PKG)-test.cpp' -o '$(PREFIX)/$(TARGET)/bin/test-boost.exe' \
-        -DBOOST_THREAD_USE_LIB \
-        -lboost_serialization-mt \
-        -lboost_thread_win32-mt \
-        -lboost_system-mt \
-        -lboost_chrono-mt
-
-    # test cmake
-    mkdir '$(1).test-cmake'
-    cd '$(1).test-cmake' && '$(TARGET)-cmake' \
-        -DPKG=$(PKG) \
-        -DPKG_VERSION=$($(PKG)_VERSION) \
-        '$(PWD)/src/cmake/test'
-    $(MAKE) -C '$(1).test-cmake' -j 1 install
 endef

And compiled with the following command:

$ ./usr/bin/i686-w64-mingw32.static-g++ -std=c++11 1559.cpp -o 1559.exe -lboost_coroutine -lboost_context-mt -lboost_system -lws2_32

Note that Boost.Context is still in multi-threaded mode.

But it fails with the same error (tested in wine):

1
2
terminate called after throwing an instance of 'boost::system::system_error'
  what():  Unknown error

abnormal program termination

@rkfg
Copy link
Author

rkfg commented Nov 1, 2016

Then that's not it, thanks for testing and confirming. My workaround with yield[ec] worked good enough so I'll just stick to it until the real culprit is found.

@tonytheodore
Copy link
Member

--enable-dw2-exceptions

@starius, the way to enable dw2 is to disable sjlj:

--disable-sjlj-exceptions

There's a comment that it works with posix-dwarf-boost 1.62. It also seems that 1.62 fixed the issue with masm being the only fully supported assembler StephanTLavavej/mingw-distro@b97f05a.

With current 1.60 and enable exceptions patch

make boost MXE_TARGETS=i686-w64-mingw32.static.posix.dw2
...
i686-w64-mingw32.static.posix.dw2-g++ src/boost-test-asio.cpp -o usr/i686-w64-mingw32.static.posix.dw2/bin/test-boost-asio.exe -std=c++14 -lboost_system-mt -lws2_32 -lboost_coroutine-mt -lboost_context-mt

running on Win7

H:\dev\mxe\usr\i686-w64-mingw32.static.posix.dw2\bin>test-boost-asio.exe
1
2
Caught No such host is known

Testing i686-w64-mingw32.static.posix and i686-w64-mingw32.static now.

@rkfg
Copy link
Author

rkfg commented Feb 4, 2017

Oh, good to know, thanks! Although I've already changed my code to use the mentioned workaround, I'd much like to revert it back so it's easier to read and use.

@tonytheodore
Copy link
Member

H:\dev\mxe\usr\i686-w64-mingw32.static\bin>test-boost-asio.exe
1
2
terminate called after throwing an instance of 'boost::system::system_error'
  what():  No such host is known

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
H:\dev\mxe\usr\i686-w64-mingw32.static.posix\bin>test-boost-asio.exe
1
2
terminate called after throwing an instance of 'boost::system::system_error'
  what():  No such host is known

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
H:\dev\mxe\usr\i686-w64-mingw32.static.dw2\bin>test-boost-asio.exe
1
2
Caught No such host is known

So it seems that dw2 is the key, makes sense since boost is configured with threadapi=win32.

With #1664 you can do

make boost MXE_TARGETS=i686-w64-mingw32.static.dw2

@tonytheodore
Copy link
Member

@starius wrote #1559 (comment):

Note that Boost.Context is still in multi-threaded mode.

same is true for thread_win32 and atomic - the libs always have the -mt suffix.

@rkfg #1664 is merged now so you can use the MXE_TARGETS=i686-w64-mingw32.static.dw2 style target names.

@rkfg
Copy link
Author

rkfg commented Feb 12, 2017

@tonytheodore Thanks a lot, will try it when I can (currently working on another project)!

@rkfg
Copy link
Author

rkfg commented Feb 25, 2017

I tested the dw2 variant and got this on Wine:

1
2
Caught Unknown error

On a real Windows installation it reports an unknown host so it's probably not an issue. Both Wine and Windows don't get terminated because of an uncaught exception so on the 32-bit side it seems to be fixed.

However, x86_64 build (with dw2 exceptions) crashes at startup on both Wine and Windows. Is this not supported at the moment? Because I'd like to be able to build both 32 and 64 bit binaries with Asio exceptions working.

@tonytheodore
Copy link
Member

There's no dw2 exceptions on x86_64, there's seh (default) and sjlj. Both crash on launch without producing any output.

@tonytheodore tonytheodore reopened this Feb 25, 2017
@tonytheodore
Copy link
Member

Crashing in different ways with v1.63:

H:\dev\mxe>test-boost-asio-64-seh.exe
Assertion failed!

Program: H:\dev\mxe\test-boost-asio-64-seh.exe
File: /Users/tonyt/dev/mxe/usr/x86_64-w64-mingw32.static/include/boost/coroutine/detail/push_coroutine_object.hpp, Line 284

Expression: ! base_t::unwind_requested()

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
H:\dev\mxe>test-boost-asio-64-sjlj.exe
1
2
# exits with crash dialog

@rkfg
Copy link
Author

rkfg commented Dec 22, 2018

If anyone's still interested, I've updated the test program to be compiled with the recent Boost 1.67 (they added a template parameter to strand and the default strand is in io_service now).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants