All the code for the iOS Shared Core is in IosSharedCoreHelpers class in Liblinphone.

In the following sections we will cover the main features of Linphone Shared Core.

Mutual Exclusion between Shared Cores

The mutual exclusion between Shared Cores is made using the UserDefaults. The App Group capability allow the processes to access a shared file system as well as a shared dictionary: the UserDefaults. The data stored in this dictionary is persistent.

We use the UserDefaults to store the information about Shared Core state: if a Main Core is running, if an Executor Core is running, or is being stopped and if no Core is running. Using this system, we can prevent an Executor Shared Core to start if a Main Shared Core is running. But a Main Shared Core that tries to start while an Executor Shared Core is running will stop it before starting.

To be able to stop an Executor Core, a Main Core uses Darwin Notifications from CFNotificationCenter. These are inter-processes notifications. When it wants to start, a Main Core will post a Darwin Notifications to stop the running Executor Cores.

Getting the messages

When calling LinphonePushNotificationMessage *linphone_core_get_new_message_from_callid(lc, call_id) there are two cases: the app in in background or in foreground:

- App in foreground: when the app is in foreground, the Main Shared Core is running so the app extension can't start its Executor Shared Core. The Main Shared Core will receive the new message and write it to the UserDefaults. When the Executor Shared Core try to get the messages, it will see that the Main Core is started and will get the message from the UserDefaults.

The synchronization is implemented using Darwin Notifications: The Executor Core is notified by the Main Core when the new message is available in the UserDefaults.

- App in background: As the app is in background, the Main Shared Core is stopped. That means that the Executor Shared Core will have to start to receive the message. So it calls linphone_core_start() and then linphone_core_iterate() until the message is received.

Performance considerations

Often you receive multiple messages in a short period of time: so, for each message (i.e. push notification), a NotificationService extension instance is created (multiple thread of the same process). Each one of these app extensions will try to start a Shared Core to get it message but only on Core is allowed to run at a time. It takes a long time to start a core, and iterate until the message is received.

We made this optimization to be able to display messages faster to the user: The Executor Shared Core that is running will iterate until he has received all the new messages. Then it will display its message and stop. The next Executor Cores that can process will only need to get the new messages in the UserDefaults. It won't have to start and call linphone_core_iterate().