Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  BackgroundTaskManager.swift   Sprache: JAVA

 

import CocoaLumberjackSwift
import Foundation
import ThreemaFramework

final class BackgroundTaskManager {
    
    static let shared = BackgroundTaskManager()
    
    private var backgroundTasks: [String: UIBackgroundTaskIdentifier]
    private var backgroundTaskIdentifierCounter = AtomicInteger()
    private var backgroundTaskQueue: DispatchQueue
    
    private init() {
        self.backgroundTasks = [String: UIBackgroundTaskIdentifier]()
        self.backgroundTaskQueue = DispatchQueue(label: "ch.threema.backgroundTaskQueue", attributes: [])
    }
    
    // MARK: Public functions
    
    /// Create a new (or renew if exists already) background task. The completion handler is called on the main thread.
    func newBackgroundTask(key: String, timeout: Int, completionHandler: (() -> Void)?) {
        DDLogVerbose("Background task new \(key) with timeout of \(timeout) seconds")
        backgroundTaskQueue.async {
            // Remove already existing background task first
            if let item = self.backgroundTasks.first(where: { $0.key.hasPrefix(key) }) {
                self.endBackgroundTask(identifier: item.value, internalKey: item.key)
            }

            // Creates internal key (unique), to prevent execution of old time out routine for renewed background task
            let internalKey = self.counter(identifier: key)

            // New background task
            var bgTask: UIBackgroundTaskIdentifier?
            bgTask = UIApplication.shared.beginBackgroundTask(withName: internalKey, expirationHandler: {
                
                self.backgroundTaskQueue.async {
                    self.endBackgroundTaskWithDisconnect(internalKey)
                }
            })
            self.backgroundTasks.updateValue(bgTask!, forKey: internalKey)
            
            // Initialize time out routine for background task
            self.backgroundTaskQueue.asyncAfter(deadline: .now() + Double(timeout)) {
                self.endBackgroundTaskWithDisconnect(internalKey)
            }
            
            DispatchQueue.main.async {
                completionHandler?()
            }
        }
    }
    
    func cancelBackgroundTask(key: String) {
        DDLogVerbose("Background task cancel \(key)")
        backgroundTaskQueue.async {
            if let item = self.backgroundTasks.first(where: { $0.key.hasPrefix(key) }) {
                self.endBackgroundTaskWithDisconnect(item.key)
            }
        }
    }

    /// Extents key with postffix count.
    func counter(identifier: String) -> String {
        identifier + String(backgroundTaskIdentifierCounter.incrementAndGet())
    }
    
    func isBackgroundTaskRunning(key: String) -> Bool {
        backgroundTasks.first(where: { $0.key.hasPrefix(key) }) != nil
    }
    
    // MARK: Private functions
    
    private func endBackgroundTaskWithDisconnect(_ internalKey: String) {
        guard let identifier: UIBackgroundTaskIdentifier = backgroundTasks[internalKey] else {
            return
        }
        
        if internalKey.hasPrefix(kAppPushReplyBackgroundTask) {
            // The reply is done, so is its claim. Also in the foreground, where the socket is kept by `.app`,
            // otherwise a reply handled there keeps the claim for the rest of the process lifetime
            ServerConnector.shared().disconnect(initiator: .notificationHandler)
        }
        else if SceneDelegate.isAppInBackground {
            if internalKey.hasPrefix(kAppClosedByUserBackgroundTask) {
                tearDownConnectionForSuspension()
            }
            else if internalKey.hasPrefix(kAppVoIPBackgroundTask) ||
                internalKey.hasPrefix(kAppVoIPIncomCallBackgroundTask) {
                let callState = VoIPCallStateManager.shared.currentCallState()

                // Keep the connection for an active call, but not for a ringing one: we get suspended anyway, and
                // only when disconnected does the server push the hangup
                if !callState.active || callState.isUnansweredIncoming {
                    if callState.active {
                        DDLogNotice(
                            "[Call] Background time is up while an incoming call is ringing (\(callState)), releasing the server connection"
                        )
                    }
                    // Wait, ending the last background task can suspend us before an async disconnect ran
                    ServerConnector.shared().disconnectWait(initiator: .threemaCall)
                }
            }
        }

        endBackgroundTask(identifier: identifier, internalKey: internalKey)
    }

    /// Close everything down before the system suspends us.
    ///
    /// Last point with execution time, so every claim has to go, otherwise we are suspended with an open socket and
    /// end up with "Another connection". `.app` is released last, it triggers the actual disconnect.
    private func tearDownConnectionForSuspension() {
        // Sessions are resumed on the next foreground, the claim goes in any case
        if WCSessionManager.shared.isRunningWCSession() {
            DDLogNotice("Close WebClient and server connection")
            WCSessionManager.shared.pauseAllRunningSessions()
        }
        ServerConnector.shared().disconnect(initiator: .threemaWeb)

        // Safety net, a reply in flight releases the claim itself when its own task ends
        if !isBackgroundTaskRunning(key: kAppPushReplyBackgroundTask) {
            ServerConnector.shared().disconnect(initiator: .notificationHandler)
        }

        ServerConnector.shared().disconnect(initiator: .app)

        // Hand over to the extensions. Not before, without IPC this flag also gates incoming message processing.
        // Not during a call, its messages (e.g. hangup) would be dropped.
        if !UserSettings.shared().ipcCommunicationEnabled, !VoIPCallStateManager.shared.currentCallState().active {
            AppGroup.setActive(false, for: AppGroupTypeApp)
        }
    }

    private func endBackgroundTask(identifier: UIBackgroundTaskIdentifier, internalKey: String) {
        backgroundTasks.removeValue(forKey: internalKey)

        // Ending the last background task allows the system to suspend us right away. Get the log messages of the just
        // finished background work onto disk before that happens, otherwise they are lost and a write in flight during
        // suspension risks a `0xdead10cc` termination for holding a lock in the app group container.
        if backgroundTasks.isEmpty {
            LogManager.flushLogs()
        }

        UIApplication.shared.endBackgroundTask(identifier)
    }
}

final class AtomicInteger {
    
    private let lock = DispatchSemaphore(value: 1)
    private var _value: Int
    
    init(value initialValue: Int = 0) {
        self._value = initialValue
    }
    
    var value: Int {
        get {
            lock.wait()
            defer { lock.signal() }
            return _value
        }
        set {
            lock.wait()
            defer { lock.signal() }
            _value = newValue
        }
    }
    
    func decrementAndGet() -> Int {
        lock.wait()
        defer { lock.signal() }
        _value -= 1
        return _value
    }
    
    func incrementAndGet() -> Int {
        lock.wait()
        defer { lock.signal() }
        _value += 1
        return _value
    }
}

Messung
CHZG
849510093

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104842
#Domains=499168