Uploaded image for project: 'Realm Cocoa SDK'
  1. Realm Cocoa SDK
  2. RCOCOA-683

Intermittent crash while running Realm in a background thread after upgrading to 5.0.3

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Critical - P2 Critical - P2
    • None
    • Affects Version/s: None
    • Component/s: None

      Goals

      <!--- What do you want to achieve? -->
      Run Realm write code that runs in a background queue without crashing, as was possible in Realm 4.4.0. After upgrading to Realm 5.0.3, the sample code below crashes intermittently, but frequently enough to become a problem.

      In our project, we have a Realm extension that implements several helper methods, one of which closely resembles the code sample found in this Realm documentation:
      https://realm.io/docs/cookbook/swift/object-to-background/

      I’m not sure if it’s related, but after coming across #6555, #6559, #6574, I have attempted implementing the fix from #6576, but the same crash still occurs.
      EXC_BAD_ACCESS KERN_INVALID_ADDRESS

      Expected Results

      <!--- What did you expect to happen? -->
      No crash, as in Realm 4.4.0.

      Actual Results

      <!--- What happened instead?
      e.g. the stack trace of a crash
      -->

      Crashed: job_progress_queue
      EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x00000003d7fd8a7c
      0  Realm                          0x1064071f0 realm::Array::init_from_mem(realm::MemRef) + 12
      1  Realm                          0x1064a8294 realm::Group::attach(unsigned long, bool, bool) + 196
      2  Realm                          0x1064af054 realm::Group::advance_transact(unsigned long, unsigned long, realm::_impl::NoCopyInputStream&, bool) + 316
      3  Realm                          0x10635a9dc void realm::Transaction::rollback_and_continue_as_read<realm::_impl::NullInstructionObserver>(realm::_impl::NullInstructionObserver*) + 588
      4  Realm                          0x10635a030 realm::Transaction::rollback_and_continue_as_read() + 28
      5  Realm                          0x106359e8c realm::_impl::transaction::cancel(realm::Transaction&, realm::BindingContext*) + 140
      6  Realm                          0x1062b3c5c realm::Realm::cancel_transaction() + 264
      7  Realm                          0x1061fedd0 -[RLMRealm cancelWriteTransaction] + 36
      8  Realm                          0x1061ff3c0 -[RLMRealm dealloc] + 80
      9  libobjc.A.dylib                0x1bc9d1358 AutoreleasePoolPage::releaseUntil(objc_object**) + 184
      10 libobjc.A.dylib                0x1bc9d1244 objc_autoreleasePoolPop + 232
      11 libswiftObjectiveC.dylib       0x1f37ccc2c autoreleasepool<A>(invoking:) + 76
      12 MyJobApp                       0x101ad1b98 closure #1 in static Realm.runAsync(in:errorHandler:block:) + 15 (Realm+Helpers.swift:15)
      13 MyJobApp                       0x100f97360 thunk for @escaping @callee_guaranteed () -> () + 56 (<compiler-generated>:56)
      14 libdispatch.dylib              0x1bc93eec4 _dispatch_call_block_and_release + 32
      15 libdispatch.dylib              0x1bc94033c _dispatch_client_callout + 20
      16 libdispatch.dylib              0x1bc94685c _dispatch_lane_serial_drain + 568
      17 libdispatch.dylib              0x1bc947290 _dispatch_lane_invoke + 400
      18 libdispatch.dylib              0x1bc950928 _dispatch_workloop_worker_thread + 584
      19 libsystem_pthread.dylib        0x1bc9a7714 _pthread_wqthread + 276
      20 libsystem_pthread.dylib        0x1bc9ad9c8 start_wqthread + 8
      

      Steps to Reproduce

      <!--- What are steps we can follow to reproduce this issue? -->
      See code sample below, but this is basically an intermittent but fairly frequent crash. In our code, we instantiate a Job object multiple times, as needed, to perform different tasks that we need completed.
      For the purposes of the code sample below, assume code similar to the following is run multiple times:

      let job: Job = Job()
      job.start()
      

      Code Sample

      <!---
      Provide a code sample or test case that highlights the issue.
      If relevant, include your model definitions.
      For larger code samples, links to external gists/repositories are preferred.
      Alternatively share confidentially via mail to help@realm.io.
      Full Xcode projects that we can compile ourselves are ideal!
      -->

      // Job.swift
      public class Job: Object {
          
          @objc public dynamic var id: String = UUID().uuidString
          @objc public dynamic var progress: Double = 0.0
          
          public let tasks: List<Task> = .init()
          @objc public dynamic var runningTask: Task?
          
          public override static func primaryKey() -> String? {
              return "id"
          }
          
          private let jobProgressQueue: DispatchQueue = DispatchQueue(
              label: "job_progress_queue",
              qos: .utility,
              attributes: [],
              autoreleaseFrequency: .inherit,
              target: nil)
          
          public func start() {
              let primaryKey: String = self.id
              let jobProgressQueue: DispatchQueue = self.jobProgressQueue
              //
              // The specifics of `runningTask` are not important, other than the fact that
              // the `progressHandler` block is called frequently to update the caller on
              // its progress from another DispatchQueue.
              //
              runningTask.run(progressHandler: { (progress: Double) in
                  Job.update(primaryKey, progress: progress, in: jobProgressQueue)
              })
          }
          
          public static func update(_ primaryKey: String, progress: Double, in dispatchQueue: DispatchQueue) {
              Realm.runAsync(in: dispatchQueue) { (realm: Realm) in
                  guard let job: Job = realm.object(ofType: Job.self, forPrimaryKey: primaryKey) else { return }
                  try? realm.write {
                      guard !job.isInvalidated else { return }
                      job.progress = progress
                  }
              }
          }
          
      }
      
      // Realm+Helpers.swift
      extension Realm {
          
          public static func runAsync(in dispatchQueue: DispatchQueue, errorHandler: @escaping (_ error: Swift.Error) -> Void = { _ in return }, block: @escaping (Realm) -> Void) {
              dispatchQueue.async {
                  autoreleasepool { // <--- Thread 49: EXC_BAD_ACCESS (code=1, address=0x3d7fd8a7c)
                      do {
                          let realm: Realm = try Realm()
                          block(realm)
                      } catch {
                          errorHandler(error)
                      }
                  }
              }
          }
          
      }
      

      Version of Realm and Tooling

      <!---
      In the CONTRIBUTING guidelines, you will find a script,
      which will help determining some of these versions.
      -->
      Realm framework version: 5.0.3

      Realm Object Server version: N/A

      Xcode version: 11.5 (11E608c)

      iOS version: 13.5 (17F75)

      Dependency manager + version: CocoaPods 1.9.3

            Assignee:
            pavel.yakimenko@mongodb.com Pavel Yakimenko (Inactive)
            Reporter:
            unitosyncbot Unito Sync Bot
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: