ios, core: better notifications processing to avoid contention for database (#3485)

* core: forward notifications about message processing (for iOS notifications)

* simplexmq

* the option to keep database key, to allow re-opening the database

* export new init with keepKey and reopen DB api

* stop remote ctrl when suspending chat

* ios: close/re-open db on suspend/activate

* allow activating chat without restoring (for NSE)

* update NSE to suspend/activate (does not work)

* simplexmq

* suspend chat and close database when last notification in the process is processed

* stop reading notifications on message markers

* replace async stream with cancellable concurrent queue

* better synchronization of app and NSE

* remove outside of task

* remove unused var

* whitespace

* more debug logging, handle cancelled read after dequeue

* comments

* more comments
This commit is contained in:
Evgeny Poberezkin authored and GitHub committed 2023-12-09 21:59:40 +00:00
1 parent 2f7632a70f
commit d3059afc99
29 files changed
+661 -224

No files matched your search

+27 -10
View File
@@ -18,6 +18,8 @@ let bgSuspendTimeout: Int = 5 // seconds
let terminationTimeout: Int = 3 // seconds
let activationDelay: Double = 1.5 // seconds
private func _suspendChat(timeout: Int) {
// this is a redundant check to prevent logical errors, like the one fixed in this PR
let state = appStateGroupDefault.get()
@@ -47,8 +49,6 @@ func suspendBgRefresh() {
}
}
private var terminating = false
func terminateChat() {
logger.debug("terminateChat")
suspendLockQueue.sync {
@@ -64,7 +64,6 @@ func terminateChat() {
case .stopped:
chatCloseStore()
default:
terminating = true
// the store will be closed in _chatSuspended when event is received
_suspendChat(timeout: terminationTimeout)
}
@@ -85,14 +84,17 @@ private func _chatSuspended() {
if ChatModel.shared.chatRunning == true {
ChatReceiver.shared.stop()
}
if terminating {
chatCloseStore()
chatCloseStore()
}
func setAppState(_ appState: AppState) {
suspendLockQueue.sync {
appStateGroupDefault.set(appState)
}
}
func activateChat(appState: AppState = .active) {
logger.debug("DEBUGGING: activateChat")
terminating = false
suspendLockQueue.sync {
appStateGroupDefault.set(appState)
if ChatModel.ok { apiActivateChat() }
@@ -101,7 +103,6 @@ func activateChat(appState: AppState = .active) {
}
func initChatAndMigrate(refreshInvitations: Bool = true) {
terminating = false
let m = ChatModel.shared
if (!m.chatInitialized) {
do {
@@ -113,16 +114,32 @@ func initChatAndMigrate(refreshInvitations: Bool = true) {
}
}
func startChatAndActivate() {
terminating = false
func startChatAndActivate(dispatchQueue: DispatchQueue = DispatchQueue.main, _ completion: @escaping () -> Void) {
logger.debug("DEBUGGING: startChatAndActivate")
if ChatModel.shared.chatRunning == true {
ChatReceiver.shared.start()
logger.debug("DEBUGGING: startChatAndActivate: after ChatReceiver.shared.start")
}
if .active != appStateGroupDefault.get() {
if .active == appStateGroupDefault.get() {
completion()
} else if nseStateGroupDefault.get().inactive {
activate()
} else {
suspendLockQueue.sync {
appStateGroupDefault.set(.activating)
}
// TODO can be replaced with Mach messenger to notify the NSE to terminate and continue after reply, with timeout
dispatchQueue.asyncAfter(deadline: .now() + activationDelay) {
if appStateGroupDefault.get() == .activating {
activate()
}
}
}
func activate() {
logger.debug("DEBUGGING: startChatAndActivate: before activateChat")
activateChat()
completion()
logger.debug("DEBUGGING: startChatAndActivate: after activateChat")
}
}