Boost threads: on iOS, the thread_info object is destroyed before the thread completes execution

Our project uses several libraries with the extension 1.48 on several platforms, including Windows, Mac, Android and IOS. We can consistently ensure that the iOS version of the project crashes (but not trivially, but reliably) when using iOS and from our research, we see that ~ thread_data_base is called in the thread_info thread while its thread is still running.

This seems to be due to the smart pointer reaching zero, although it is obviously still within the framework of the thread_proxy function, which creates it and runs the requested function in the stream. This, apparently, happens in different cases: the call stack is not identical between failures, although there are several variations that are common.

Just to be clear - this often requires running code that creates hundreds of threads, although there are no more than 30 at a time. I was "lucky", and he got up to run very early too, but it's rare. I created a version of the destructor that actually catches the code in the act:

in libs / thread / src / pthread / thread.cpp:

thread_data_base::~thread_data_base() { boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data(); void *void_thread_info = (void *) thread_info; void *void_this = (void *) this; // is somebody destructing the thread_data other than its own thread? // (remember that its own which should no longer point to it anyway, // because of the call to detail::set_current_thread_data(0) in thread_proxy) if (void_thread_info) { // == void_this) { __builtin_trap(); } } 

I should note that (as can be seen from the code with comments), which I previously checked to see that void_thread_info == void_this, because I was checked only in case the thread_info thread was destroyed. I also saw cases where the value returned by get_current_thread_data other than zero differs from "this", which is really strange.

Also, when I first wrote this version of the code, I wrote:

 if (((void*)thread_info) == ((void*)this)) 

and at runtime I got a very strange exception, which said something about a table of virtual functions or something like that - I don’t remember. I decided that he was trying to call "==" for this type of object and was unsatisfied with it, so I rewrote it as above, turning the conversions into invalid * as separate lines of code. This in itself is rather suspicious for me. I'm not the only one to run to rush to the compiler accusations, but ...

I should also note that when we caught this trap, we saw a destructor for ~ shared_count appearing twice in a row on the stack in the Xcode source. Very double. We tried to make out the disassembly, but could not make anything out of it.

Again - it looks like it is always the result of shared_count, which seems to belong to shared_ptr, to which thread_info belongs, which reached zero too soon.

Update: it seems that you can get into situations that reach the above trap without causing harm. After fixing the problem (see Answer), I saw how this happens, but always after thread_info-> run () completes. I don’t understand how ... but it works.

Additional Information:

I should note that boost.sh from Pete Goodliffe (and modified by others), which is commonly used to compile boost for iOS, has the following note in the header:

 : ${EXTRA_CPPFLAGS:="-DBOOST_AC_USE_PTHREADS -DBOOST_SP_USE_PTHREADS"} # The EXTRA_CPPFLAGS definition works around a thread race issue in # shared_ptr. I encountered this historically and have not verified that # the fix is no longer required. Without using the posix thread primitives # an invalid compare-and-swap ARM instruction (non-thread-safe) was used for the # shared_ptr use count causing nasty and subtle bugs. # # Should perhaps also consider/use instead: -BOOST_SP_USE_PTHREADS 

I use these flags, but to no avail.

I found the following, which is very painful - it looks like they had the same problem in std::thread:

http://llvm.org/bugs/show_bug.cgi?format=multiple&id=12730

This suggests using an alternative implementation inside boost for handheld processors, which seems to also directly solve this problem: spinlock_gcc_arm.hpp

The version included in boost 1.48 uses an outdated console. I took the updated version with boost 1.52, but it's hard for me to compile it. I get the following error: Suggested instructions must be in the IT block

I found a link to what looks like a similar use of this instruction here: https://zeromq.jira.com/browse/LIBZMQ-414

I was able to use the same idea to get the code 1.52 for compilation by changing the code as follows (I inserted the appropriate IT instruction)

 __asm__ __volatile__( "ldrex %0, [%2]; \n" "cmp %0, %1; \n" "it ne; \n" "strexne %0, %1, [%2]; \n" BOOST_SP_ARM_BARRIER : "=&r"( r ): // outputs "r"( 1 ), "r"( &v_ ): // inputs "memory", "cc" ); 

But anyway, there are ifdefs in this file that are looking for an arm architecture that is not defined that way in my environment. After I just edited the file so that only the ARM 7 code is left, the compiler complains about the definition of BOOST_SP_ARM_BARRIER:

In the file included from. / boost / smart _ptr / detail / spinlock.hpp: 35: ./boost/smart_ptr/detail/spinlock_gcc_arm.hpp:39:13: error: the command requires the CPU function not to be enabled at the moment BOOST_SP_ARM_BARRIER: ^. / boost / smart _ptr / detail / spinlock_gcc_arm.hpp: 13: 32: note: extended from the macro "BOOST_SP_ARM_BARRIER"

 # define BOOST_SP_ARM_BARRIER "dmb" 

Any ideas?

+8
c ++ multithreading boost ios
source share
2 answers

Figured it out. It turns out that the boost.sh script I mentioned in the question chose the wrong boost flag to solve this problem - instead of BOOST_SP_USE_PTHREADS (and the other flag there with it, BOOST_AC_USE_PTHREADS ) it turns out what is required for IOS BOOST_SP_USE_SPINLOCK . This ultimately gives an almost identical solution used in the std :: thread problem mentioned in the question.

If you are compiling for any modern iOS device that uses ARM 7 but using an older upgrade (we use 1.48), you need to copy the spinlock_gcc_arm.hpp file from a later upgrade (for example, 1.52). This file has # ifdef'd for different arm architectures, but it's not clear to me that the definitions it is looking for are defined in the IOS compilation environment using a script. Thus, you can either edit the file (violent, but effective), or spend some time figuring out how to make it neat and proper.

In any case, you may need to insert an additional assembly instruction, which I made above in the question: "it ne; \ n" I have not returned yet to see if I can delete it now when I have a problem with the compilation environment.

However, we are not done yet. The code used in boost for this option includes, as described, ARM assembly language instructions. ARM chips support two sets of instructions that cannot be mixed in this module (they are not sure about the area, but, obviously, file by file is acceptable detail when compiling). The instructions used in boost for this lock include non-Thumb instructions, but iOS by default uses the Thumb instruction set. The boost code, given the problem of the command set, checks that you have arm turned on, but not the thumb , but by default in iOS, the thumb is turned on.

Getting the compiler to generate a small ARM code depends on which compiler you use on iOS - Apple LLVM or LLVM GCC. GCC is deprecated, and Apple LLVM is used by default when using Xcode.

For the standard Clang + Apple LLVM 4.1, you need to compile using the -mno-thumb flag. Also, any files in your iOS application that use any part of boost that uses smart pointers should also be compiled using -mno-thumb.

To compile boost, I think you can just add -mno-thumb to EXTRA_CPP_FLAGS in the script. (I myself changed user-config.jam during the experiments and have not returned to cleanup yet.)

For your application in Xcode, you need to select your purpose, then go to the "Phase Assembly" tab and select "Compile Sources." You can add flag compilations there, so for each corresponding file (including boost) add the -mno-thumb flag. You can do this directly in project.pbxproj, where each file has

 settings = { COMPILER_FLAGS = ""; }; 

you just change it to

 settings = { COMPILER_FLAGS = "-mno-thumb"; }; 

But there is a little more. You also need to modify the darwin.jam file in the tools / build / v2 / tools directory. There is code in boost 1.48 that says:

  case arm : { options = -arch armv6; } 

It should be changed to

  case arm : { options = -arch armv7 ; } 

Finally, in the boost.sh script, in the writeBjamUserConfig() function, you must remove the references to -arch armv6.

If someone knows how to do this a little more broadly and cleanly, I’m sure that we will all win. This is where I have come to at the moment, and I hope this helps other iOS users increase the flow of users. Hope the various options on boost.sh iOS script will be updated there. I plan to add some more links to this answer later.

Update:. Excellent article describing the problem at the processor level,
take a look here: http://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu

Enjoy it!

+8
source share

I use boost.asio, boost.thread, boost.smart_ptr, etc. on the iOS platform, the application always crashes when launched in release mode, which causes a cigar signal. Emergency Call Stack:

 __stack_chk_fail boost::asio::detail::completion_handle boost::asio::detail::task_ios_service_operation::complete boost::asio::detail::task_io_service::do_run_one boost::asio::detail::task_ios_service::run boost::asio::io_service::run ![when create a asio work with creating new thread and io_service][1] 

While trying to solve the problem, I found the following articles:

 [boost-thread-threads-not-starting-on-the-iphone-ipad-in-release-build][2] [The issue of spin_lock and thumb on iOS][3] 

Then I try to add -mno-thumb to my project compilation flag, and the issue that occurred in release mode has disappeared.

However, a new error appears: EXC_ARM_DA_ALIGN , which crashed into where I am trying to convert network data to host-endian.

As stated in [this article] [4], ARM instructions are strict so that memory data is aligned.

And follow the article [Exc_arm_da_align][5] , I will fix it using memcpy to convert the data, instead of directly converting from a pointer.

  [1]: http://i.stack.imgur.com/3ijF4.png [2]: http://stackoverflow.com/questions/4201262/boost-thread-threads-not-starting-on-the-iphone-ipad-in-release-builds/4245821#4245821 [3]: http://groups.google.com/group/boost-list/browse_thread/thread/7dc1e80659182ab3 [4]: https://brewx.qualcomm.com/bws/content/gi/common/appseng/en/knowledgebase/docs/kb95.html [5]: http://www.cnblogs.com/unionfind/archive/2013/02/25/2932262.html 
0
source share

All Articles