Using boost::interprocess::file_lock cause app crash

We have app and notification extension both loading an agent lib to access the same files with read/write/delete operations. To avoid app and notification extension access the file simultaneously, we use boost::interprocess::file_lock when app enters foreground and notification extension didReceive notifications. If extension failed to grab file lock, it will skip loading agent and returns. If app failed to grab file lock, it will start a timer to keep trying while UI will display spinner. Sometimes we saw crash report from our app with:

Exception Type: EXC_CRASH (SIGKILL) Exception Codes: 0x0000000000000000, 0x0000000000000000 Termination Reason: RUNNINGBOARD 3735883980 Triggered by Thread: 0

The code 3735883980 indicate "deadlock" - The operating system terminated the app because it held on to a file lock or SQLite database lock during suspension.

In sysdiagnose, we also see "[osservice<com.apple.InputUI>:1496] check if suspended process is holding locks" in runningboardd process. Does it mean that our app fail to unlock?

We did lock when app enters foreground and unlock when app goes to background. Is there anything we did wrong?

@implementation FileLock

-(id)init
{
    self = [super init];
    if (nil != self)
    {
        // Find empty lock file, created if it doesn't exist.
        FILE *file = fopen(lockFile.c_str(), "a");
        if (file)
        {
            fclose(file);
        }
        m_lock = boost::make_shared<boost::interprocess::file_lock>(lockFile.c_str());
    }
    return self;
}

-(BOOL)lock
{
    return m_lock->try_lock();
}

-(void)unlock
{
    return m_lock->unlock();
}
@end

func applicationDidBecomeActive(_ application: UIApplication) 
{
         if !bFileLockAcquired
        {
              if  fileLock.lock()
              {
                   bFileLockAcquired = true
                   // load agent
              }
             else
             {
                  lockTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { _ in
                     DispatchQueue.main.async {
                           if fileLock.lock()
                           {
                                self.lockTimer?.invalidate()
                                self.lockTimer = nil
                               bFileLockAcquired = true
                                // load agent
                           }
                    }
            })
        }
}
func applicationDidEnterBackground(_ application: UIApplication)
 {
        self.lockTimer?.invalidate()
        self.lockTimer = nil
        fileLock.unlock()
        bFileLockAcquired = false
}

Replies

I found more info in sysdiagnose:

[app<com.example.myApp(B7AB7300-8A17-4C71-88BC-BA3D55AF6666)>:1522] check if suspended process is holding locks

[app<com.example.myApp(B7AB7300-8A17-4C71-88BC-BA3D55AF6666)>:1522] suspended with locked system files: /var/mobile/Containers/Shared/AppGroup/A4A5C452-2871-4AEF-8737-D0436AE7F9F4/.lock

[app<com.example.myApp(B7AB7300-8A17-4C71-88BC-BA3D55AF6666)>:1522] locked files not in allowed directories: /var/mobile/Containers/Data/Application/C9C92475-09F8-445B-B244-F2EF514DC72F/tmp /var/mobile/Containers/Data/Application/C9C92475-09F8-445B-B244-F2EF514DC72F

[app<com.example.myApp(B7AB7300-8A17-4C71-88BC-BA3D55AF6666)>:1522] Terminating with context: <RBSTerminateContext| domain:15 code:0xDEAD10CC explanation:[app<com.example.myApp(B7AB7300-8A17-4C71-88BC-BA3D55AF6666)>:1522] was suspended with locked system files: /var/mobile/Containers/Shared/AppGroup/A4A5C452-2871-4AEF-8737-D0436AE7F9F4/.lock not in allowed directories: /var/mobile/Containers/Data/Application/C9C92475-09F8-445B-B244-F2EF514DC72F/tmp /var/mobile/Containers/Data/Application/C9C92475-09F8-445B-B244-F2EF514DC72F reportType:CrashLog maxTerminationResistance:Absolute>

Should I delete the lock file after unlock it? Or it just complaints that lock file cannot stay in share app group folder?

Maybe didEnterBackground is too late; try unlocking in willResignActive?

You call unlock unconditionally in didEnterBackground; you shouldn’t call it unless you are sure the lock is held. I don’t think that’s the problem though.

How is boost::interprocesss::file_lock implemented on iOS?

P.S. m_lock can be a unique_ptr, no need for make_shared.

It’s hard to offer advice on this without knowing how this third-party library uses Apple APIs. However, I did want to point you to Understanding the exception types in a crash report, which is the official explanation for the 0xdead10cc code.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I believe it is using fcntl(F_SETLK) everywhere except on Windows:

https://github.com/boostorg/interprocess/blob/develop/include/boost/interprocess/sync/file_lock.hpp https://github.com/boostorg/interprocess/blob/develop/include/boost/interprocess/detail/os_file_functions.hpp

   struct ::flock lock;
   lock.l_type    = F_WRLCK;
   lock.l_whence  = SEEK_SET;
   lock.l_start   = 0;
   lock.l_len     = 0;
   int ret = ::fcntl(hnd, F_SETLK, &lock);

Thanks @endecotp, it is good to know. We found the root cause is flag bFileLockAcquired is being reset somewhere else, so it cause timer starts even after successfully locked. Then fileLock.lock called twice.