Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use core background fetch, use .default qos #2071

Merged
merged 4 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions DcCore/DcCore/DC/events.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,6 @@ public class DcEventHandler {
}

case DC_EVENT_CONNECTIVITY_CHANGED:
if let sem = dcAccounts.fetchSemaphore, dcAccounts.isAllWorkDone() {
sem.signal()
}
if accountId != dcAccounts.getSelected().id {
return
}
Expand All @@ -167,6 +164,11 @@ public class DcEventHandler {
NotificationCenter.default.post(name: eventConnectivityChanged, object: nil)
}

case DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE:
r10s marked this conversation as resolved.
Show resolved Hide resolved
if let sem = dcAccounts.fetchSemaphore {
sem.signal()
}

case DC_EVENT_WEBXDC_STATUS_UPDATE:
if accountId != dcAccounts.getSelected().id {
return
Expand Down
2 changes: 1 addition & 1 deletion DcShare/Helper/ShareAttachment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class ShareAttachment {
_ = self.addDcMsg(path: url.relativePath, viewType: DC_MSG_VIDEO)
self.delegate?.onAttachmentChanged()
if self.imageThumbnail == nil {
DispatchQueue.global(qos: .background).async {
DispatchQueue.global().async {
self.imageThumbnail = DcUtils.generateThumbnailFromVideo(url: url)
DispatchQueue.main.async {
self.delegate?.onThumbnailChanged()
Expand Down
39 changes: 18 additions & 21 deletions deltachat-ios/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// maybeNetwork() shall not be called in ui thread;
// Reachability::reachabilityChanged uses DispatchQueue.main.async only
logger.info("network: reachable \(reachability.connection.description)")
DispatchQueue.global(qos: .background).async { [weak self] in
DispatchQueue.global().async { [weak self] in
guard let self else { return }
self.dcAccounts.maybeNetwork()
if self.notifyToken == nil &&
Expand All @@ -145,7 +145,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD

reachability.whenUnreachable = { _ in
logger.info("network: not reachable")
DispatchQueue.global(qos: .background).async { [weak self] in
DispatchQueue.global().async { [weak self] in
self?.dcAccounts.maybeNetworkLost()
}
}
Expand Down Expand Up @@ -233,7 +233,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
applicationInForeground = true
dcAccounts.startIo()

DispatchQueue.global(qos: .background).async { [weak self] in
DispatchQueue.global().async { [weak self] in
guard let self else { return }
if let reachability = self.reachability {
if reachability.connection != .unavailable {
Expand Down Expand Up @@ -484,35 +484,32 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
pushToDebugArray("1")

// move work to non-main thread to not block UI (otherwise, in case we get suspended, the app is blocked totally)
// (we are using `qos: default` as `qos: .background` or `main.asyncAfter` may be delayed by tens of minutes)
// (we are using `qos: default` as `qos: .background` may be delayed by tens of minutes)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I know, .background-work only gets done when the app is in background and stuff gets done with a very low priority. So it's not a background thread (as in "not main") while the app is running in foreground. So it's not only a question of time.

When we switch to iOS 13+, we could start using Task and async/await for background-stuff.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also like async/await because it would make working with jsonrpc nicer.
Also iOS 13 brings swiftUI but that seems to be not mature enough to be used from what I heard.
But on the other hand we are a messenger and people appreciate that we support older devices as well at least on android, for iPhone users it might be more common to upgrade to a new phone regularly - I suggest we look at the usage percentage take that into account.

DispatchQueue.global().async { [weak self] in
guard let self else { completionHandler?(.failed); return }

// we're in background, run IO for a little time
self.dcAccounts.startIo()
self.dcAccounts.maybeNetwork()
self.pushToDebugArray("2")

self.addDebugFetchTimestamp()

// create a new semaphore to make sure the received DC_CONNECTIVITY_CONNECTED really belongs to maybeNetwork() from above
// (maybeNetwork() sets connectivity to DC_CONNECTIVITY_CONNECTING, when fetch is done, we're back at DC_CONNECTIVITY_CONNECTED)
self.dcAccounts.fetchSemaphore = DispatchSemaphore(value: 0)
let start = CFAbsoluteTimeGetCurrent()

// backgroundFetch() pauses IO as needed
if !self.dcAccounts.backgroundFetch(timeout: 20) {
logger.error("backgroundFetch failed")
self.pushToDebugArray("ERR2")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does ERR2 stand for?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it js just a random short string added to "Settings/Advaned/View Log". it makes sense only if you look at the code. once things are settled, we can remove this DebugArray completely

}
let diff = CFAbsoluteTimeGetCurrent() - start

// wait for DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE;
// without IO being started, more events that could interfere with shutdown are not added
_ = self.dcAccounts.fetchSemaphore?.wait(timeout: .now() + 20)
self.dcAccounts.fetchSemaphore = nil
let diff2 = CFAbsoluteTimeGetCurrent() - start

// TOCHECK: it seems, we are not always reaching this point in code,
// semaphore?.wait() does not always exit after the given timeout and the app gets suspended -
// maybe that is on purpose somehow to suspend inactive apps, not sure.
// this does not happen often, but still.
// cmp. https://github.com/deltachat/deltachat-ios/pull/1542#pullrequestreview-951620906
logger.info("⬅️ finishing fetch")
logger.info("⬅️ finishing fetch in \(diff) s + \(diff2) s")
self.pushToDebugArray(String(format: "3/%.3fs", Double(Date().timeIntervalSince1970)-nowTimestamp))

if !self.appIsInForeground() {
self.dcAccounts.stopIo()
}

// to avoid 0xdead10cc exceptions, scheduled jobs need to be done before we get suspended;
// we increase the probabilty that this happens by waiting a moment before calling completionHandler()
usleep(1_000_000)
Expand Down Expand Up @@ -590,7 +587,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
return
}
eventHandlerActive = true
DispatchQueue.global(qos: .background).async { [weak self] in
DispatchQueue.global().async { [weak self] in
guard let self else { return }
let eventHandler = DcEventHandler(dcAccounts: self.dcAccounts)
let eventEmitter = self.dcAccounts.getEventEmitter()
Expand Down
4 changes: 2 additions & 2 deletions deltachat-ios/Chat/ChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ class ChatViewController: UITableViewController, UITableViewDropDelegate {

// things that do not affect the chatview
// and are delayed after the view is displayed
DispatchQueue.global(qos: .background).async { [weak self] in
DispatchQueue.global().async { [weak self] in
guard let self else { return }
self.dcContext.marknoticedChat(chatId: self.chatId)
}
Expand Down Expand Up @@ -959,7 +959,7 @@ class ChatViewController: UITableViewController, UITableViewDropDelegate {
let indexPaths = tableView.indexPathsForVisibleRows {
let visibleMessagesIds = indexPaths.map { UInt32(messageIds[$0.row]) }
if !visibleMessagesIds.isEmpty {
DispatchQueue.global(qos: .background).async { [weak self] in
DispatchQueue.global().async { [weak self] in
self?.dcContext.markSeenMessages(messageIds: visibleMessagesIds)
}
}
Expand Down
2 changes: 1 addition & 1 deletion deltachat-ios/Controller/ConnectivityViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class ConnectivityViewController: WebViewViewController {
self.loadHtml()
}
isLowDataMode = monitor.currentPath.isConstrained
monitor.start(queue: DispatchQueue.global(qos: .background))
monitor.start(queue: DispatchQueue.global())
self.connectivityMonitor = monitor
}
}
Expand Down
8 changes: 4 additions & 4 deletions deltachat-ios/DC/DcAccount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,6 @@ public class DcAccounts {
dc_accounts_maybe_network_lost(accountsPointer)
}

public func isAllWorkDone() -> Bool {
return dc_accounts_all_work_done(accountsPointer) != 0
}

public func startIo() {
dc_accounts_start_io(accountsPointer)
}
Expand All @@ -71,6 +67,10 @@ public class DcAccounts {
dc_accounts_stop_io(accountsPointer)
}

public func backgroundFetch(timeout: UInt64) -> Bool {
return dc_accounts_background_fetch(accountsPointer, timeout) == 1
}

public func select(id: Int) -> Bool {
return dc_accounts_select_account(accountsPointer, UInt32(id)) == 1
}
Expand Down
2 changes: 1 addition & 1 deletion deltachat-ios/Extensions/UIImageView+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ extension UIImageView {
func loadVideoThumbnail(from url: URL, placeholderImage: UIImage?, completionHandler: ((UIImage?) -> Void)?) {

self.image = placeholderImage
DispatchQueue.global(qos: .background).async {
DispatchQueue.global().async {
let thumbnailImage = DcUtils.generateThumbnailFromVideo(url: url)
DispatchQueue.main.async { [weak self] in
self?.image = thumbnailImage
Expand Down
2 changes: 1 addition & 1 deletion deltachat-ios/Handler/DeviceContactsHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class DeviceContactsHandler {

private func fetchContactsWithEmailFromDevice(completionHandler: @escaping ([CNContact]) -> Void) {

DispatchQueue.global(qos: .background).async {
DispatchQueue.global().async {
let keys = [CNContactFamilyNameKey, CNContactGivenNameKey, CNContactEmailAddressesKey]

var fetchedContacts: [CNContact] = []
Expand Down
2 changes: 1 addition & 1 deletion deltachat-ios/Helper/FileHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public class FileHelper {

public static func deleteFile(atPath: String?) {
if Thread.isMainThread {
DispatchQueue.global(qos: .background).async {
DispatchQueue.global().async {
deleteFile(atPath)
}
} else {
Expand Down
4 changes: 2 additions & 2 deletions deltachat-ios/Helper/NotificationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class NotificationManager {
}

public static func removeNotificationsForChat(dcContext: DcContext, chatId: Int) {
DispatchQueue.global(qos: .background).async {
DispatchQueue.global().async {
NotificationManager.removePendingNotificationsFor(dcContext: dcContext, chatId: chatId)
NotificationManager.removeDeliveredNotificationsFor(dcContext: dcContext, chatId: chatId)
NotificationManager.updateApplicationIconBadge()
Expand Down Expand Up @@ -70,7 +70,7 @@ public class NotificationManager {
logger.info("notification background task will end soon")
}

DispatchQueue.global(qos: .background).async { [weak self] in
DispatchQueue.global().async { [weak self] in
guard let self else { return }
if let ui = notification.userInfo,
let chatId = ui["chat_id"] as? Int,
Expand Down