Crash on sendChangeMessage [Android]


#1

I’m getting a crash which I’ve narrowed down to the sendChangeMessage method. This happens only on Android. The ChangeBroadcaster class that is causing the crash also happens to be instantiated as a SharedResourcePointer. I don’t know if that should make a difference. I might try and make it a Singleton instead.

The crash log is as follows:

                                                            --------- beginning of crash
09-08 20:52:37.371 7253-7297/com.mycompany.myapp A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 7297 (mqt_native_modu)
09-08 20:52:37.392 7253-7263/com.mycompany.myapp W/art: Suspending all threads took: 5.389ms
09-08 20:52:37.476 1202-1202/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
09-08 20:52:37.476 1202-1202/? A/DEBUG: Build fingerprint: 'Android/sdk_google_phone_x86/generic_x86:6.0/MASTER/3038907:userdebug/test-keys'
09-08 20:52:37.476 1202-1202/? A/DEBUG: Revision: '0'
09-08 20:52:37.476 1202-1202/? A/DEBUG: ABI: 'x86'
09-08 20:52:37.477 1202-1202/? A/DEBUG: pid: 7253, tid: 7297, name: mqt_native_modu  >>> com.mycompany.myapp <<<
09-08 20:52:37.477 1202-1202/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
09-08 20:52:37.484 1202-1202/? A/DEBUG:     eax 00000000  ebx a137dad0  ecx 9f8078f0  edx 00000000
09-08 20:52:37.484 1202-1202/? A/DEBUG:     esi 00100646  edi 9eeb9cc8
09-08 20:52:37.484 1202-1202/? A/DEBUG:     xcs 00000073  xds 0000007b  xes 0000007b  xfs 00000097  xss 0000007b
09-08 20:52:37.484 1202-1202/? A/DEBUG:     eip a0b7ae5c  ebp 9eeb9c78  esp 9eeb9c60  flags 00210246
09-08 20:52:37.487 1202-1202/? A/DEBUG: backtrace:
09-08 20:52:37.487 1202-1202/? A/DEBUG:     #00 pc 003ace5c  /data/app/com.mycompany.myapp-1/lib/x86/libjuce_jni.so (_JNIEnv::CallVoidMethodV(_jobject*, _jmethodID*, char*)+10)
09-08 20:52:37.487 1202-1202/? A/DEBUG:     #01 pc 0040775a  /data/app/com.mycompany.myapp-1/lib/x86/libjuce_jni.so (juce::GlobalRef::callVoidMethod(_jmethodID*, ...) const+64)
09-08 20:52:37.487 1202-1202/? A/DEBUG:     #02 pc 006a24c3  /data/app/com.mycompany.myapp-1/lib/x86/libjuce_jni.so (juce::MessageManager::postMessageToSystemQueue(juce::MessageManager::MessageBase*)+69)
09-08 20:52:37.487 1202-1202/? A/DEBUG:     #03 pc 0069cf98  /data/app/com.mycompany.myapp-1/lib/x86/libjuce_jni.so (juce::MessageManager::MessageBase::post()+58)
09-08 20:52:37.487 1202-1202/? A/DEBUG:     #04 pc 0069e1c8  /data/app/com.mycompany.myapp-1/lib/x86/libjuce_jni.so (juce::AsyncUpdater::triggerAsyncUpdate()+150)
09-08 20:52:37.487 1202-1202/? A/DEBUG:     #05 pc 0069e555  /data/app/com.mycompany.myapp-1/lib/x86/libjuce_jni.so (juce::ChangeBroadcaster::sendChangeMessage()+55)
09-08 20:52:37.487 1202-1202/? A/DEBUG:     #06 pc 0050f528  /data/app/com.mycompany.myapp-1/lib/x86/libjuce_jni.so (SharedObjects::resumeApp()+94)
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #07 pc 00510eab  /data/app/com.mycompany.myapp-1/lib/x86/libjuce_jni.so (Java_com_mycompany_myapp_MainActivity_resumeJuceApp+49)
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #08 pc 01abab6e  /data/app/com.mycompany.myapp-1/oat/x86/base.odex (offset 0xa90000) (void com.mycompany.myapp.MainActivity.resumeJuceApp()+98)
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #09 pc 01934187  /data/app/com.mycompany.myapp-1/oat/x86/base.odex (offset 0xa90000) (void com.mycompany.myapp.ReactScaleController.resume()+187)
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #10 pc 00137a82  /system/lib/libart.so (art_quick_invoke_stub+338)
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #11 pc 001435c4  /system/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+212)
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #12 pc 0050f858  /system/lib/libart.so (art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned int)+1736)
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #13 pc 0048c5e3  /system/lib/libart.so (art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobject*)+80)
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #14 pc 725a8ca4  /data/dalvik-cache/x86/system@framework@boot.oat (offset 0x1eb2000)
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #15 pc 00011eff  [anon:libc_malloc]
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #16 pc 00101e73  [stack:7297]
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #17 pc 00102013  [stack:7297]
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #18 pc 00001fff  [anon:thread signal stack]
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #19 pc 82989e99  <unknown>
09-08 20:52:37.488 1202-1202/? A/DEBUG:     #20 pc 82596436  <unknown>
09-08 20:52:37.696 1521-7454/system_process W/ActivityManager:   Force finishing activity com.mycompany.myapp/com.reactnativenavigation.controllers.NavigationActivity


