Bug 8050 - C++ to C# callback from different threads causes an exception when running in a debugger
Summary: C++ to C# callback from different threads causes an exception when running in...
Alias: None
Product: iOS
Classification: Xamarin
Component: General ()
Version: 6.0.x
Hardware: Other Other
: --- normal
Target Milestone: Untriaged
Assignee: Rodrigo Kumpera
Depends on:
Reported: 2012-10-29 18:06 UTC by Conway
Modified: 2016-02-20 00:50 UTC (History)
2 users (show)

Is this bug a regression?: ---
Last known good build:

callback test source code (129.18 KB, application/octet-stream)
2012-10-30 10:39 UTC, Conway

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Conway 2012-10-29 18:06:36 UTC
MonoTouch 6.0.4
Mono 2.10.9
Xcode 4.5.1

Our app has a render callback mechanism where video frames are passed from our C++ library to C# so that we can use OpenGL to draw them onto the screen. For each video stream, the first call back is a SetVideoResolution and then for each video frame, there are a pair of BeginDraw/EndDraw callbacks. Our app has a long term cycle test: start video, stream video for 10 seconds, stop video, repeat. Each cycle starts a new thread in C++, calls the SetVideoResolution callback, calls BeginDraw/EndDraw numerous times and then stops the thread. After ~1000 cycles, the app (running in the debugger) crashes with the stacktrace below. I think if the app is not running in the debugger, it doesn't crash.

There appears to be a bug in the interaction between MonoTouch's profiler and the default garbage collector. The first callback from a new thread created in our C++ library seems to create a corresponding thread in the profiler code but the corresponding profiler thread never exits. Perhaps there is a max number of threads that the profiler can have created and once that limit is hit, a crash results? If you look at the debug output below from a test app that I created, the "Thread started" message comes from the created profiler thread. There is never a corresponding "Thread Finished" message.

If the "Project Options->Build->iPhone Build->Advanced->Use SGen generational garbage collector" option is enabled, the crash does not happen (runs indefinitely). The debug output shows that there is a "Thread Finished" message for each "Thread Started" message.

I'll attached a zip of the test app.

Thread started: 
2012-10-29 16:26:07.391 CallbackTest[883:42cf] Callback::SetVideoResolution 994
2012-10-29 16:26:07.394 CallbackTest[883:42cf] Callback::BeginDraw 994
2012-10-29 16:26:07.398 CallbackTest[883:42cf] Callback::EndDraw 994
Thread started: 
2012-10-29 16:26:07.592 CallbackTest[883:42d3] Callback::SetVideoResolution 995
2012-10-29 16:26:07.596 CallbackTest[883:42d3] Callback::BeginDraw 995
2012-10-29 16:26:07.599 CallbackTest[883:42d3] Callback::EndDraw 995
Thread started: 
2012-10-29 16:26:07.792 CallbackTest[883:42d7] Callback::SetVideoResolution 996
2012-10-29 16:26:07.795 CallbackTest[883:42d7] Callback::BeginDraw 996
2012-10-29 16:26:07.799 CallbackTest[883:42d7] Callback::EndDraw 996
Thread started: 
2012-10-29 16:26:07.992 CallbackTest[883:42db] Callback::SetVideoResolution 997
2012-10-29 16:26:07.995 CallbackTest[883:42db] Callback::BeginDraw 997
2012-10-29 16:26:07.999 CallbackTest[883:42db] Callback::EndDraw 997
Thread started: 
2012-10-29 16:26:08.191 CallbackTest[883:42df] Callback::SetVideoResolution 998
2012-10-29 16:26:08.194 CallbackTest[883:42df] Callback::BeginDraw 998
2012-10-29 16:26:08.198 CallbackTest[883:42df] Callback::EndDraw 998
Thread started: 
2012-10-29 16:26:08.391 CallbackTest[883:42e3] Callback::SetVideoResolution 999
2012-10-29 16:26:08.394 CallbackTest[883:42e3] Callback::BeginDraw 999
2012-10-29 16:26:08.397 CallbackTest[883:42e3] Callback::EndDraw 999
Thread started: 
2012-10-29 16:26:08.592 CallbackTest[883:42e7] Callback::SetVideoResolution 1000
2012-10-29 16:26:08.595 CallbackTest[883:42e7] Callback::BeginDraw 1000
2012-10-29 16:26:08.599 CallbackTest[883:42e7] Callback::EndDraw 1000
Thread started: 
2012-10-29 16:26:08.791 CallbackTest[883:42eb] Callback::SetVideoResolution 1001
2012-10-29 16:26:08.795 CallbackTest[883:42eb] Callback::BeginDraw 1001
2012-10-29 16:26:08.798 CallbackTest[883:42eb] Callback::EndDraw 1001
Thread started: 
2012-10-29 16:26:08.992 CallbackTest[883:42ef] Callback::SetVideoResolution 1002
2012-10-29 16:26:08.995 CallbackTest[883:42ef] Callback::BeginDraw 1002
2012-10-29 16:26:08.999 CallbackTest[883:42ef] Callback::EndDraw 1002
Thread started: 
2012-10-29 16:26:09.192 CallbackTest[883:42f3] Callback::SetVideoResolution 1003
2012-10-29 16:26:09.195 CallbackTest[883:42f3] Callback::BeginDraw 1003
2012-10-29 16:26:09.199 CallbackTest[883:42f3] Callback::EndDraw 1003

