Jelajahi Sumber

Merge pull request #740 from deltachat/async-jobs

use async core
bjoern 5 tahun lalu
induk
melakukan
50da021bd9

+ 1 - 1
DcCore/DcCore.xcodeproj/project.pbxproj

@@ -302,7 +302,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "# The $PATH used by XCode likely won't contain Cargo, fix that.\n# This assumes a default `rustup` setup.\nexport PATH=\"$HOME/.cargo/bin:$PATH\"\n\nexport CFLAGS_x86_64_apple_darwin=\"-I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include\"\n\n# ensure all targets are installed\nrustup target add aarch64-apple-ios x86_64-apple-ios --toolchain `cat ../deltachat-ios/libraries/deltachat-core-rust/rust-toolchain`\n\n# --xcode-integ determines --release and --targets from XCode's env vars.\n# Depending your setup, specify the rustup toolchain explicitly.\ncargo +`cat ../deltachat-ios/libraries/deltachat-core-rust/rust-toolchain` lipo --release --manifest-path ../deltachat-ios/libraries/deltachat-core-rust/deltachat-ffi/Cargo.toml --no-default-features --features nightly\n";
+			shellScript = "# The $PATH used by XCode likely won't contain Cargo, fix that.\n# This assumes a default `rustup` setup.\nexport PATH=\"$HOME/.cargo/bin:$PATH\"\n\nexport CFLAGS_x86_64_apple_darwin=\"-I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include\"\n\n# ensure all targets are installed\nrustup target add aarch64-apple-ios x86_64-apple-ios --toolchain `cat ../deltachat-ios/libraries/deltachat-core-rust/rust-toolchain`\n\n# --xcode-integ determines --release and --targets from XCode's env vars.\n# Depending your setup, specify the rustup toolchain explicitly.\ncargo +`cat ../deltachat-ios/libraries/deltachat-core-rust/rust-toolchain` lipo --release --manifest-path ../deltachat-ios/libraries/deltachat-core-rust/deltachat-ffi/Cargo.toml\n";
 		};
 /* End PBXShellScriptBuildPhase section */
 

+ 79 - 37
DcCore/DcCore/DC/Wrapper.swift

@@ -8,22 +8,18 @@ public class DcContext {
     /// where we want to have more than one DcContext.
     static let dcContext: DcContext = DcContext()
     public var logger: Logger?
-    let contextPointer: OpaquePointer
+    var contextPointer: OpaquePointer?
     public var lastErrorString: String?
     public var lastWarningString: String = "" // temporary thing to get a grip on some weird errors
     public var maxConfigureProgress: Int = 0 // temporary thing to get a grip on some weird errors
 
     private init() {
-        var version = ""
-        if let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
-            version += " " + appVersion
-        }
-
-        contextPointer = dc_context_new(callback_ios, nil, "iOS" + version)
     }
 
     deinit {
+        if contextPointer == nil { return } // avoid a warning about a "careless call"
         dc_context_unref(contextPointer)
+        contextPointer = nil
     }
 
     /// Injection of DcContext is preferred over the usage of the shared variable
@@ -90,6 +86,10 @@ public class DcContext {
         return chatlist
     }
 
