Closed stel closed 7 years ago
After some more investigation it seems like the issue is related to realm size also, it doesn't reproduce for small realms.
I've made an example app that generates a realm file on the server with 10K records. Trying to open this URL (/crash
) leads to crash.
It looks like something bad is happening to the info object on the Realm between creation and use. Note how the name
field changes from "Widget" to something completely invalid between the two invocations.
Good
Bad
My guess is that it has something to do with implicit conversions from std::string
to StringData
. I refactored realm::ObjectSchema
in my pod to force conversions to StringData
take place on a copy of the string, and it seems to work now.
Note that the correct fix is to figure out why the member variable is becoming corrupt to begin with, and fixing the underlying bug. That's what I'm working on.
Actually, running Instruments leads me to believe that the entire rlmObjectSchema
(ed: should be objectSchema
) may be invalid at the time the crash happens. (This makes sense; there are other changes in the printed-out description in addition to the bogus string.)
That also makes sense at a higher level: schema changes are being applied after the RLMSchema
& accessors have already been constructed, and schema changes that are being applied (by virtue of transactions being reapplied locally) are causing state to change and we're not reacting entirely defensively for something in those changes.
The realm::ObjectSchema
pointed to by RLMClassInfo.objectSchema
has definitely been destroyed.
The malloc_history
output tells the story:
FREE 0x6080000bc620-0x6080000bc67f [size=96]: thread_100f643c0 | start | main |
NSApplicationMain | -[NSApplication run] | -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] |
_DPSNextEvent | _BlockUntilNextEventMatchingListInModeWithFilter | ReceiveNextEventCommon |
RunCurrentEventLoopInMode | CFRunLoopRunSpecific | __CFRunLoopRun | __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ |
_dispatch_main_queue_callback_4CF | _dispatch_client_callout | _dispatch_call_block_and_release |
__49-[RLMDynamicSchemaLoader schemaDidLoadWithError:]_block_invoke | __53-[RLMDocument loadWithCredentials:completionHandler:]_block_invoke_4 |
-[RLMDocument loadWithError:] | _NSSetLongLongValueAndNotify | -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] |
-[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:usingBlock:] | NSKeyValueDidChange |
NSKeyValueNotifyObserver | -[RLMRealmBrowserWindowController observeValueForKeyPath:ofObject:change:context:] |
-[RLMRealmBrowserWindowController documentSchemaLoaded] | -[RLMRealmBrowserWindowController handleDocumentState] |
-[RLMRealmBrowserWindowController realmDidLoad] | -[RLMTypeOutlineViewController realmDidLoad] |
-[NSOutlineView expandItem:expandChildren:] | -[NSOutlineView _batchExpandItemsWithItemEntries:expandChildren:] |
-[NSTableRowData _doWorkAfterEndUpdates] | -[NSTableRowData _updateVisibleViewsBasedOnUpdateItemsAnimated] |
-[NSTableRowData _updateVisibleViewsBasedOnUpdateItems] | -[NSTableRowData _addRowViewForVisibleRow:withPriorView:] |
-[NSTableRowData _initializeRowView:atRow:] | -[NSTableRowData _addViewsToRowView:atRow:] |
-[NSTableRowData _addViewToRowView:atColumn:row:] | -[NSTableView(NSTableViewViewBased) makeViewForTableColumn:row:] |
-[RLMTypeOutlineViewController outlineView:viewForTableColumn:item:] | -[RLMClassNode cellViewForTableView:] |
-[RLMClassNode instanceCount] | -[RLMClassNode allObjects] | -[RLMRealm allObjects:] | RLMGetObjects |
RLMClassInfo::table() const | -[RLMRealm group] | realm::Realm::read_group() | realm::Realm::begin_read(realm::VersionID) |
realm::Realm::read_schema_from_group_if_needed() | realm::Schema::operator=(realm::Schema&&) |
std::__1::vector<realm::ObjectSchema, std::__1::allocator<realm::ObjectSchema> >::__move_assign(std::__1::vector<realm::ObjectSchema, std::__1::allocator<realm::ObjectSchema> >&, std::__1::integral_constant<bool, true>) |
std::__1::vector<realm::ObjectSchema, std::__1::allocator<realm::ObjectSchema> >::deallocate()
It looks like the realm::ObjectSchema*
that the RLMClassInfo
holds points into the vector that is realm::Schema
. When the schema is re-read by Realm::read_schema_from_group_if_needed()
, it can invalidate any pointers into that object schema vector. It looks like this problem may be specific to the dynamic schema mode used by the Browser, as the non-dynamic schema case doesn't perform the assignment that is invalidating the vector.
I'm not sure what the best way is to address the broader design problem (the Cocoa binding RLMClassInfo
storing a pointer to an element of a vector that can be reallocated when the schema changes), but since no schema change is occurring here it should be straightforward to prevent the crash.
Thanks a lot @bdash, @austinzheng!
https://github.com/realm/realm-object-store/issues/450 works perfectly.
Closing this PR.
While trying different workarounds for #297 found more stable way to reproduce the issue:
__admin
realm from the server browser window.Even if the realm is opened it becomes corrupted. Otherwise there is a crash.
Few notes about what's going on there:
realm-object-server
directory with all cached realms.__admin
realm, waits for the first notification (at this point the schema is loaded) then closes it and opens it again.__admin
realm by double clicking it, Browser does the same as in 2 but with the unique local file path for caching synced realm./cc @tgoyne, @jpsim