I did some research on why my QT app crashes at the destructor of std::thread on Android 10 devices at the user side with the following call stack:
#00 /apex/com.android.runtime/lib64/bionic/libc.so (abort+160) #01 /system/lib64/libc++.so (abort_message+232) #02 /system/lib64/libc++.so (demangling_terminate_handler()+44) #03 /system/lib64/libc++.so (std::__terminate(void (*)())+12) #04 /system/lib64/libc++.so (std::terminate()+52) #05 /system/lib64/libc++.so (std::__1::thread::~thread()+20) #06 /apex/com.android.runtime/lib64/bionic/libc.so (__cxa_finalize+212) #07 /apex/com.android.runtime/lib64/bionic/libc.so (exit+24) #08 /data/app/com.domain.myapp-Rs_sm5VrLR1Jj8QW6oYByA==/lib/arm64/libplugins_platforms_qtforandroid_arm64-v8a.so
and figured out that its likely because std::thread destructor is being invoked while the thread is still joinable at some point of the application execution (thanks to G. M. on stackoverflow.com).
I was unable to reproduce this crash in my app on my devices and emulators, but at least I created a small test that demonstrates how std::thread destructor calls std::terminate:
#include "Awl/Testing/TestChain.h" #include <thread> #include <chrono> AWT_TEST(ThreadAbort) { using namespace std::chrono_literals; std::thread * p_t = new std::thread([]() { std::this_thread::sleep_for(5s); }); std::this_thread::sleep_for(1s); delete p_t; }
The result on Android 64 bit is:
#00 /system/lib64/libc.so (abort+104) #01 /data/app/com.domain.MyApp-l8euiXwxCtbbVZ_8HvQcMw==/lib/arm64/libc++_shared.so #02 /data/app/com.domain.MyApp-l8euiXwxCtbbVZ_8HvQcMw==/lib/arm64/libc++_shared.so #03 /data/app/com.domain.MyApp-l8euiXwxCtbbVZ_8HvQcMw==/lib/arm64/libc++_shared.so #04 /data/app/com.domain.MyApp-l8euiXwxCtbbVZ_8HvQcMw==/lib/arm64/libc++_shared.so (_ZSt9terminatev+52) #05 /data/app/com.domain.MyApp-l8euiXwxCtbbVZ_8HvQcMw==/lib/arm64/libc++_shared.so (std::__ndk1::thread::~thread()+24) #06 /data/app/com.domain.MyApp-l8euiXwxCtbbVZ_8HvQcMw==/lib/arm64/libLinesGameQt_arm64-v8a.so #07 /data/app/com.domain.MyApp-l8euiXwxCtbbVZ_8HvQcMw==/lib/arm64/libLinesGameQt_arm64-v8a.so (Squircle::setUnitTestMode(bool)+128)
and if I compile the test on Win32 with MSVC I get the same result:
std::thread::~thread() Line 57 C++ std::thread::`scalar deleting destructor'(unsigned int) C++ ThreadAbort_TestFunc(const awl::testing::TestContext & context) Line 20 C++ awl::testing::TestLink::Run(const awl::testing::TestContext & context) Line 30 C++ Squircle::setUnitTestMode(bool val) Line 961 C++ Squircle::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) Line 582 C++ Squircle::qt_metacall(QMetaObject::Call _c, int _id, void * * _a) Line 637 C++
std::thread destructor is implemented like this in MSVC (and likely on all the platforms):
class thread { public: ... ~thread() noexcept { // clean up if (joinable()) _STD terminate(); } ... };
so, obviously, the conclusion is that std::thread object is destroyed at a wrong place on Android 10, but it is not clear where and how to reproduce this in a real-life application.
Less often the app also crashes at the destructor of std::mutex:
#00 /apex/com.android.runtime/lib64/bionic/libc.so (abort+176) #01 /apex/com.android.runtime/lib64/bionic/libc.so (__fortify_fatal(char const*, ...)+116) #02 /apex/com.android.runtime/lib64/bionic/libc.so (HandleUsingDestroyedMutex(pthread_mutex_t*, char const*)+52) #03 /apex/com.android.runtime/lib64/bionic/libc.so (pthread_mutex_destroy+148) #04 /system/lib64/libc++.so (std::__1::mutex::~mutex()+8) #05 /apex/com.android.runtime/lib64/bionic/libc.so (__cxa_finalize+212) #06 /apex/com.android.runtime/lib64/bionic/libc.so (exit+24) #07 /data/app/com.domain.myapp-hLEaSANDEatnCiV7f2wbeg==/lib/arm64/libplugins_platforms_qtforandroid_arm64-v8a.so
And there are another crashes in __start_thread:
#00 /apex/com.android.runtime/lib64/bionic/libc.so (abort+160) #01 /apex/com.android.runtime/lib64/bionic/libc.so (__fortify_fatal(char const*, ...)+116) #02 /apex/com.android.runtime/lib64/bionic/libc.so (HandleUsingDestroyedMutex(pthread_mutex_t*, char const*)+52) #03 /apex/com.android.runtime/lib64/bionic/libc.so (pthread_mutex_lock+148) #04 /apex/com.android.runtime/lib64/bionic/libc.so (pthread_cond_wait+72) #05 /system/lib64/libc++.so (std::__1::condition_variable::wait(std::__1::unique_lock<std::__1::mutex>&)+20) #06 /system/lib64/libstagefright_bufferpool@2.0.so (android::hardware::media::bufferpool::V2_0::implementation::Accessor::Impl::invalidatorThread(std::__1::map<unsigned int, std::__1::weak_ptr<android::hardware::media::bufferpool::V2_0::implementation::Accessor::Impl> const, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::weak_ptr<android::hardware::media::bufferpool::V2_0::implementation::Accessor::Impl> const>>>&, std::__1::mutex&, std::__1::condition_variable&, bool&) [clone .cfi]+132) #07 /system/lib64/libstagefright_bufferpool@2.0.so (void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (*)(std::__1::map<unsigned int, std::__1::weak_ptr<android::hardware::media::bufferpool::V2_0::implementation::Accessor::Impl> const, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::weak_ptr<android::hardware::media::bufferpool::V2_0::implementation::Accessor::Impl> const> > >&, std::__1::mutex&, std::__1::condition_variable&, bool&), std::__1::reference_wrapper<std::__1::map<unsigned int, std::__1::weak_ptr<android::hardware::media::bufferpool::V2_0::implementation::Accessor::Impl> const, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::weak_ptr<android::hardware::media::bufferpool::V2_0::implementation::Accessor::Impl> const> > > >, std::__1::reference_wrapper<std::__1::mutex>, std::__1::reference_wrapper<std::__1::condition_variable>, std::__1::reference_wrapper<bool> > >(void*) (.cfi)+64) #08 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36) #09 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
#00 /apex/com.android.runtime/lib64/bionic/libc.so (abort+160) #01 /apex/com.android.runtime/lib64/bionic/libc.so (__fortify_fatal(char const*, ...)+116) #02 /apex/com.android.runtime/lib64/bionic/libc.so (HandleUsingDestroyedMutex(pthread_mutex_t*, char const*)+52) #03 /apex/com.android.runtime/lib64/bionic/libc.so (pthread_mutex_lock+148) #04 /system/lib64/libc++.so (std::__1::mutex::lock()+8) #05 /system/lib64/libstagefright_bufferpool@2.0.so (android::hardware::media::bufferpool::V2_0::implementation::ClientManager::getInstance() [clone .cfi]+28) #06 /system/lib64/libcodec2_hidl_client@1.0.so (android::hardware::media::c2::V1_0::utils::objcpy(std::__1::list<std::__1::unique_ptr<C2Work, std::__1::default_delete<C2Work>>, std::__1::allocator<std::__1::unique_ptr<C2Work, std::__1::default_delete<C2Work>>>>*, android::hardware::media::c2::V1_0::WorkBundle const&) [clone .cfi]+396) #07 /system/lib64/libcodec2_client.so (android::Codec2Client::Component::HidlListener::onWorkDone(android::hardware::media::c2::V1_0::WorkBundle const&)+60) #08 /system/lib64/android.hardware.media.c2@1.0.so (android::hardware::media::c2::V1_0::BnHwComponentListener::_hidl_onWorkDone(android::hidl::base::V1_0::BnHwBase*, android::hardware::Parcel const&, android::hardware::Parcel*, std::__1::function<void (android::hardware::Parcel&)>)+196) #09 /system/lib64/android.hardware.media.c2@1.0.so (android::hardware::media::c2::V1_0::BnHwComponentListener::onTransact(unsigned int, android::hardware::Parcel const&, android::hardware::Parcel*, unsigned int, std::__1::function<void (android::hardware::Parcel&)>)+636) #10 /system/lib64/libhidlbase.so (android::hardware::BHwBinder::transact(unsigned int, android::hardware::Parcel const&, android::hardware::Parcel*, unsigned int, std::__1::function<void (android::hardware::Parcel&)>)+68) #11 /system/lib64/libhidlbase.so (android::hardware::IPCThreadState::getAndExecuteCommand()+1036) #12 /system/lib64/libhidlbase.so (android::hardware::IPCThreadState::joinThreadPool(bool)+96) #13 /system/lib64/libhidlbase.so (android::hardware::PoolThread::threadLoop()+24) #14 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+288) #15 /system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+140) #16 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36) #17 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
#00 /apex/com.android.runtime/lib64/bionic/libc.so (abort+160) #01 /apex/com.android.runtime/lib64/bionic/libc.so (__fortify_fatal(char const*, ...)+116) #02 /apex/com.android.runtime/lib64/bionic/libc.so (HandleUsingDestroyedMutex(pthread_mutex_t*, char const*)+52) #03 /apex/com.android.runtime/lib64/bionic/libc.so (pthread_mutex_lock+148) #04 /apex/com.android.runtime/lib64/bionic/libc.so (pthread_cond_wait+72) #05 /system/lib64/libc++.so (std::__1::condition_variable::wait(std::__1::unique_lock<std::__1::mutex>&)+20) #06 /system/lib64/libhwui.so (android::uirenderer::CommonPool::workerLoop()+96) #07 /system/lib64/libhwui.so (void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, android::uirenderer::CommonPool::CommonPool()::$_0> >(void*)+132) #08 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36) #09 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
they all are on Android 10 and has HandleUsingDestroyedMutex in the call stack.
My QT version is 5.14.2.
Workaround
After some workaround app crash rate reduced a little bit:

but the crashes still occurs.
Hi this is Michael from Stack Overflow with some additional Info: Both our apps are 95% written in QML but in both apps we added the Helpshift SDK and then the crashes on Android 10 started. So we highly suspect that our implementation (C++ communication with native Java code and the Java Helpshift functions) might be to blame – we’ll look into that now.
Hi Michael! My QT app extends QtActivity in my custom Java code and uses JNI with QAndroidJniObject and QtAndroid::androidActivity(). The app calls Java from C++ and handles callbacks from Java. Also the app has a lot of QML.