MailCore / mailcore2

MailCore 2 provide a simple and asynchronous API to work with e-mail protocols IMAP, POP and SMTP. The API has been redesigned from ground up.
Other
2.58k stars 623 forks source link

[Bug] - iOS: Thread not released by IMAP Session (each session allocates 560KB = standard thread side) #1993

Open Be-Maps opened 5 months ago

Be-Maps commented 5 months ago

Summary On iOS in Xcode OperationQueue Thread is not released by IMAP Session (each session allocates 560KB = standard thread size) These threads stay in memory, and finally app goes out of memory. Might be because [dispatch_release] is not supported anymore or maybe something holds in [DispatchQueue] or the tasks count is not released to zero (so it will not get cleared), or something is not [nil]'ed to release by iOS. Platform(s) iOS iPhone 13

XCode **Happens on Mail Server** outlook Hotmail And few others - hosted mail servers **Piece of code** To reproduce run it on timer (all class below): public class MapViewModel { public var TAG:String = "MapViewModel : " var timer: Timer? = nil public func StopTaskScheduler(){ if (self.timer != nil) { self.timer!.invalidate() self.timer = nil } } func StartTaskScheduler(){ if timer != nil {StopTaskScheduler()} if timer == nil { timer = Timer.scheduledTimer(timeInterval: 50.0, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true) timer?.fire() } } @objc func timerAction() { print("timer started - begin") // you can run it as closure imapConnect() // you can run it async - same result as closure Task { [weak self] in guard let strongSelf = self else { return } await strongSelf.imapConnectAsync() } } private func imapConnect() { let session = MCOIMAPSession() Log.d("TAG", "IMAP isOperationQueueRunning: \(session.isOperationQueueRunning)") session.hostname = "imap.*********.com" session.username = "*****@********.com" session.password = "*******" session.port = 993 session.allowsFolderConcurrentAccessEnabled = true session.connectionType = MCOConnectionType.TLS session.authType = MCOAuthType.saslLogin session.isCheckCertificateEnabled = false session.isVoIPEnabled = false session.maximumConnections = 2 session.timeout = 10 if let loginOperation = session.checkAccountOperation() { loginOperation.start { (error) -> Void in if (error != nil) { print("Error login:\n\(String(describing: error))") } else { print("imapConnect - Successful IMAP connection") } } } // list folders var folderList:[Any]? = nil if let folderListOperation = session.fetchAllFoldersOperation() { folderListOperation.start { error, folders in if let error = error { print("Error downloading folder list: \(error.localizedDescription)") } else { folderList = folders print("All IMAP Folders loaded") for value in folderList! { // MARK: folderlist was null switch value { case is MCOIMAPFolder: let folder:MCOIMAPFolder = value as! MCOIMAPFolder //folder.path print("\(folder.path.description) is a folder") //folder. default: print("Skip ... possibly null value!") } } } } } // logout if let logoutOperation = session.disconnectOperation() { logoutOperation.start { error in if (error != nil) { print("IMAP logoutOperation Error") } else { print("imapConnect - Successful IMAP logoutOperation") } } } session.cancelAllOperations() Log.d("TAG", "IMAP isOperationQueueRunning: \(session.isOperationQueueRunning)") } private func imapConnectAsync() async { let session = MCOIMAPSession() Log.d("TAG", "IMAP isOperationQueueRunning: \(session.isOperationQueueRunning)") session.hostname = "imap.*******.com" session.username = "******@*****.com" session.password = "*******" session.port = 993 session.allowsFolderConcurrentAccessEnabled = true session.connectionType = MCOConnectionType.TLS session.authType = MCOAuthType.saslLogin session.isCheckCertificateEnabled = false session.isVoIPEnabled = false session.maximumConnections = 2 session.timeout = 10 if let loginOperation = session.checkAccountOperation() { do { try await loginOperation.start(); print("imapConnect - Successful IMAP connection") } catch { print("imapConnect - IMAP Connect Error: \(error)"); return } } // list folders var folderList:[Any]? = nil if let folderListOperation = session.fetchAllFoldersOperation() { do { folderList = try await folderListOperation.start(); print("All IMAP Folders loaded") } catch { print("Error listing folders: \(error)") } } for value in folderList! { // MARK: folderlist was null switch value { case is MCOIMAPFolder: let folder:MCOIMAPFolder = value as! MCOIMAPFolder print("\(folder.path.description) is a folder") default: print("Skip ... possibly null value!") } } // logout if let logoutOperation = session.disconnectOperation() { do { try await logoutOperation.start(); print("Successful IMAP logoutOperation") } catch { print("IMAP logoutOperation Error: \(error)") return } } session.cancelAllOperations() Log.d("TAG", "IMAP isOperationQueueRunning: \(session.isOperationQueueRunning)") } } **Actual outcome** MailCore does not release IMAP Session thread ** Logs** Please see the screenshot Screenshot 2024-01-26 at 04 10 58 **Expected outcome** MailCore should release IMAP Session thread