iOS Inter-process communication

Hi

There is a critical code section between the app and its app extensions that needs to be synchronized and done atomically.

What would be a suitable solution to achieve locking and synchronization between app’s processes? Is there a way to share memory or an operation queue between the app and its app extensions?

Thank you

What sort of app extensions are we talking about here?

This matters because the best approach depends on the lifecycle of your appex, and that lifecycle varies based on the appex type. For example, a share extension is short lived — it only runs while the share UI is showing — whereas a Network Extension provider can run for hours.

Share and Enjoy

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

I think we are talking about the Notification Service Extension.

Thanks.

The Notification Service Extension will run for 30 seconds and only occasionally when a notification is received. Unless your main app is employing a method like map navigation or playing audio, it is almost guaranteed to have been in a suspended, if not terminated, state. Which means you will have no way of directly communicating between the two processes.

Your only viable solution is to use the shared file container to pass data from one to the other. If critical to your use case, you may also need to implement an additional file based mechanism to ensure proper synchronization between the two.

What Gualtier Malde said plus…

One gotcha here is that, if your container app happens to be running, it might not notice changes in the shared container. There are a variety of ways you can resolve this. A simple one is for each party to post a Darwin notification when it wants the other party to be aware of changes.

Share and Enjoy

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

Yes it's the NSE.

Isn't there any other way for in-memory locks?

File based may cause some issues if the NSE process got killed by the OS for any reason while the file is in a locked state by the NSE. So there will be a need to implement another solution to track the state of the file based lock + the notification between the processes. It seems complex and fragile.

Are there any alternatives? Like a shared operation queue or a lock that can be shared between the processes?

Thank you

Isn't there any other way for in-memory locks?

It’s possible to share a lock between two processes. However, that’s not useful on iOS because:

  • You can’t guarantee that the other process is running.

  • Even if it is, it may end up being suspended, and it’d be bad if a process got suspended while holding this lock.

It seems complex and fragile.

Right. But a in-memory solution makes it more complex and fragile, not less.

Keep in mind that working with the file system doesn’t require you to use file locks. For example:

  • It’s easy to implement a producer/consumer system on top of the file system based on the atomic nature of the rename system call.

  • Similarly, if your app wants to provide read-only access to a database to the appex, or vice versa, you can do that on top of rename.

  • Some APIs, like SQLite and hence Core Data, take care of file locks for you.

So, depending on your specific requirements, you may not need anything super complicated.

Share and Enjoy

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

It’s possible to share a lock between two processes

What are the APIs for handling a shared lock between processes?

Even if it is, it may end up being suspended, and it’d be bad if a process got suspended while holding this lock.

What will happen if a process got killed while holding the lock? Will the lock get released or it will be held until all other processes be killed?

How does a shared in-memory lock work in this case?

Similarly, if your app wants to provide read-only access to a database to the appex, or vice versa, you can do that on top of rename

Could you please elaborate on how to it?

Thanks

Could you please elaborate on how to it?

It’s very much like the producer/consumer setup, except with the whole database (-:

Imagine that you app wants to publish a database that your appex accesses read-only. You might do something like this:

  1. Start with the database in a shared app group container. The appex reads that.

  2. When the app has an update, it copies the database to a temporary location. You can use an APFS clone if you want, but a vanilla copy is fine because only the app modifies the file and there can only be one instance of the app running.

  3. And the app modifies that copy of the database.

  4. When it’s done, it uses rename to atomically replace the file in the shared container.

  5. When the appex reads the file, it’ll either see the old or the new contents.

If the appex needs ongoing access to the database, you can have it open the file. The content access via that file descriptor will always be consistent, because the app never modifies that file. Before using the file descriptor, the appex calls fstat on the file descriptor and stat on the file. If the inode numbers match it has the latest. If not, it needs to close and re-open.

Or have the app post a Darwin notification that the appex listens for and refreshes.

Share and Enjoy

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

Using the rename system call is much clearer now thanks.

OTOH, could you please share how an in-memory lock shared between processes work in the case discussed before and what are the APIs for it?

Also, what will happen if a process got killed while holding the lock? Will the lock get released or it will be held until all other processes be killed?

Thank you

@DTS Engineer Kind reminder

iOS Inter-process communication
 
 
Q