Crash using C# delegate in C++ plugin in android only

So here is my problem. We are using a C++ dll (.so) with our unity app. First we register callbacks to delegate using the following signature:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void OnClientStateChange(int oldState, int newState);
public OnClientStateChange onClientStateChange;

And I pass those to the unmanaged side with: Marshal.GetFunctionPointerForDelegate(onClientStateChange)

In unmanaged my signature is
typedef void CDECL tClientStateChange(int oldState, int newState);
tClientStateChange* cb_clientStateChange;
where CDECL is defined to nothing under android.

Now on the unmanaged side, we are using multiple threads, and one of those is calling my callback. Everything works fine, except one thing… a few seconds later my app crashes.

This issue only occurs on android (everything is fine in windows). I tried emptying the callback (basically calling an empty function) and I still get that crash a few seconds later.

I am nowhere near an expert on CLI and C++/C# interactions. I read as much as I could and ended up reading that in multi-threaded environments I need to call mono_thread_attach on any new thread before invoking managed code. I have no idea if this is accurate or if I misunderstood, nor how or where to find that function (and google has very few answers)

My guess is that the call to managed callback somehow messes with my memory, and creates a crash later down the line (not calling the callback, no crash. calling it, even with an empty function as callback, crash). I also tried using the stdcall calling convention, with no luck.

If anyone has any idea I would really appreciate. Unfortunately I can’t provide a code example as it is a rather big and proprietary project. My adb log is pretty much worthless, and always indicate an error in libmono. I am not proficient enough with assembly to make any sense of an objdump and anyways, the crash happens when I am randomly pressing buttons in the app later on, and nowhere near the moment when I make my call to the callback. (those buttons only show/hide menus, no funky stuff in there)

  1. Is your callback called successfully (from C++ back to managed C#) ?
  2. Can you post the crashing stack trace from logcat ?
  3. Have you tried other methods of communication between native and managed code ? why did you go for this implementation ?
  1. Yes, the callback is called. It’s a callback that sends a byte array to unity, which represents audio. the audio can even be played properly. The problem is, the app crashes a bit later if a call is made to the callback.

  2. Not right now as it is Saturday and I’m not in the office, but I’ll try to get one on monday. The stack trace really didnt say much, it was all libMono. attempts to use objdump failed, the adresses didnt match anything.

  3. Our plugin does a whole lot of stuff, it’s basically a bridge between our company’s libraries and Unity. The only particular thing with that one callback, is the fact that it is not being called from the main C++ thread, but rather a thread created in it. For now I used a workaround (instead of sending the byte array to unity, I buffer it, and I let unity poll it/retrieve it) but for my sanity’s sake, I’d love to know why the previous method crashed. As you might have guessed, tracking the crash to that particular function took a lot of time, and I only have a partial understanding of why it might crash, but heap corruption seems the most likely.

From your code snippet, it looks like a delegate that sends 2 ints, not a byte array.
Also, you say that the callback is indeed called. When does it crash? i didn’t fully understand from what you wrote.

Regarding the stack - i can try to help you decode the crash stack trace (given that it occurs in a library that you have the symbols for).

If this is something you’d like to resolve (although you mentioned that you have a workaround), i can help you out. we can discuss in private if you’re interested.

Yeah sorry, I copy pasted some generic callback we have, trying to give a sample of what we do and simplifying the context :slight_smile:
Originally, the function returned a byte buffer, that was coTaskMemAlloc and freed within unity, expecting that might be the issue, I stripped it down and down, unity I was calling a function that took2 int params, and did nothing at all, the crash still occured.

When does it crash?.. ok full context, We do the equivalent of a phone call, think skype. the callback is what sends the audio to unity. the crash occurs after we hang up the call, when the user presses ui buttons (at least that’s the easiest repro I’ve found) the ui buttons just enable/disable canvas, so nothing fancy or related, and mashing them at any other call won’t crash.

The crash ONLY occurs IF the callback was called (even if the callback is stripped to an empty function) and from what I’ve seen, after we hang up. The fiddling with UI buttons only accelerates the crash occuring.

I’ll try to grab logs early next week. we do have symbols for everything.

Here is a log as promised:

2874947–210816–log.txt (5.11 KB)

Looks like the crash originates from Mono, could be related to GC.
Going back to the question i asked in my first reply: Have you tried other methods of communication between native and managed code ? why did you go for this implementation ?

I can think of 2 different ways of doing this:

  • UnitySendMessage - Unity’s “built-in” method of calling into the managed side from native code. It supports passing primitive args and may be suitable for your case here. I am not sure about its performance though.
  • Using Mono’s automatic marshalling… you shouldn’t really declare the delegate and pass a native pointer to it, as Mono should know how to handle this already.

Another point is memory management - i am not sure what is the usage pattern of the callback:
Are you storing it and invoking it later on? what about object lifetimes?
Does the class declaring the delegate method still exist when you’re invoking the callback?

Lastly, the GC will compact memory from time to me. If you’re passing a reference (address) of a C# delegate into native code, this address will point to garbage after the GC executes as it may move this object around in memory. There are ways to correct this, but it may very well be the issue you’re having.

I am available to talk in private as well, in case you’re interested.

*As a sidenote, this link could be helpful to go over: Interop with Native Libraries | Mono

We don’t really have other means of communication from native to managed. Either the managed polls the native, or the native calls a callback, or maybe I misunderstand your question.

I was not aware of the existence of UnitySendMessage, it might be worth a shot, but I’d still be curious as to why my code crashes.

I based my code off the samples provided by Unity and the link you provided at the end of your message. I can’t remember the exact location of those samples though, it’s been a while, and like I said, we use a lot of those callbacks, and it’s the only one that crashed our app.

The plugin handler (C#, containing the callback) is created when the app is launched, it sends its callbacks to the plugin, and dies with the app. I see a lot of samples using static functions for their callbacks though, maybe it makes a difference GC-wise, but I am certain the object containing the callbacks is not destroyed.

We can talk in private if you prefer, but I guess someone might find this thread helpful :wink:

This might be useful to anyone trying to write a complex plugin…

Any solution to the problem yet?

I think I have encountered the same problem on Android. In my case, I first started a native thread loop and kept it running in c++ making callbacks. Initially, the callback function pointer was set to null and no calls are made. The first time when I connected the callback (by passing the unmanaged function pointer from c# to c/c++ side, in exactly the same way you did), it executed correctly and callback messages are constantly received on c# side. However when I disconnected the callback (by resetting the callback function pointer to null on c++ side), paused and then resumed the app, and then tried to reconnect the callback, the app crashed upon receiving a few callbacks. The app will also crash on exit, even when I disconnected the callback, set the static delegate to null.

The same code does not crash on iOS.

Edit: I can connect and disconnect the callback multiple times with no crash before the app is first paused. Also if I keep my native thread running during app pause, then no crash on resume also. But that is not the right way doing it.

We ended up polling the dll instead or relying on the callback. It feels a bit lazy and I,kd loe o understand the issue though.

I solved my crashing issue. Just a simple trick - use IL2CPP backend instead of Mono. You can set that under PlayerSettings->Other Settings->Scripting backend. You’ll be prompted to set your NDK path if it is the first time. Note the compilation time can be longer than mono, so be patient and maybe just compile for ARMv7 instead of FAT option to speed up compilation.

1 Like