realm-swift: Started experiencing SIGKILL crashes very frequently after iOS 15 launch

How frequently does the bug occur?

Sometimes

Description

We’re having a ton of crash reports appearing after iOS 15 release that have not been present before. All of them are SIGKILL crashes and stack traces have nothing nut realm activity. Is this something that is a known issue?

Stacktrace & log output

Multiple different stack traces

Can you reproduce the bug?

Yes, sometimes

Reproduction Steps

The iOS will kill the app eventually

Version

10.7.6

What SDK flavour are you using?

Local Database only

Are you using encryption?

Yes, using encryption

Platform OS and version(s)

iOS 15

Build environment

Xcode version: … Dependency manager and version: …

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 31 (7 by maintainers)

Most upvoted comments

I’ve understood and solved the issue for our app:

TL;DR: requesting a RLMRealm via any of the realmWithConfiguration constructors (ex let realm = try RLMRealm.init(configuration: configuration) must always be wrapped in a background task, since if the app is suspended while that constructor is being executed, the app will crash with a SIGKILL on an idle main thread. Any usage of that RLMRealm must also be wrapped.

Explanation: The issue is that (as of iOS 15) if you are holding a file lock in a shared app container when the app is suspended, the OS kills the app. This is regardless of other running processes. It’s a protection mechanism to prevent deadlocks in other processes that might access the file. (This information comes from here (https://developer.apple.com/forums/thread/655225?answerId=632433022#632433022) and here (https://githubhot.com/repo/realm/realm-swift/issues/7649))

I was able to repro the crash behaviour with a simple prototype app, requesting a RLMRealm in a shared container, by inserting a sleep between the application going to background, requesting the RLMRealm, and being suspended. With the right sleep timing, which forces the RLMRealm to be created as the app is being suspended, the crash is 100% reproducible.

We solved this issue with the following RLMRealm wrapper:

@objc class BGSafeRealm: RLMRealm {

    var bgTask: BackgroundTask? // Custom implementation that handles UIApplication.shared.beginBackgroundTask overhead

    @objc static func safeRealm(with configuration: RLMRealmConfiguration) throws -> BGSafeRealm {
        let bgTask = <BGTaskAbstraction>.startBackgroundTask(withName: description != nil ? description! : "")

        do {
            let realm = try BGSafeRealm.init(configuration: configuration)
            realm.bgTask = bgTask
            return realm
        } catch {
            bgTask?.stop()
            throw(error)
        }
    }

    deinit {
        // We need to end the background task *after* realm does whatever it needs to do to cleanup. Dispatch.
        let captureTask = bgTask
        DispatchQueue.global().async {
            captureTask?.stop()
        }
    }
}

It can then be used as follows: the background task lasts until the RLMRealm instance is deallocated:

    RLMRealm *realm = [MXBGSafeRealm safeRealmWith:self.realmConfiguration description:@"Some task" error:nil];

    [realm transactionWithBlock:^{
       // Do something with the realm object
    }];

Important note: if you don’t wrap up your Realm activity before the background task expiration handler is called (~30 seconds after backgrounding), you’ll crash, for the same reason as before. This gives you time to wrap up what you are doing, but doesn’t fix that you can’t hold a file lock when the app is suspended.