realm-dotnet: Using Realm with a SynchronizationContext implementation that is not single-thread-affine

Crash at Realms.SynchronizationContextEventLoopSignal/EventLoop

Goal

I have a complex async/await/Reactive flow which uses multiple Realm instances using the same RealmConfiguration and also has a custom SynchronizationContext involved that is crashing almost 100% of the time. The Realm 0.80.0 version of the App runs fine.

I repo’d this “random” issue using xUnit for Devices / Xunit.Sdk.AsyncTestSyncContext and Realm 0.81.0 as it produces a similar crash and stack trace. You will a see repeating xUnit [Theory] in the unit test to ensure that it does crash. It almost always crashes on the first execution, but sometimes it might work 2,3 times before crashing… as the crash is not 100% deterministic.

Expected Results

No crash due to custom SynchronizationContext

Actual Results

libc++abi.dylib: terminating with uncaught exception of type realm::IncorrectThreadException: Realm accessed from incorrect thread.
Realm0810SynchronizationContext[24731:19379897] critical: Stacktrace:

critical:   at <unknown> <0xffffffff>
critical:   at (wrapper managed-to-native) object.wrapper_native_0x10f019150 (intptr) <IL 0x00009, 0x000dd>
critical:   at Realms.SynchronizationContextEventLoopSignal/EventLoop/<Post>c__AnonStorey0.<>m__0 (object) <IL 0x0001e, 0x000a5>
critical:   at Xunit.Sdk.AsyncTestSyncContext/<>c__DisplayClass7_0.<Post>b__1 (object) <IL 0x0000c, 0x00082>
critical:   at Xunit.Sdk.MaxConcurrencySyncContext.RunOnSyncContext (System.Threading.SendOrPostCallback,object) <IL 0x0000d, 0x000c4>
critical:   at Xunit.Sdk.MaxConcurrencySyncContext/<>c__DisplayClass11_0.<WorkerThreadProc>b__0 (object) <IL 0x0001c, 0x000f3>
critical:   at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x0008d] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:957
critical:   at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x00000] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:904
critical:   at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object) [0x00031] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:893
critical:   at (wrapper runtime-invoke) <Module>.runtime_invoke_void_object_object_object (object,intptr,intptr,intptr) <IL 0x00065, 0x00352>
critical:   at <unknown> <0xffffffff>
critical:   at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) <IL 0x00016, 0x00131>
critical:   at System.Reflection.MonoMethod.Invoke (object,System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo) [0x00038] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:305
critical:   at System.Reflection.MethodBase.Invoke (object,object[]) [0x00000] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/reflection/methodbase.cs:229
critical:   at Microsoft.Scripting.Interpreter.MethodInfoCallInstruction.InvokeWorker (object[]) [0x00010] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/dlr/Runtime/Microsoft.Dynamic/Interpreter/Instructions/CallInstruction.cs:261
critical:   at Microsoft.Scripting.Interpreter.MethodInfoCallInstruction.Invoke (object[]) [0x00000] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/dlr/Runtime/Microsoft.Dynamic/Interpreter/Instructions/CallInstruction.cs:239
critical:   at Microsoft.Scripting.Interpreter.MethodInfoCallInstruction.Run (Microsoft.Scripting.Interpreter.InterpretedFrame) [0x0003b] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/dlr/Runtime/Microsoft.Dynamic/Interpreter/Instructions/CallInstruction.cs:289
critical:   at Microsoft.Scripting.Interpreter.Interpreter.Run (Microsoft.Scripting.Interpreter.InterpretedFrame) [0x0001b] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/dlr/Runtime/Microsoft.Dynamic/Interpreter/Interpreter.cs:126
critical:   at Microsoft.Scripting.Interpreter.LightLambda.RunVoid2<T0_REF, T1_REF> (T0_REF,T1_REF) [0x0002a] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/dlr/Runtime/Microsoft.Dynamic/Interpreter/LightLambda.Generated.cs:117
critical:   at Xunit.Sdk.ExecutionContextHelper.Run (object,System.Action`1<object>) <IL 0x00011, 0x000f3>
critical:   at Xunit.Sdk.MaxConcurrencySyncContext.WorkerThreadProc () <IL 0x0007c, 0x00450>
critical:   at Xunit.Sdk.XunitWorkerThread/<>c.<QueueUserWorkItem>b__5_0 (object) <IL 0x0000d, 0x000d1>
critical:   at System.Threading.Tasks.Task.InnerInvoke () [0x0002b] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/Tasks/Task.cs:2885
critical:   at System.Threading.Tasks.Task.Execute () [0x00016] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/Tasks/Task.cs:2502
critical:   at System.Threading.Tasks.Task.ExecutionContextCallback (object) [0x00007] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/Tasks/Task.cs:2865
critical:   at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x0008d] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:957
critical:   at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x00000] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:904
critical:   at System.Threading.Tasks.Task.ExecuteWithThreadLocal (System.Threading.Tasks.Task&) [0x0005f] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/Tasks/Task.cs:2827
critical:   at System.Threading.Tasks.Task.ExecuteEntry (bool) [0x0006f] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/Tasks/Task.cs:2760
critical:   at System.Threading.Tasks.ThreadPoolTaskScheduler.LongRunningThreadWork (object) [0x00007] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/Tasks/ThreadPoolTaskScheduler.cs:47
critical:   at System.Threading.ThreadHelper.ThreadStart_Context (object) [0x0002c] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/thread.cs:72
critical:   at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x0008d] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:957
critical:   at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x00000] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:904
critical:   at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object) [0x00031] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:893
critical:   at System.Threading.ThreadHelper.ThreadStart (object) [0x00012] in /Users/builder/data/lanes/3985/b4a43820/source/xamarin-macios/_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/referencesource/mscorlib/system/threading/thread.cs:87
critical:   at (wrapper runtime-invoke) <Module>.runtime_invoke_void__this___object (object,intptr,intptr,intptr) <IL 0x00053, 0x00310>
critical: 
Native stacktrace:
2016-12-15 18:23:16.487 Realm0810SynchronizationContext[24731:19379660] 	[PASS] Realm0810SynchronizationContext.RealmRead.ObjectForPrimaryKey_Sequential
Thread finished:  #9
Thread finished:  #10
 Tests run: 1 Passed: 1 Failed: 0 Skipped: 0
critical: 	0   libmonosgen-2.0.dylib               0x000000011a5d9c3d mono_handle_native_sigsegv + 253
critical: 	1   libsystem_platform.dylib            0x000000011e44352a _sigtramp + 26
critical: 	2   libsystem_c.dylib                   0x000000011e1d50b3 suboptarg + 5403
critical: 	3   libsystem_c.dylib                   0x000000011e1a0fd7 abort + 129
critical: 	4   libc++abi.dylib                     0x000000011ac6d95a __cxa_bad_cast + 0
critical: 	5   libc++abi.dylib                     0x000000011ac92ce7 _ZL25default_terminate_handlerv + 243
critical: 	6   libobjc.A.dylib                     0x000000011acaa4b4 _ZL15_objc_terminatev + 124
critical: 	7   libc++abi.dylib                     0x000000011ac8fe69 _ZSt11__terminatePFvvE + 8
critical: 	8   libc++abi.dylib                     0x000000011ac8f8e0 _ZN10__cxxabiv1L22exception_cleanup_funcE19_Unwind_Reason_CodeP17_Unwind_Exception + 0
critical: 	9   Realm0810SynchronizationContext     0x000000010f034e62 _ZNK5realm5Realm13verify_threadEv + 80
critical: 	10  Realm0810SynchronizationContext     0x000000010f03562e _ZN5realm5Realm6notifyEv + 68
critical: 	11  Realm0810SynchronizationContext     0x000000010f018f65 _ZNK5realm5_impl17WeakRealmNotifier8CallbackclEv + 45
critical: 	12  ???                                 0x00000001346393ce 0x0 + 5173908430
critical: 	13  ???                                 0x00000001345d2435 0x0 + 5173486645
critical: 	14  ???                                 0x000000013302362c 0x0 + 5150750252
critical: 	15  libmonosgen-2.0.dylib               0x000000011a52e747 mono_jit_runtime_invoke + 2247
critical: 	16  libmonosgen-2.0.dylib               0x000000011a6f7638 do_runtime_invoke + 88
critical: 	17  libmonosgen-2.0.dylib               0x000000011a6fb070 mono_runtime_try_invoke_array + 1536
critical: 	18  libmonosgen-2.0.dylib               0x000000011a6684ee ves_icall_InternalInvoke + 734
critical: 	19  ???                                 0x00000001331701e2 0x0 + 5152113122
critical: 	20  ???                                 0x000000013316fad8 0x0 + 5152111320
critical: 	21  ???                                 0x00000001345b6168 0x0 + 5173371240
critical: 
=================================================================
Got a SIGABRT while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
=================================================================

Code Sample

Xamarin.iOS app using xUnit for Devices:

Realm081Test.zip SHA Checksum is : 78ec0beeeb0beb21e6eae718c9c92f6b8ebc6e90

Within the SynchronizationContextCrash.cs | GenerateDB method, if you comment out the Realm.WriteAsync block the crash does not ever happen.

Downgrading the Realm package to 0.80.0 will result in Success: 100 tests

async Task<Realm> GenerateDB(string path)
{
	var config = new RealmConfiguration(path)
	{
		ObjectClasses = new Type[] { typeof(KeyValue) }
	};
	var cache = Realm.GetInstance(config);

	// Commit out the WriteSync, replace with "await Task.FromResult(0);" and the "random" crash goes away
	await cache.WriteAsync(r =>
	{
		// Fetch and write new objects
	});
	//await Task.FromResult(0);

	return cache;
}

Version of Realm and tooling

Realm version(s): 0.81.0 Which operating system version and device: Only iOS has be tested so far (sim & device)

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 18 (9 by maintainers)

Most upvoted comments

Hey @sushihangover,

This is an unfortunate side-effect of our work to support Windows. For 0.81.0 we migrated away from platform-specific code for the iOS RunLoop and Android Looper to an abstraction based on System.Threading.SynchronizationContext in order to support more platforms.

Most of the SynchronizationContext implementations out there - the ones Xamarin built on top of the iOS RunLoop and Android Looper and the ones that come with Windows Forms, WPF, and UWP, are thread-affine in that they always post work on the same thread. xUnit, however, comes with a SynchronizationContext implementation that uses a dedicated worker thread.

The simple workaround is to temporarily unset the current SynchronizationContext when opening a realm on a worker thread so that the realm doesn’t capture it:

Realm GetInstanceWithoutCapturingContext(RealmConfiguration config)
{
    var context = SynchronizationContext.Current;
    SynchronizationContext.SetSynchronizationContext(null);

    Realm realm = null;
    try
    {
        realm = Realm.GetInstance(config);
    }
    finally
    {
        SynchronizationContext.SetSynchronizationContext(context);
    }

    return realm;
}

When opening a realm on a thread without a SynchronizationContext, the reactive properties of Realm - change events and auto refresh, will be disabled, same as when opening a realm on a thread without a runloop or looper in previous versions.

Alternatively, you can use Nito.AsyncEx.AsyncContext in your tests which will install a SynchronizationContext implementation that is thread-affine. This is the library I’m using for running our tests on Windows.

There might be further improvements we can come up with to be compatible with more SynchronizationContext implementations, which will hopefully eliminate the need for these workarounds.

manually calculating/publishing change events is ugly

You can call Realm.Refresh and it should force the realm to move to the latest version of the realm file and emit change events for any transactions between the current and new one.

I will re-visit this as I lost the change events for some reason… (great info on the trans/pipe/refresh usage, you should tag that info in an “Advanced Realm threading for Dummies” 😉 section of the docs #1041)

losing the Realm benefits

Unless the thread you open the realm instance on has an active and spinning CFRunLoop or ALooper that realm instance…

Yes, I have CFRunloops&Loopers (non-UI) so I can post long running user requests, capture network sources, etc…

The app uses a custom IScheduler/SynchronizationContext that manages a thread pool

I would be really grateful if you could go into a little bit more detail about the nature of this SynchronizationContext implementation

NP, I’ll put together something and post another comment