combov2: Wait for coroutines to finish in onStop()

This commit is contained in:
Carlos Rafael Giani 2023-03-08 22:19:50 +01:00
parent 2cdc4c45ea
commit fe1235dbe1

View file

@ -134,8 +134,8 @@ class ComboV2Plugin @Inject constructor (
// Coroutine scope and the associated job. All coroutines // Coroutine scope and the associated job. All coroutines
// that are started in this plugin are part of this scope. // that are started in this plugin are part of this scope.
private val pumpCoroutineMainJob = SupervisorJob() private var pumpCoroutineScopeJob = SupervisorJob()
private val pumpCoroutineScope = CoroutineScope(Dispatchers.Default + pumpCoroutineMainJob) private var pumpCoroutineScope = CoroutineScope(Dispatchers.Default + pumpCoroutineScopeJob)
private val _pumpDescription = PumpDescription() private val _pumpDescription = PumpDescription()
@ -255,6 +255,8 @@ class ComboV2Plugin @Inject constructor (
} }
override fun onStart() { override fun onStart() {
aapsLogger.info(LTag.PUMP, "Starting combov2 driver")
super.onStart() super.onStart()
updateComboCtlLogLevel() updateComboCtlLogLevel()
@ -290,49 +292,63 @@ class ComboV2Plugin @Inject constructor (
aapsLogger.debug(LTag.PUMP, "Creating bluetooth interface") aapsLogger.debug(LTag.PUMP, "Creating bluetooth interface")
bluetoothInterface = AndroidBluetoothInterface(context) bluetoothInterface = AndroidBluetoothInterface(context)
aapsLogger.info(LTag.PUMP, "Continuing combov2 driver start in coroutine")
// Continue initialization in a separate coroutine. This allows us to call // Continue initialization in a separate coroutine. This allows us to call
// runWithPermissionCheck(), which will keep trying to run the code block // runWithPermissionCheck(), which will keep trying to run the code block
// until either the necessary Bluetooth permissions are granted, or the // until either the necessary Bluetooth permissions are granted, or the
// coroutine is cancelled (see onStop() below). // coroutine is cancelled (see onStop() below).
pumpCoroutineScope.launch { pumpCoroutineScope.launch {
runWithPermissionCheck( try {
context, config, aapsLogger, androidPermission, runWithPermissionCheck(
permissionsToCheckFor = listOf("android.permission.BLUETOOTH_CONNECT") context, config, aapsLogger, androidPermission,
) { permissionsToCheckFor = listOf("android.permission.BLUETOOTH_CONNECT")
aapsLogger.debug(LTag.PUMP, "Setting up bluetooth interface") ) {
bluetoothInterface!!.setup() aapsLogger.debug(LTag.PUMP, "Setting up bluetooth interface")
bluetoothInterface!!.setup()
aapsLogger.debug(LTag.PUMP, "Setting up pump manager") aapsLogger.debug(LTag.PUMP, "Setting up pump manager")
pumpManager = ComboCtlPumpManager(bluetoothInterface!!, pumpStateStore) pumpManager = ComboCtlPumpManager(bluetoothInterface!!, pumpStateStore)
pumpManager!!.setup { pumpManager!!.setup {
_pairedStateUIFlow.value = false _pairedStateUIFlow.value = false
unpairing = false unpairing = false
}
// UI flows that must have defined values right
// at start are initialized here.
// The paired state UI flow is special in that it is also
// used as the backing store for the isPaired() function,
// so setting up that UI state flow equals updating that
// paired state.
val paired = pumpManager!!.getPairedPumpAddresses().isNotEmpty()
_pairedStateUIFlow.value = paired
setDriverState(DriverState.Disconnected)
aapsLogger.info(LTag.PUMP, "combov2 driver start complete")
// NOTE: EventInitializationChanged is sent in getPumpStatus() .
} }
} catch (e: CancellationException) {
// UI flows that must have defined values right aapsLogger.info(LTag.PUMP, "combov2 driver start cancelled")
// at start are initialized here. throw e
// The paired state UI flow is special in that it is also
// used as the backing store for the isPaired() function,
// so setting up that UI state flow equals updating that
// paired state.
val paired = pumpManager!!.getPairedPumpAddresses().isNotEmpty()
_pairedStateUIFlow.value = paired
setDriverState(DriverState.Disconnected)
// NOTE: EventInitializationChanged is sent in getPumpStatus() .
} }
} }
} }
override fun onStop() { override fun onStop() {
// Cancel any ongoing background coroutines. This includes an ongoing aapsLogger.info(LTag.PUMP, "Stopping combov2 driver")
// unfinished initialization that still waits for the user to grant
// Bluetooth permissions.
pumpCoroutineScope.cancel()
runBlocking { runBlocking {
// Cancel any ongoing background coroutines. This includes an ongoing
// unfinished initialization that still waits for the user to grant
// Bluetooth permissions. Also join to wait for the coroutines to
// finish. Otherwise, race conditions can occur, for example, when
// a coroutine tries to access bluetoothInterface right after it
// was torn down below.
pumpCoroutineScopeJob.cancelAndJoin()
// Normally this should not happen, but to be safe, // Normally this should not happen, but to be safe,
// make sure any running pump instance is disconnected. // make sure any running pump instance is disconnected.
pump?.disconnect() pump?.disconnect()
@ -353,7 +369,13 @@ class ComboV2Plugin @Inject constructor (
rxBus.send(EventInitializationChanged()) rxBus.send(EventInitializationChanged())
initializationChangedEventSent = false initializationChangedEventSent = false
// The old job and scope were completed. We need new ones.
pumpCoroutineScopeJob = SupervisorJob()
pumpCoroutineScope = CoroutineScope(Dispatchers.Default + pumpCoroutineScopeJob)
super.onStop() super.onStop()
aapsLogger.info(LTag.PUMP, "combov2 driver stopped")
} }
override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) { override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) {