+    public func sendMsgSync(chatId: Int, msg: DcMsg) {
+        dc_send_msg_sync(contextPointer, UInt32(chatId), msg.messagePointer)
+    }
+
     public func getChatMedia(chatId: Int, messageType: Int32, messageType2: Int32, messageType3: Int32) -> [Int] {
         guard let messagesPointer = dc_get_chat_media(contextPointer, UInt32(chatId), messageType, messageType2, messageType3) else {
             return []
@@ -178,45 +178,39 @@ public class DcContext {
     }
 
     public func interruptIdle() {
-        dc_interrupt_imap_idle(contextPointer)
-        dc_interrupt_smtp_idle((contextPointer))
-        dc_interrupt_mvbox_idle((contextPointer))
-        dc_interrupt_sentbox_idle((contextPointer))
     }
 
-    public func openDatabase(dbFile: String) {
-        _ = dc_open(contextPointer, dbFile, nil)
+    public func getEventEmitter() -> DcEventEmitter {
+        let eventEmitterPointer = dc_get_event_emitter(contextPointer)
+        return DcEventEmitter(eventEmitterPointer: eventEmitterPointer)
     }
 
-    public func closeDatabase() {
-        dc_close(contextPointer)
-    }
+    public func openDatabase(dbFile: String) {
+        var version = ""
+        if let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
+            version += " " + appVersion
+        }
 
-    public func performImap() {
-        dc_perform_imap_jobs(contextPointer)
-        dc_perform_imap_fetch(contextPointer)
-        dc_perform_imap_idle(contextPointer)
+        contextPointer = dc_context_new("iOS" + version, dbFile, nil)
     }
 
-    public func performMoveBox() {
-        dc_perform_mvbox_jobs(contextPointer)
-        dc_perform_mvbox_fetch(contextPointer)
-        dc_perform_mvbox_idle(contextPointer)
+    public func closeDatabase() {
+        dc_context_unref(contextPointer)
+        contextPointer = nil
     }
 
-    public func performSmtpJobs() {
-        dc_perform_smtp_jobs(contextPointer)
+    public func maybeStartIo() {
+        if isConfigured() {
+            dc_start_io(contextPointer)
+        }
     }
-    
-    public func performSmtp() {
-        dc_perform_smtp_jobs(contextPointer)
-        dc_perform_smtp_idle(contextPointer)
+
+    public func stopIo() {
+        dc_stop_io(contextPointer)
     }
 
-    public func performSentbox() {
-        dc_perform_sentbox_jobs(contextPointer)
-        dc_perform_sentbox_fetch(contextPointer)
-        dc_perform_sentbox_idle(contextPointer)
+    public func isIoRunning() -> Bool {
+        return dc_is_io_running(contextPointer) != 0
     }
 
     public func setStockTranslation(id: Int32, localizationKey: String) {
@@ -579,6 +573,56 @@ public class DcContext {
     }
 }
 
+public class DcEventEmitter {
+    private var eventEmitterPointer: OpaquePointer?
+
+    // takes ownership of specified pointer
+    public init(eventEmitterPointer: OpaquePointer?) {
+        self.eventEmitterPointer = eventEmitterPointer
+    }
+
+    public func getNextEvent() -> DcEvent? {
+        guard let eventPointer = dc_get_next_event(eventEmitterPointer) else { return nil }
+        return DcEvent(eventPointer: eventPointer)
+    }
+
+    deinit {
+        dc_event_emitter_unref(eventEmitterPointer)
+    }
+}
+
+public class DcEvent {
+    private var eventPointer: OpaquePointer?
+
+    // takes ownership of specified pointer
+    public init(eventPointer: OpaquePointer?) {
+        self.eventPointer = eventPointer
+    }
+
+    deinit {
+        dc_event_unref(eventPointer)
+    }
+
+    public var id: Int32 {
+        return Int32(dc_event_get_id(eventPointer))
+    }
+
+    public var data1Int: Int {
+        return Int(dc_event_get_data1_int(eventPointer))
+    }
+
+    public var data2Int: Int {
+        return Int(dc_event_get_data2_int(eventPointer))
+    }
+
+    public var data2String: String {
+        guard let cString = dc_event_get_data2_str(eventPointer) else { return "" }
+        let swiftString = String(cString: cString)
+        dc_str_unref(cString)
+        return swiftString
+    }
+}
+
 public class DcChatlist {
     private var chatListPointer: OpaquePointer?
 
@@ -720,12 +764,10 @@ public class DcArray {
     public var count: Int {
        return Int(dc_array_get_cnt(dcArrayPointer))
     }
-
-    ///TODO: add missing methods here
 }
 
 public class DcMsg {
-    private var messagePointer: OpaquePointer?
+    var messagePointer: OpaquePointer?
 
     /**
         viewType: one of

+ 14 - 12
DcCore/DcCore/DC/events.swift

@@ -11,24 +11,26 @@ public let dcNotificationViewChat = Notification.Name(rawValue: "MrEventViewChat
 public let dcNotificationContactChanged = Notification.Name(rawValue: "MrEventContactsChanged")
 public let dcNotificationChatModified = Notification.Name(rawValue: "dcNotificationChatModified")
 
-@_silgen_name("callbackSwift")
+public func handleEvent(event: DcEvent) {
+    let id = event.id
+    let data1 = event.data1Int
+    let data2 = event.data2Int
 
-public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLong, data1String: UnsafePointer<Int8>, data2String: UnsafePointer<Int8>) {
-    if event >= DC_EVENT_ERROR && event <= 499 {
-        let s = String(cString: data2String)
+    if id >= DC_EVENT_ERROR && id <= 499 {
+        let s = event.data2String
         DcContext.shared.lastErrorString = s
         DcContext.shared.logger?.error("event: \(s)")
         return
     }
 
-    switch event {
+    switch id {
 
     case DC_EVENT_INFO:
-        let s = String(cString: data2String)
+        let s = event.data2String
         DcContext.shared.logger?.info("event: \(s)")
 
     case DC_EVENT_WARNING:
-        let s = String(cString: data2String)
+        let s = event.data2String
         DcContext.shared.lastWarningString = s
         DcContext.shared.logger?.warning("event: \(s)")
 
@@ -73,10 +75,10 @@ public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLon
         }
 
     case DC_EVENT_IMAP_CONNECTED, DC_EVENT_SMTP_CONNECTED:
-        DcContext.shared.logger?.warning("network: \(String(cString: data2String))")
+        DcContext.shared.logger?.warning("network: \(event.data2String)")
 
     case DC_EVENT_MSGS_CHANGED, DC_EVENT_MSG_READ, DC_EVENT_MSG_DELIVERED, DC_EVENT_MSG_FAILED:
-        DcContext.shared.logger?.info("change: \(event)")
+        DcContext.shared.logger?.info("change: \(id)")
 
         let nc = NotificationCenter.default
 
@@ -93,7 +95,7 @@ public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLon
         }
 
     case DC_EVENT_CHAT_MODIFIED:
-        DcContext.shared.logger?.info("chat modified: \(event)")
+        DcContext.shared.logger?.info("chat modified: \(id)")
         let nc = NotificationCenter.default
         DispatchQueue.main.async {
             nc.post(
@@ -137,7 +139,7 @@ public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLon
         }
 
     case DC_EVENT_SMTP_MESSAGE_SENT:
-        DcContext.shared.logger?.info("network: \(String(cString: data2String))")
+        DcContext.shared.logger?.info("network: \(event.data2String)")
 
     case DC_EVENT_MSG_DELIVERED:
         DcContext.shared.logger?.info("message delivered: \(data1)-\(data2)")
@@ -187,6 +189,6 @@ public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLon
         }
 
     default:
-        DcContext.shared.logger?.warning("unknown event: \(event)")
+        DcContext.shared.logger?.warning("unknown event: \(id)")
     }
 }

+ 1 - 7
DcCore/DcCore/DC/wrapper.c

@@ -1,9 +1,3 @@
 #include "wrapper.h"
 
-void callbackSwift(int, long, long, const char*, const char*);
-
-uintptr_t callback_ios(dc_context_t* mailbox, int event, uintptr_t data1, uintptr_t data2)
-{
-    callbackSwift(event, data1, data2, data1? (const char*)data1 : "", data2? (const char*)data2 : "");
-    return 0;
-}
+// if needed, add c-related wrapper functions here

+ 0 - 2
DcCore/DcCore/DC/wrapper.h

@@ -13,6 +13,4 @@ typedef dc_lot_t dc_lot_t;
 typedef dc_array_t dc_array_t;
 typedef dc_chatlist_t dc_chatlist_t;
 
-uintptr_t callback_ios(dc_context_t* mailbox, int event, uintptr_t data1, uintptr_t data2);
-
 #endif /* wrapper_h */

+ 2 - 3
DcShare/Controller/SendingController.swift

@@ -69,10 +69,9 @@ class SendingController: UIViewController {
 
     private func sendMessage() {
         DispatchQueue.global(qos: .utility).async {
-            for message in self.dcMsgs {
-                message.sendInChat(id: self.chatId)
+            for dcMsg in self.dcMsgs {
+                self.dcContext.sendMsgSync(chatId: self.chatId, msg: dcMsg)
             }
-            self.dcContext.performSmtpJobs()
             self.delegate?.onSendingAttemptFinished()
         }
     }

+ 1 - 1
deltachat-ios.xcodeproj/project.pbxproj

@@ -1264,7 +1264,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "# The $PATH used by XCode likely won't contain Cargo, fix that.\n# This assumes a default `rustup` setup.\n export PATH=\"$HOME/.cargo/bin:$PATH\"\n\n export CFLAGS_x86_64_apple_darwin=\"-I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include\"\n\n# ensure all targets are installed\n rustup target add aarch64-apple-ios x86_64-apple-ios --toolchain `cat deltachat-ios/libraries/deltachat-core-rust/rust-toolchain`\n\n# --xcode-integ determines --release and --targets from XCode's env vars.\n# Depending your setup, specify the rustup toolchain explicitly.\n cargo +`cat deltachat-ios/libraries/deltachat-core-rust/rust-toolchain` lipo --release --manifest-path deltachat-ios/libraries/deltachat-core-rust/deltachat-ffi/Cargo.toml --no-default-features --features nightly\n";
+			shellScript = "# The $PATH used by XCode likely won't contain Cargo, fix that.\n# This assumes a default `rustup` setup.\n export PATH=\"$HOME/.cargo/bin:$PATH\"\n\n export CFLAGS_x86_64_apple_darwin=\"-I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include\"\n\n# ensure all targets are installed\n rustup target add aarch64-apple-ios x86_64-apple-ios --toolchain `cat deltachat-ios/libraries/deltachat-core-rust/rust-toolchain`\n\n# --xcode-integ determines --release and --targets from XCode's env vars.\n# Depending your setup, specify the rustup toolchain explicitly.\n cargo +`cat deltachat-ios/libraries/deltachat-core-rust/rust-toolchain` lipo --release --manifest-path deltachat-ios/libraries/deltachat-core-rust/deltachat-ffi/Cargo.toml\n";
 		};
 /* End PBXShellScriptBuildPhase section */
 

+ 34 - 42
deltachat-ios/AppDelegate.swift

@@ -24,6 +24,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     var reachability = Reachability()!
     var window: UIWindow?
     var state = ApplicationState.stopped
+    var appIsInForeground = false
 
     func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
         // main()
@@ -43,6 +44,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         }
 
         openDatabase()
+        installEventHandler()
         RelayHelper.setup(dcContext)
         appCoordinator = AppCoordinator(window: window, dcContext: dcContext)
         locationManager = LocationManager(context: dcContext)
@@ -90,23 +92,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         return true
     }
 
+
     func application(_: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
         logger.info("---- background-fetch ----")
+        startThreads()
 
-        startThreads {
-            // TODO: actually set the right value depending on if we found sth
+        // we have 30 seconds time for our job, leave some seconds for graceful termination.
+        // also, the faster we return, the sooner we get called again.
+        DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
+            if !self.appIsInForeground {
+                self.dcContext.stopIo()
+            }
             completionHandler(.newData)
         }
     }
 
     func applicationWillEnterForeground(_: UIApplication) {
         logger.info("---- foreground ----")
+        appIsInForeground = true
         startThreads()
     }
 
     func applicationDidEnterBackground(_: UIApplication) {
         logger.info("---- background ----")
-
+        appIsInForeground = false
         maybeStop()
     }
 
@@ -141,6 +150,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         dcContext.openDatabase(dbFile: databaseLocation)
     }
 
+    func closeDatabase() {
+        state = .stopped
+        dcContext.closeDatabase()
+    }
+
     func setStockTranslations() {
         dcContext.setStockTranslation(id: DC_STR_NOMESSAGES, localizationKey: "chat_no_messages")
         dcContext.setStockTranslation(id: DC_STR_SELF, localizationKey: "self")
@@ -176,56 +190,34 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         dcContext.setStockTranslation(id: DC_STR_DEVICE_MESSAGES, localizationKey: "device_talk")
     }
 
-    func stopThreads() {
-        state = .background
-        dcContext.interruptIdle()
-    }
-
-    func closeDatabase() {
-        state = .stopped
-        dcContext.closeDatabase()
-    }
-
-    func startThreads(_ completion: (() -> Void)? = nil) {
-        logger.info("---- start ----")
-
-        if state == .running {
-            return
-        }
-        state = .running
-
+    func installEventHandler() {
         DispatchQueue.global(qos: .background).async {
             self.registerBackgroundTask()
-            while self.state == .running {
-                self.dcContext.performImap()
+            let eventEmitter = self.dcContext.getEventEmitter()
+            while true {
+                guard let event = eventEmitter.getNextEvent() else { break }
+                handleEvent(event: event)
             }
             if self.backgroundTask != .invalid {
-                completion?()
                 self.endBackgroundTask()
             }
         }
+    }
 
-        DispatchQueue.global(qos: .utility).async {
-            self.registerBackgroundTask()
-            while self.state == .running {
-                self.dcContext.performSmtp()
-            }
-            if self.backgroundTask != .invalid {
-                self.endBackgroundTask()
-            }
-        }
+    func startThreads() {
+        logger.info("---- start ----")
 
-        DispatchQueue.global(qos: .background).async {
-            while self.state == .running {
-                self.dcContext.performSentbox()
-            }
+        if state == .running {
+            return
         }
+        state = .running
 
-        DispatchQueue.global(qos: .background).async {
-            while self.state == .running {
-                self.dcContext.performMoveBox()
-            }
-        }
+        dcContext.maybeStartIo()
+    }
+
+    func stopThreads() {
+        state = .background
+        dcContext.stopIo()
     }
 
     // MARK: - BackgroundTask

+ 4 - 0
deltachat-ios/Controller/AccountSetupController.swift

@@ -579,6 +579,7 @@ class AccountSetupController: UITableViewController, ProgressAlertHandler {
         }
 
         print("oAuth-Flag when loggin in: \(dcContext.getAuthFlags())")
+        dcContext.stopIo()
         dcContext.configure()
         showProgressAlert(title: String.localized("login_header"), dcContext: dcContext)
     }
@@ -659,6 +660,7 @@ class AccountSetupController: UITableViewController, ProgressAlertHandler {
             notification in
             if let ui = notification.userInfo {
                 if ui["error"] as! Bool {
+                    self.dcContext.maybeStartIo()
                     var errorMessage = ui["errorMessage"] as? String
                     if let appDelegate = UIApplication.shared.delegate as? AppDelegate, appDelegate.reachability.connection == .none {
                         errorMessage = String.localized("login_error_no_internet_connection")
@@ -667,6 +669,7 @@ class AccountSetupController: UITableViewController, ProgressAlertHandler {
                     }
                     self.updateProgressAlert(error: errorMessage)
                 } else if ui["done"] as! Bool {
+                    self.dcContext.maybeStartIo()
                     self.updateProgressAlertSuccess(completion: self.handleLoginSuccess)
                 } else {
                     self.updateProgressAlertValue(value: ui["progress"] as? Int)
@@ -789,6 +792,7 @@ class AccountSetupController: UITableViewController, ProgressAlertHandler {
             appDelegate.closeDatabase()
             DatabaseHelper().clearAccountData()
             appDelegate.openDatabase()
+            appDelegate.installEventHandler()
             appDelegate.startThreads()
             appDelegate.appCoordinator.presentWelcomeController()
         }))

+ 1 - 0
deltachat-ios/Controller/WelcomeViewController.swift

@@ -114,6 +114,7 @@ class WelcomeViewController: UIViewController, ProgressAlertHandler {
         if success {
             addProgressAlertListener(progressName: dcNotificationConfigureProgress, onSuccess: handleLoginSuccess)
             showProgressAlert(title: String.localized("login_header"), dcContext: dcContext)
+            dcContext.stopIo()
             dcContext.configure()
         } else {
             accountCreationErrorAlert()

+ 2 - 0
deltachat-ios/Handler/ProgressAlertHandler.swift

@@ -68,8 +68,10 @@ extension ProgressAlertHandler {
             guard let self = self else { return }
             if let ui = notification.userInfo {
                 if ui["error"] as? Bool ?? false {
+                    DcContext.shared.maybeStartIo()
                     self.updateProgressAlert(error: ui["errorMessage"] as? String)
                 } else if ui["done"] as? Bool ?? false {
+                    DcContext.shared.maybeStartIo()
                     self.updateProgressAlertSuccess(completion: onSuccess)
                 } else {
                     self.updateProgressAlertValue(value: ui["progress"] as? Int)