realm-swift: Crash: Cannot create asynchronous query while in a write transaction

Goals

Avoid crashes.

Expected Results

Crashes do not happen. Also I want to know what kind of code can lead to this crash.

Actual Results

Possible related threads are below.

Fatal Exception: realm::InvalidTransactionException: Cannot create asynchronous query while in a write transaction



Crashed: com.twitter.crashlytics.ios.exception
0  ??????                         0xfefe19 CLSProcessRecordAllThreads + 17612313
1  ??????                         0xfefe19 CLSProcessRecordAllThreads + 17612313
2  ??????                         0xfefd11 CLSProcessRecordAllThreads + 17612049
3  ??????                         0xfe41ef CLSHandler + 17564143
4  ??????                         0xfee787 __CLSExceptionRecord_block_invoke + 17606535
5  libdispatch.dylib              0x3a3f47a7 _dispatch_client_callout + 22
6  libdispatch.dylib              0x3a3fbac9 _dispatch_barrier_sync_f_invoke + 48
7  ??????                         0xfee189 CLSExceptionRecord + 17605001
8  ??????                         0xfedba3 CLSTerminateHandler() + 17603491
9  libc++abi.dylib                0x397cdde3 std::__terminate(void (*)()) + 78
10 libc++abi.dylib                0x397cd8af __cxa_rethrow + 102
11 libobjc.A.dylib                0x39e94dd3 objc_exception_rethrow + 42
12 CoreFoundation                 0x2c5ec45d CFRunLoopRunSpecific + 632
13 CoreFoundation                 0x2c5ec1d3 CFRunLoopRunInMode + 106
14 GraphicsServices               0x339ea0a9 GSEventRunModal + 136
15 UIKit                          0x2fbfa7b1 UIApplicationMain + 1440
16 ??????                         0xefea7 main (main.m:14)
17 libdyld.dylib                  0x3a414aaf start + 2