#2

Can you give us some code to reliably reproduce this? Can you reproduce this with a minimal app that just has a ChangeBroadcaster as a SharedResourcePointer?


#3

I’ll see if I can reproduce it. Thanks


#4

I haven’t had time to create a minimal app to reproduce this yet, but its not to do with SharedResourcePointer necessarily - I changed my class to a Singleton based on std::unique_ptr and it still crashes.

I don’t know if its a red herring or not but it seems to be due to the JNIEnv returning null, from the assertion hit: JUCE Assertion failure in juce_android_SystemStats.cpp:107

JNIEnv* getEnv() noexcept
{
    JNIEnv* env = androidJNIEnv.get();
    jassert (env != nullptr);    // <--------- assertion failure

    return env;
}

The message it posts never gets to the Java side, so it looks like the assertion may be causing the crash.

Is there a reason why a Singleton or SharedResourcePointer derived from ChangeBroadcaster might return null for JNIEnv?


#5

I also hit this assertion when calling startThread() on a Thread subclass from the same Singleton.


#6

Hmmm this would happen if the Thread was not created by JUCE. Any chance that the thread was created by another program?


#7

No, definitely created by JUCE! The Thread class is instantiated as a std::unique_ptr member of another class.


#8

Just ran into this error again in a different app. I meant to send an example project to highlight the issue. I will when I get the time (probably after the ADC now!)

My current assumption is that it is to do with threading. Both apps are using React Native, which employs multiple threads.


#9

Actually there is a chance the thread was created by React Native. Would that make more sense?


#10

Yes that would make perfect sense. If you have a thread created by something other than JUCE, then you need to call setEnv on the thread before using any JUCE code. See the comment in juce_android_JNIHelper.h:

// You should rarely need to use this function. Only if you expect callbacks
// on a java thread which you did not create yourself.
extern void setEnv (JNIEnv* env) noexcept;

#11

Thanks I’ll give that a go


#12

I’m using JUCE with some other code that creates it’s own thread and would like those threads to be able to post updates to the JUCE GUI. (This is all C++ code, no Java). I’m running into the issue above when trying to run on Android. What do I need to pass to the setEnv() call made by own thread? I tried cacheing the result of getEnv() from the JUCE GUI thread and then using this later as the parameter to setEnv() in the other thread … it makes it pass the Assert() here, but ends up failing anyway. Any ideas would be very helpful


#13

No, every thread has it’s own JNIEnv pointer so you can’t re-use one from another thread.

There are two ways to get a JNIEnv pointer:

  1. In most cases you are currently executing native code because somewhere up the call-stack java code invoked some native method. Check your call-stack if this is the case. The JNI pointer will be supplied to you automatically by java: it is passed as the first argument to the C++ entry point for the native method that java called. You need to somehow store it there (or call just setEnv there if you can).

  2. The native code launched a new thread via pthreads or forking. If this is the case, then you need to create a new JNIEnv pointer via JNI’s AttachCurrentThread. AttachCurrentThread needs a JavaVM pointer which can be shared between different threads. You can get the JavaVM pointer on any thread that already has it’s own JNIEnv via JNI’s GetJavaVM method.


#14

Thanks! My case is #2 and I will try that. Meanwhile I was able to wrap a Juce Thread around my own cross-platform threading code class as a work-around in a pretty clean fashion. I’m currently debugging some deadlock issue that seems to be occurring where my native thread (now a Juce thread) is calling triggerAsyncUpdate() as a cue to the GUI thread component to update some displayed content … it’s only happening on Android (update - solved that issue)