Opened 8 years ago
Last modified 7 years ago
#10174 new Bugs
call_stack not working when building asio as shared module on windows
Reported by: | Owned by: | chris_kohlhoff | |
---|---|---|---|
Milestone: | To Be Determined | Component: | asio |
Version: | Boost 1.55.0 | Severity: | Problem |
Keywords: | windows dispatch | Cc: |
Description
The call_stack is not working when building asio as shared module on windows.
Also, the call_stack doesn't work on windows when using asio as header only and loading a dll at runtime (ie: LoadLibrary) which is also using asio as header only.
See and run the attached test case that demonstrates the problem in the following scenario:
- MSVC 2010 Express SP1 32bit (I'm guessing it's an issue on other compilers as well, but this is what I was using)
- build asio as a shared library according to instructions for "separate compilation" [ 1 ]
- build and run the test case, where all cases should say "PASS"
Now, I know the documentation says that .dispatch() *may* execute the handler right away (see discussion[ 2 ] on mailing list), so it's officially not a bug.
BUT, I noticed that "strand.running_in_this_thread()" worked. That's what clued me in that call_stack was working as expected for running_in_this_thread(), but not for dispatch().
It appears that the issue is with static storage for boost::asio::detail::callstack::top_ .
It appears that two copies of this static storage item are being referenced: one from the .exe file, and one from the .dll (both of which include the asio headers).
It looks like the reason "running_in_this_thread()" works is because it calls callstack<>::contains() from an .ipp file, which is included in the .dll so it references top_ from the .dll.
In the case of using asio as header only, not running any asio code from a .dll, the issue is non-existent. There is no confusion as to which top_ variable to use.
See the attached patch to asio/detail/impl/strand_service.hpp that fixes strand.dispatch() by calling "running_in_this_thread()". That forces the reference of top_ from the .dll instead of the reference from the .exe that's linked against the asio dll or another .dll that includes asio headers.
Possible fixes for this:
- put the definition of top_ in an .ipp file to include in the boost asio .dll so there's only one symbol for it.
- put all usages of call_stack in .ipp files (similar to the attached patch) so there's no confusion about the symbol (ugly and error prone, since it would have to be done for all use cases)
It should be noted that in order for call_stack to work across a dll boundary (ie: sharing io_service or strand between a .exe and .dll), asio *must* be built as a shared library.
Also, note that this only happens on Windows. Doing the equivalent with .so files works in Linux (maybe by chance? not sure...).
Not having this fix makes .dispatch() pretty much useless when using asio on windows with .dlls.
Attachments (3)
Change History (6)
by , 8 years ago
Attachment: | asioTest.cpp added |
---|
by , 8 years ago
Attachment: | 0001-asio_fix_strand_dispatch_win32.patch added |
---|
strand dispatch workaround
comment:2 by , 8 years ago
I have also seen related issues with 1.56.0 and 1.57.0 on Windows (mingw-w64) where boost::asio based code using strands is split across multiple implicitly-linked DLLs used by an exe, and BOOST_ASIO_HEADER_ONLY is defined for the build of all the DLLs and the exe.
The call stack code does not appear to be working correctly and strand::running_in_this_thread() is not returning the correct result depending on the code path a thread takes through the various DLLs and which instance of top_ is being consulted.
The same code (with each DLL replaced by an equivalent .so) appears to work fine on Linux.
For anyone who comes across this ticket, my workaround that appears to be okay so far (as suggested by the bug reporter above) was to:
- Use asio only with BOOST_ASIO_DYN_LINK defined (I'm using mingw, so BOOST_ASIO_SEPARATE_COMPILATION would probably be sufficient)
- create a detail/call_stack.ipp file that contained the definition of the static templatised variable top_ (moved out of call_stack.hpp)
- Add the usual pattern of #if defined (BOOST_ASIO_HEADER_ONLY) #include .../call_stack.ipp etc to the end of call_stack.hpp
- Add call_stack.ipp to boost/asio/impl/src.hpp
- #include <boost/asio/impl/src.hpp> once in a suitable source file that ends up in one DLL only, as per the usual boost separate compilation requirements.
However, this led me to look through the rest of the asio code to see if the same pattern of both declaring _and_ defining a non-const static [templatised] variable in an hpp file is used elsewhere. At first glance, the same pattern is used in detail/winsock_init.hpp, detail/win_thread.hpp, and ssl/detail/openssl_init.hpp, and possibly elsewhere.
Greater minds than I will need to decide if that code is also problematic in a multi-asio-using-DLL application on Windows and whether those definitions also need to be moved into the corresponding .ipp files for correct behaviour.
by , 7 years ago
Attachment: | boost_asio_call_stack_static_storage_win32.patch added |
---|
force call_stack static storage to shared library
comment:3 by , 7 years ago
I ended up going the route that the previous commenter went, and am attaching the patch to do so.
It's kind of a windows-only workaround, but it does also compile on linux, as long as I don't "export" the static variable, because gcc complains that it's already "inlined".
This also fixes the case of io_service dispatch. But, there's a strand dispatch case that is also addressed because strand dispatch calls into the io_service (can_dispatch() I believe).
It's nice that we don't have to worry about this on linux, but here's a nice workaround on windows when using dynamic link libraries.
asio call_stack testing