diff --git a/LibreLoop/LibreLoopCGMManager/LibreLoopCGMManager.swift b/LibreLoop/LibreLoopCGMManager/LibreLoopCGMManager.swift index 9521936..a06b4dc 100644 --- a/LibreLoop/LibreLoopCGMManager/LibreLoopCGMManager.swift +++ b/LibreLoop/LibreLoopCGMManager/LibreLoopCGMManager.swift @@ -865,6 +865,7 @@ public final class LibreLoopCGMManager: CGMManager { eventListenerTask = nil // Drop the live monitor and its BLE link so no session of ours lingers // on the shared central to collide with a future re-add's handshake. + monitor?.stop() monitor = nil if scannerWasInitialized { scanner.stopScan() diff --git a/LibreLoop/Pairing/LibreLoopCGMManager+Pairing.swift b/LibreLoop/Pairing/LibreLoopCGMManager+Pairing.swift index 59b4f7f..4fb8ccc 100644 --- a/LibreLoop/Pairing/LibreLoopCGMManager+Pairing.swift +++ b/LibreLoop/Pairing/LibreLoopCGMManager+Pairing.swift @@ -180,6 +180,10 @@ extension LibreLoopCGMManager { let delegate = cgmManagerDelegate Task { await delegate?.retractAlert(identifier: id) } } + if let previous = self.monitor, previous !== monitor { + llog("adopt: stopping previous monitor") + previous.stop() + } self.monitor = monitor self.connectedAt = Date() // Fresh session: hasn't streamed a reading yet. Drives the @@ -192,7 +196,13 @@ extension LibreLoopCGMManager { self.lastHistoricalPageAt = nil monitor.setHandlers( onReading: { [weak self] sample in self?.ingest(sample) }, - onDisconnect: { [weak self] in self?.handleMonitorDisconnect() }, + onDisconnect: { [weak self, weak monitor] in + guard let self, let monitor, self.monitor === monitor else { + llog("disconnect from a replaced monitor; ignoring") + return + } + self.handleMonitorDisconnect() + }, onStatus: { [weak self] text in self?.updateStatusDetail(text) }, onHistoricalPage: { [weak self] page in self?.handleHistoricalPage(page) }, onClinicalRecord: { [weak self] record in self?.handleClinicalRecord(record) }, diff --git a/LibreLoop/Sensor/LibreLoopSensorMonitor.swift b/LibreLoop/Sensor/LibreLoopSensorMonitor.swift index fc3706a..d6cd42e 100644 --- a/LibreLoop/Sensor/LibreLoopSensorMonitor.swift +++ b/LibreLoop/Sensor/LibreLoopSensorMonitor.swift @@ -427,12 +427,24 @@ public final class LibreLoopSensorMonitor: @unchecked Sendable { ) } + /// Also drops the handlers: a stopped monitor's stream ends on cancellation, + /// and it must not report that as a disconnect of whatever replaced it. public func stop() { lock.lock() let t = task task = nil let wd = silenceWatchdog silenceWatchdog = nil + readingHandler = nil + disconnectHandler = nil + statusHandler = nil + historicalPageHandler = nil + clinicalRecordHandler = nil + embeddedHistoricalHandler = nil + patchStatusHandler = nil + lifeCountHandler = nil + readyHandler = nil + rawReadHandler = nil lock.unlock() t?.cancel() wd?.cancel()