title: mutex lock failed: Invalid argument from autoreleasepool
body: <p>I have all methods using <code>autoreleasepool</code> to prevent memory leak from <code>realm</code> database.
  And there's a background thread looper which may be doing some realm operations.
  So the <code>autoreleasepool</code> is necessary.
  All these methods are designed as Singleton pattern.
  The following method is one of them.</p>
  <pre><code> func deleteMyArticle(by owner: String) throws {
          try autoreleasepool {
              let logHelper = LogHelper(subsystem: &quot;ArticleLogService&quot;, category: &quot;deleteMyArticle&quot;)
  
              let realm = try Realm()
              let results = ArticleLogService.shared.getMyArticleLogs(realm, by: owner, distinct: false)
                  try realm.write {
                      realm.delete(results)
                  }
  
          }
      }
  </code></pre>
  <p>But it throws this exception more than 300 times a week and it may freezes my app randomly which I can't reproduce on my test device.</p>
  <pre><code>Fatal Exception: std::__1::system_error
  mutex lock failed: Invalid argument
  Fatal Exception: std::__1::system_error
  
  com.google.firebase.crashlytics.ios.exception
  0x0000000000000000
  Crashed: com.google.firebase.crashlytics.ios.exception
  0  FirebaseCrashlytics            0x104b5c7c0 FIRCLSProcessRecordAllThreads + 180
  1  FirebaseCrashlytics            0x104b5cba4 FIRCLSProcessRecordAllThreads + 1176
  2  FirebaseCrashlytics            0x104b54174 FIRCLSHandler + 48
  3  FirebaseCrashlytics            0x104b4fb34 __FIRCLSExceptionRecord_block_invoke + 92
  4  libdispatch.dylib              0x18f2c2280 _dispatch_client_callout + 16
  5  libdispatch.dylib              0x18f271b8c _dispatch_lane_barrier_sync_invoke_and_complete + 56
  6  FirebaseCrashlytics            0x104b4f2cc FIRCLSExceptionRecord + 212
  7  FirebaseCrashlytics            0x104b4ef6c FIRCLSTerminateHandler() + 592
  8  libc++abi.dylib                0x1a3aee154 std::__terminate(void (*)()) + 16
  9  libc++abi.dylib                0x1a3aee0ec std::terminate() + 44
  10 Realm                          0x105672190 __clang_call_terminate + 10
  11 Realm                          0x105a199f0 realm::Group::detach() + 118
  12 Realm                          0x105a236c4 realm::Transaction::close() + 60
  13 Realm                          0x105a26350 TransactionDeleter(realm::Transaction*) + 20
  14 Realm                          0x105a2b360 std::__1::__shared_ptr_pointer&lt;realm::Transaction*, void (*)(realm::Transaction*), std::__1::allocator&lt;realm::Transaction&gt; &gt;::__on_zero_shared() + 20
  15 Realm                          0x105674da0 std::__1::shared_ptr&lt;realm::app::App&gt;::~shared_ptr() + 64
  16 Realm                          0x1057d6980 realm::Realm::~Realm() + 96
  17 Realm                          0x105674da0 std::__1::shared_ptr&lt;realm::app::App&gt;::~shared_ptr() + 64
  18 libobjc.A.dylib                0x1a39f1ae0 object_cxxDestructFromClass(objc_object*, objc_class*) + 112
  19 libobjc.A.dylib                0x1a3a078a4 objc_destructInstance + 88
  20 libobjc.A.dylib                0x1a3a0e6ec _objc_rootDealloc + 52
  21 Realm                          0x1057b58b0 -[RLMRealm dealloc] + 92
  22 libobjc.A.dylib                0x1a3a11574 AutoreleasePoolPage::releaseUntil(objc_object**) + 200
  23 libobjc.A.dylib                0x1a3a11450 objc_autoreleasePoolPop + 208
  24 libdispatch.dylib              0x18f2c2260 _dispatch_last_resort_autorelease_pool_pop + 40
  25 libdispatch.dylib              0x18f26b908 _dispatch_lane_invoke$VARIANT$mp + 520
  26 libdispatch.dylib              0x18f275338 _dispatch_workloop_worker_thread + 712
  27 libsystem_pthread.dylib        0x1d7dbf5a4 _pthread_wqthread + 272
  28 libsystem_pthread.dylib        0x1d7dc2874 start_wqthread + 8
  
  </code></pre>
  <p>Is there any issue if I'm using <code>autoreleasepool</code> on the main thread?
  According to firebase, the app state are 100% in the background state when these issues occured.
  What's the cause of this issue?</p>
  <p>RealmSwift 10.1.4
  XCode 12.4
  iOS 10-14</p>
  <p><strong>UPDATE</strong></p>
  <p>There is a <code>realm.invalidate()</code> in <code>AppDelegate.applicationWillTerminate</code>.</p>
  <pre><code>func applicationWillTerminate(_ application: UIApplication) {
      RealmCacheHelper.shared.invalidateSemaphore.wait()
      defer {
          RealmCacheHelper.shared.invalidateSemaphore.signal()
      }
  
      if let realm = try? Realm() {
          realm.invalidate()
      }
  }
  </code></pre>
  <p>And an insert operation(worker thread) of realm in <code>AppDelegate.applicationDidEnterBackground</code> to save caches which are loaded from web services.</p>
  <pre><code>func applicationDidEnterBackground(_ application: UIApplication) {
      // insert(caches:);
  }
  
  public func insert(caches c: [String:[MemoryCache]]?){
      transactionQueue.async {
          do {
              RealmCacheHelper.shared.invalidateSemaphore.wait()
  
              let logHelper = LogHelper(subsystem: &quot;PageCacheService&quot;, category: &quot;insertCache&quot;)
  
              var backgroundTaskID: UIBackgroundTaskIdentifier?
  
              // Request the task assertion and save the ID.
              backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: &quot;PageCache insertion task.&quot;) {
                  logHelper.w(&quot;Background task expired.&quot;)
                  // End the task if time expires.
                  UIApplication.shared.endBackgroundTask(backgroundTaskID!)
                  backgroundTaskID = .invalid
              }
  
              defer{
                  RealmCacheHelper.shared.invalidateSemaphore.signal()
                  logHelper.i(&quot;Background task finished.&quot;)
                  UIApplication.shared.endBackgroundTask(backgroundTaskID!)
                  backgroundTaskID = .invalid
              }
  
              // realm.write()
          }
          catch{}
      }
  }
  </code></pre>
  <p>All of these are used to prevent the size of realm increases drastically. The reason to save caches in <code>applicationDidEnterBackground</code> is to decrease the operation frequency of realm.
  There was a <code>autoreleasepool</code> in the <code>transactionQueue</code>. But it crashed a lot too, so I removed it few weeks ago.
  I think these <code>realm</code> operations in the background state may be the cause of <code>mutex lock failed</code> issue. But I have to do these task in the background state.
  There may be 50 or more json strings to be saved.</p>
  <p><strong>UPDATE</strong></p>
  <p>@Jay I have everything you need in my code, I just discarded these things to make it looks more easier to read. The try-catch is in the <code>//realm.write</code> comment. And it didn't catch any error because the problem was not thrown by this method. <code>RealmCacheHelper</code> is a singleton object which holds the same <code>invalidateSemaphore</code> to make sure there's nothing running in the background when it's calling <code>realm.invalidate()</code>.
  Most of your concerns are not related to this issue. That's why I removed them from the code snippet.</p>
  <p>This is the nil checker.</p>
  <pre><code>   guard let caches = c, !caches.isEmpty else {
          let logHelper = LogHelper(subsystem: &quot;PageCacheService&quot;, category: &quot;insertCache&quot;)
          logHelper.i(&quot;Memory cache is empty.&quot;)
          return
      }
  </code></pre>
  <p>This is why I need try-catch.</p>
  <pre><code>    let _realm = try RealmCacheHelper.shared.realm()
  
      _realm.beginWrite()
  
      for memoryCache in memoryCaches {
          let mid = PageCacheService.shared.getWorkerThreadMID(_realm)
  
          let cache = MatrixCache.newInstance(
                  id: mid,
                  controllerId: memoryCache.controllerId,
                  page: memoryCache.page)
  
          cache.content = memoryCache.matrixJson
          cache.font = memoryCache.fontJson
  
          _realm.add(cache)
      }
  
      try _realm.commitWrite()
  </code></pre>
  <pre><code>    func getWorkerThreadMID(_ realm: Realm) -&gt; Int {
          semaphore.wait()
          defer {
              semaphore.signal()
          }
  
          _mid = autoIncrement(
                  realm: realm,
                  id: _mid,
                  realmObject: MATRIX_CLASS,
                  primaryKey: MatrixCache.Column.PrimaryKey.rawValue
          )
  
          return _mid;
      }
  </code></pre>
  <pre><code>    internal func autoIncrement(realm: Realm, id: Int, realmObject: Object.Type, primaryKey: String) -&gt; Int{
          if( id == -1 ){
              return ( realm.objects( realmObject ).max(ofProperty: primaryKey) as Int? ?? 0 ) + 1
          }
          
          return id + 1
      }
  </code></pre>
  <pre><code>    // RealmCacheHelper.shared.realm()
      func realm() throws -&gt; Realm {
          return try Realm(configuration: config)
      }
  
      var config: Realm.Configuration {
  
          var _config = Realm.Configuration(
                  // Set the new schema version. This must be greater than the previously used
                  // version (if you've never set a schema version before, the version is 0).
                  schemaVersion: RealmCacheHelper.schemaVersion,
                  // Set the block which will be called automatically when opening a Realm with
                  // a schema version lower than the one set above
                  migrationBlock: { migration, oldSchemaVersion in
                      // We haven’t migrated anything yet, so oldSchemaVersion == 0
                      if (oldSchemaVersion &lt; RealmCacheHelper.schemaVersion) {
                          // Nothing to do!
                          // Realm will automatically detect new properties and removed properties
                          // And will update the schema on disk automatically
                      }
                  },
                  deleteRealmIfMigrationNeeded: false
          )
  
          // let it crash if the fileURL is nil.
          _config.fileURL = _config.fileURL!.deletingLastPathComponent().appendingPathComponent(RealmCacheHelper.realmFileName)
  
          #if DEBUG
          _config.maximumNumberOfActiveVersions = 5
          #endif
  
          return _config
      }
  </code></pre>
  <p>And <code>LogHelper</code> is just a convenient class to use <code>CocoaLumberjack</code>.
  v for verbose, i for info, w for warn, d for debug, e for error.</p>
  <pre><code>    let headerV: String
      let headerD: String
      let headerI: String
      let headerW: String
      let headerE: String
  
      init(subsystem: String, category: String){
          headerV = &quot;[&quot; + subsystem + &quot;/&quot; + category + &quot;] [VERBOSE] &quot;
          headerD = &quot;[&quot; + subsystem + &quot;/&quot; + category + &quot;] [DEBUG] &quot;
          headerI = &quot;[&quot; + subsystem + &quot;/&quot; + category + &quot;] [INFO] &quot;
          headerW = &quot;[&quot; + subsystem + &quot;/&quot; + category + &quot;] [WARN] &quot;
          headerE = &quot;[&quot; + subsystem + &quot;/&quot; + category + &quot;] [ERROR] &quot;
      }
  
      public func v(_ message: String){
          DDLogVerbose(headerV + message)
      }
  </code></pre>
  <pre><code>    // ArticleLogService.shared.getMyArticleLogs(realm, by: owner, distinct: false)
      func getMyArticleLogs(_ realm: Realm, by owner: String, distinct: Bool) -&gt; Results&lt;ArticleLog&gt; {
          if distinct {
              return realm.objects(ENTITY_CLASS).filter(&quot;\(ArticleLog.Column.Owner.rawValue) = '\(owner)' AND \(ArticleLog.Column.Author.rawValue) contains[cd] %@&quot;, owner).sorted(byKeyPath: ArticleLog.Column.CreatedDate.rawValue, ascending: false).distinct(by: [ArticleLog.Column.BoardTitle.rawValue, ArticleLog.Column.Aid.rawValue])
          }
          else {
              return realm.objects(ENTITY_CLASS).filter(&quot;\(ArticleLog.Column.Owner.rawValue) = '\(owner)' AND \(ArticleLog.Column.Author.rawValue) contains[cd] %@&quot;, owner).sorted(byKeyPath: ArticleLog.Column.CreatedDate.rawValue, ascending: false)
          }
      }
  </code></pre>
  <p>It do throw <code>mutex lock failed</code> randomly only if I add <code>autoreleasepool</code> in the <code>insert</code> method.
  So there must be something wrong here while running this method in the background state.
  But everything is the same as other methods running on the main thread.</p>