-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
[REQUIRED] Step 1: Describe your environment
- Xcode version: 13.4.1
- Firebase SDK version: 9.1.0
- Installation method:
CocoaPods - Firebase Component: (Auth, Core, Database, Firestore, Messaging, Storage)
- Target platform(s):
iOS
[REQUIRED] Step 2: Describe the problem
Crashed: com.apple.root.default-qos.overcommit
0 libsystem_pthread.dylib 0x7744 + 102
1 libc++.1.dylib 0x3cc88 std::__1::recursive_mutex::lock() + 16
2 FirebaseFirestore 0x10d8ec firebase::firestore::api::QueryListenerRegistration::Remove() + 90 (__mutex_base:90)
3 Appname 0x73160 DbQuery.dispose() + 4335366496 (:4335366496)
4 Appname 0xb4a80 DataDownloadManager.addDownloadListener() + 4335635072 (:4335635072)
5 Appname 0x1bc3a0 closure #1 in closure #1 in UploadDownloadTimerManager.startTimer() + 4336714656
6 Appname 0x255194 closure #1 in closure #1 in RepeatingTimer.timer.getter + 4337340820
7 Appname 0x13411c thunk for @escaping @callee_guaranteed () -> () + 4336156956 (:4336156956)
8 libdispatch.dylib 0x481c _dispatch_client_callout + 20
9 libdispatch.dylib 0x7cf4 _dispatch_continuation_pop + 448
10 libdispatch.dylib 0x1a4b8 _dispatch_source_invoke + 1284
11 libdispatch.dylib 0x15fe0 _dispatch_root_queue_drain + 388
12 libdispatch.dylib 0x167d8 _dispatch_worker_thread2 + 112
13 libsystem_pthread.dylib 0x3768 _pthread_wqthread + 216
14 libsystem_pthread.dylib 0xa74c start_wqthread + 8
Steps to reproduce:
We have implemented a repeating DispatchSourceTimer which is fire in every one minute to check data on firestore collection (by removing existing listener and adding new one.). You can check added code for better understanding.
Relevant Code:
class RepeatingTimer {
let timeInterval: TimeInterval
init(timeInterval: TimeInterval) {
self.timeInterval = timeInterval
}
private lazy var timer: DispatchSourceTimer = {
let t = DispatchSource.makeTimerSource()
t.schedule(deadline: .now() + self.timeInterval, repeating: self.timeInterval)
t.setEventHandler(handler: { [weak self] in
self?.eventHandler?()
})
return t
}()
var eventHandler: (() -> Void)?
private enum State {
case suspended
case resumed
}
private var state: State = .suspended
deinit {
timer.setEventHandler {}
timer.cancel()
/*
If the timer is suspended, calling cancel without resuming
triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
*/
resume()
eventHandler = nil
}
func resume() {
if state == .resumed {
return
}
state = .resumed
timer.resume()
}
func suspend() {
if state == .suspended {
return
}
state = .suspended
timer.suspend()
}
}
class UploadDownloadTimerManager: NSObject {
func startTimer(){
DispatchQueue.global(qos: .background).async {
self.timer = RepeatingTimer(timeInterval: TimeInterval(AppConstants.uploadTimerInterval))
self.timer?.eventHandler = {
DataDownloadManager.sharedManager().addDownloadListener() // Add download collection listener
}
self.timer?.resume()
}
}
}
class DataDownloadManager: NSObject {
var downloadListenerQuery:DbQuery? = nil
static var _sharedManager:DataDownloadManager? = nil
static func sharedManager() -> DataDownloadManager {
if _sharedManager == nil {
_sharedManager = DataDownloadManager()
}
return _sharedManager ?? DataDownloadManager();
}
func addDownloadListener(){
self.removeDownloadListener()
// code for reinitiate listener
}
func removeDownloadListener(){
downloadListenerQuery?.dispose()
}
}
class DbQuery: NSObject {
private var firebaseListener: ListenerRegistration?
init(listener: ListenerRegistration? = nil){
firebaseListener = listener
}
func dispose() {
firebaseListener?.remove()
firebaseListener = nil
}
}