Native stacktrace:

	0   CallbackTest                        0x00213121 mono_handle_native_sigsegv + 244
	1   CallbackTest                        0x00244781 sigabrt_signal_handler + 112
	2   libsystem_c.dylib                   0x3347a7ed _sigtramp + 48
	3   libsystem_c.dylib                   0x3347020f pthread_kill + 54
	4   libsystem_c.dylib                   0x3346929f abort + 94
	5   CallbackTest                        0x00213ccd GC_remove_roots + 0
	6   CallbackTest                        0x00213d8d GC_add_roots + 44
	7   CallbackTest                        0x00252f69 mono_gc_register_root + 12
	8   CallbackTest                        0x00234cb9 thread_startup + 228
	9   CallbackTest                        0x00247cb3 mono_profiler_thread_start + 46
	10  CallbackTest                        0x0026b941 mono_thread_attach + 464
	11  CallbackTest                        0x001ce38f mono_jit_thread_attach + 38
	12  CallbackTest                        0x0000fa08 wrapper_native_to_managed_CallbackTest_CallbackTestViewController_StaticSetVideoResolution_uint_uint_CallbackTest_CallbackTestViewController_MVCSRenderSetResolutionReturnData_ + 40
	13  CallbackTest                        0x00004d67 _ZN16CIosCameraDevice20captureThreadRoutineEv + 230
	14  CallbackTest                        0x0000474f _ZN16CIosCameraDevice23captureThreadDispatcherEPv + 30
	15  CallbackTest                        0x00002f31 _ZN7CThread16threadDispatcherEPv + 252
	16  libsystem_c.dylib                   0x33431735 _pthread_start + 320
	17  libsystem_c.dylib                   0x334315f0 thread_start + 8
Comment 1 Conway 2012-10-30 10:39:50 UTC
Created attachment 2815 [details]
callback test source code
Comment 2 Conway 2012-10-30 11:01:48 UTC
Instructions for the CallbackTest source:
1. Open the MVCSCore.xcodeproj file with Xcode and compile (Product->Build) it for "iOS Device" (not the Simulator). The MonoTouch project expects the libMVCSCore.a file to be in  MVCSCore/bin/Debug-iphoneos.
2. Load the CallbackTest.sln in MonoDevelop, change the Active Configuration to Debug|iPhone and select Run->Start Debugging.
3. When the app is running on the attached iOS device, press the Start button in the amazing UI.
4. Wait for the count to reach 1000 and the app should crash. Notice the "Thread started" message and no corresponding "Thread finished" message in the debug output.
5. If you check the "Use SGen generational garbage collector" checkbox in the iPhone Build options and restart the test, the test will not crash. There are matching "Thread started" and "Thread finished" messages in the debug output.

What the app does:
1. When the app loads, it registers three callbacks with the C++ MVCSCore library and starts a 100ms timer.
2. When the timer fires, it alternates between making Enable and Disable calls into the C++ library.
3. In the C++ library:
  - the Enable call starts a thread and makes 3 callbacks back up into the C# code.
  - the Disable call stops and exits the thread that was created in the previous Enable call.
Comment 3 Rolf Bjarne Kvinge [MSFT] 2012-10-30 12:54:12 UTC
Rodrigo: this looks GC/runtime related.
Comment 4 Rodrigo Kumpera 2012-10-31 12:47:51 UTC
There is one bug in your code. You cannot pass delegates to native code and then let them be collected as this can result in crashes.

Store the delegates in a static variable, for example.

If you do this change, does it fix your problem?
Comment 5 Conway 2012-10-31 17:50:34 UTC
In ConnectToCore(), the renderInterface structure had been allocated on the stack and now I've moved the structure to be a member of the CallbackTestViewController object, which should anchor it from a garbage collection perspective since the CallbackTestViewController object survives the lifetime of the app.

With this change, the test app still crashes exactly as before (iteration 1004). Just for good measure, I changed the renderInterface variable to "static" and that crashed too.
Comment 6 Conway 2013-01-25 10:34:07 UTC
Any update on this? It still happens with MonoTouch 6.0.8. It's getting in the way of long-term cycle testing that I'm using to track down another bug in our code, because it causes the app to crash after 1000 iterations. Thanks.
Comment 7 PJ 2013-11-19 17:05:47 UTC
This bug has been in the NEEDINFO state with no changes for the last 90 days. Can we put this back into the NEW or CONFIRMED state, or are we still awaiting response?

If there is no change in the status of this bug over the next two weeks, this bug will be marked as NORESPONSE.
Comment 8 Rodrigo Kumpera 2013-11-19 18:13:53 UTC
We should probably take a look at the test case to see if it's reproducible.
Comment 9 Conway 2013-11-20 11:23:13 UTC
This still happens with Xamarain.iOS Comment #4 was requesting info, which I provided in comment #5.
Comment 10 Rolf Bjarne Kvinge [MSFT] 2016-02-20 00:50:45 UTC
According to the initial description in this bug, the issue does not happen with SGen, and since the Boehm GC is being phased out (it will be removed in a later release of Xamarin.iOS this year), I'm closing this bug.

Please reopen if it's still a problem with SGen.