RLMRealm notification listener
0  libsystem_kernel.dylib         0x3a4caa18 kevent + 24
1  Realm                          0x17aea25 realm::_impl::ExternalCommitHelper::listen() (external_commit_helper.cpp:200)
2  Realm                          0x17aed0b std::__1::__async_func<realm::_impl::ExternalCommitHelper::ExternalCommitHelper(realm::_impl::RealmCoordinator&)::$_0>::operator()() (external_commit_helper.cpp:159)
3  Realm                          0x17aec23 std::__1::__async_assoc_state<void, std::__1::__async_func<realm::_impl::ExternalCommitHelper::ExternalCommitHelper(realm::_impl::RealmCoordinator&)::$_0> >::__execute() (future:1026)
4  Realm                          0x17aeeaf std::__1::__thread_proxy<std::__1::tuple<void (std::__1::__async_assoc_state<void, std::__1::__async_func<realm::_impl::ExternalCommitHelper::ExternalCommitHelper(realm::_impl::RealmCoordinator&)::$_0> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<realm::_impl::ExternalCommitHelper::ExternalCommitHelper(realm::_impl::RealmCoordinator&)::$_0> >*> >(void*, void*) (__functional_base:383)
5  libsystem_pthread.dylib        0x3a557e93 _pthread_body + 138
6  libsystem_pthread.dylib        0x3a557e07 _pthread_start + 118
7  libsystem_pthread.dylib        0x3a555b90 thread_start + 8


com.?????.???.realm
0  libsystem_kernel.dylib         0x3a4ca354 flock + 8
1  Realm                          0x1880891 realm::util::File::lock(bool, bool) + 124
2  Realm                          0x1948f1f realm::SharedGroup::do_begin_write() + 118
3  Realm                          0x1877c61 auto realm::_impl::transaction::begin(realm::SharedGroup&, realm::BindingContext*, bool)::$_1::operator()<(anonymous namespace)::TransactLogValidator>((anonymous namespace)::TransactLogValidator&&) const (group_shared.hpp:965)
4  Realm                          0x1872805 realm::_impl::transaction::begin(realm::SharedGroup&, realm::BindingContext*, bool) (vector:449)
5  Realm                          0x18704ed realm::Realm::begin_transaction() (shared_realm.cpp:348)
6  Realm                          0x185d083 -[RLMRealm beginWriteTransaction] (RLMRealm.mm:475)
7  ??????                         0xab44b __54-[BKLResourceViewLogManager updateResourceLogSuccess:]_block_invoke (BKLResourceViewLogManager.m:149)
8  libdispatch.dylib              0x3a3f47bb _dispatch_call_block_and_release + 10
9  libdispatch.dylib              0x3a3fc5b1 _dispatch_queue_drain + 952
10 libdispatch.dylib              0x3a3f6f85 _dispatch_queue_invoke + 84
11 libdispatch.dylib              0x3a3fdb9b _dispatch_root_queue_drain + 338
12 libdispatch.dylib              0x3a3fecd7 _dispatch_worker_thread3 + 94
13 libsystem_pthread.dylib        0x3a555e31 _pthread_wqthread + 668
14 libsystem_pthread.dylib        0x3a555b84 start_wqthread + 8

Steps to Reproduce

I do not know how to reproduce it.

I haven’t got anything helpful after searching all the issues and in stackoverflow.com. And I also searched the crash message Cannot create asynchronous query while in a write transaction in realm source code but found nothing. It’s not a new issue and I first found this crash about one month ago.

Code Sample

I’ve read the doc and cannot find what’s wrong with my code. I know how to avoid common problems like nested transactions. But this one is killing me.

Is it possible to trace backwards? I mean find out which line of code sends this message Cannot create asynchronous query while in a write transaction and then write a stable demo to reproduce it. I may fix the problems in my code by comparing the demo with my code.

Here’s my code in thread com.?????.???.realm. Thanks!

- (void)updateResourceLogSuccess:(NSArray<BKLResourceViewLogRO *> *)results {
    [self changeLogs:results toStatus:BKLResourceViewLogROFinish];

    RLMResults<BKLResourceViewLogRO *> *finishResults = [BKLResourceViewLogRO objectsWhere:@"status = %ld", BKLResourceViewLogROFinish];
    if (finishResults.count > 0) {
        [[RLMRealm defaultRealm] transactionWithBlock:^{
            [[RLMRealm defaultRealm] deleteObjects:finishResults];
        }];
    }
}


- (void)changeLogs:(NSArray<BKLResourceViewLogRO *> *)logs
          toStatus:(BKLResourceViewLogROStatus)status {
    if (logs.count == 0) {
        return;
    }

    RLMRealm *realm = [RLMRealm defaultRealm];
    [realm beginWriteTransaction];

    for (BKLResourceViewLogRO *log in logs) {
        if (log.invalidated) {
            NSAssert(log.invalidated, @"Oops!");
            [realm cancelWriteTransaction];
            return;
        }

        log.status = status;
    }

    [realm commitWriteTransaction];
}

Version of Realm and Tooling

ProductName:    Mac OS X
ProductVersion: 10.12
BuildVersion:   16A323

/Applications/Xcode.app/Contents/Developer
Xcode 8.0
Build version 8A218a

/usr/local/bin/pod
1.0.1


/bin/bash
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)

/usr/local/bin/carthage
0.18
github "realm/realm-cocoa" "v2.0.2"

/usr/bin/git
git version 2.8.4 (Apple Git-73)

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 3
  • Comments: 22 (6 by maintainers)

Most upvoted comments

Oh. My bad! I fixed it by storing realm in a local variable. Thanks!

Edit: I wrote this function in my Realm extension to handle this better:

func write(withoutNotifying: [NotificationToken], f: (Realm) -> Void) throws {
    beginWrite()
    f(self)
    try commitWrite(withoutNotifying: withoutNotifying)
}    

This exception is thrown when adding a notification block from within a write transaction. Do you call addNotificationBlock() from anywhere in your code?

by the way, I have reproduced our problem and cannot find the reason. realm inWriteTransaction returns NO just before beginTransaction, but than it fails with exception and realm inWriteTransaction in that moment returns YES. And there is no other threads with realm write transactions in that moment

I solved this.

Hello Guys, I recently experimented this issue after update Realm, and I figured out I was posting a notification: NotificationCenter.default.post(name: “notificationName”, object: nil) just withing a notification block, and here was my code:

notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
            guard let tableView = self?.tableView else { return }
            switch changes {
                
            case .initial:
                
                tableView.reloadData()
                break
            case .update(_, _, _, _):
                
                NotificationCenter.default.post(name: "notificationName", object: nil)
                break
                
            case .error(let error):

                fatalError("\(error)")
                
                break
            }
        }

and, I was subscribed to hear that notification in order to make a query of the same kind of objects being updated. I removed that bunch of code and the issue was solved. I hope this can help you.