refactor(coroutines): Use SupervisorJobs (#3714)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2025-11-15 22:33:41 -06:00 committed by GitHub
parent 0f8e475388
commit 9bf4b237dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 66 additions and 47 deletions

View file

@ -31,7 +31,7 @@ import com.geeksville.mesh.util.ignoreException
import com.geeksville.mesh.util.toRemoteExceptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
@ -105,7 +105,7 @@ constructor(
val mockInterfaceAddress: String by lazy { toInterfaceAddress(InterfaceId.MOCK, "") }
/** We recreate this scope each time we stop an interface */
var serviceScope = CoroutineScope(Dispatchers.IO + Job())
var serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private var radioIf: IRadioInterface = NopInterface("")
@ -298,7 +298,7 @@ constructor(
// cancel any old jobs and get ready for the new ones
serviceScope.cancel("stopping interface")
serviceScope = CoroutineScope(Dispatchers.IO + Job())
serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
if (logSends) {
sentPacketsLog.close()

View file

@ -48,68 +48,87 @@ import org.meshtastic.core.datastore.serializer.ModuleConfigSerializer
import org.meshtastic.proto.AppOnlyProtos.ChannelSet
import org.meshtastic.proto.LocalOnlyProtos.LocalConfig
import org.meshtastic.proto.LocalOnlyProtos.LocalModuleConfig
import javax.inject.Qualifier
import javax.inject.Singleton
private const val USER_PREFERENCES_NAME = "user_preferences"
@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class DataStoreScope
@InstallIn(SingletonComponent::class)
@Module
object DataStoreModule {
@Provides
@Singleton
@DataStoreScope
fun provideDataStoreScope(): CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
@Singleton
@Provides
fun providePreferencesDataStore(@ApplicationContext appContext: Context): DataStore<Preferences> =
PreferenceDataStoreFactory.create(
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }),
migrations =
listOf(
SharedPreferencesMigration(context = appContext, sharedPreferencesName = USER_PREFERENCES_NAME),
SharedPreferencesMigration(
context = appContext,
sharedPreferencesName = "ui-prefs",
keysToMigrate =
setOf(
KEY_APP_INTRO_COMPLETED,
KEY_THEME,
KEY_NODE_SORT,
KEY_INCLUDE_UNKNOWN,
KEY_ONLY_ONLINE,
KEY_ONLY_DIRECT,
KEY_SHOW_IGNORED,
),
fun providePreferencesDataStore(
@ApplicationContext appContext: Context,
@DataStoreScope scope: CoroutineScope,
): DataStore<Preferences> = PreferenceDataStoreFactory.create(
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }),
migrations =
listOf(
SharedPreferencesMigration(context = appContext, sharedPreferencesName = USER_PREFERENCES_NAME),
SharedPreferencesMigration(
context = appContext,
sharedPreferencesName = "ui-prefs",
keysToMigrate =
setOf(
KEY_APP_INTRO_COMPLETED,
KEY_THEME,
KEY_NODE_SORT,
KEY_INCLUDE_UNKNOWN,
KEY_ONLY_ONLINE,
KEY_ONLY_DIRECT,
KEY_SHOW_IGNORED,
),
),
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
produceFile = { appContext.preferencesDataStoreFile(USER_PREFERENCES_NAME) },
)
),
scope = scope,
produceFile = { appContext.preferencesDataStoreFile(USER_PREFERENCES_NAME) },
)
@Singleton
@Provides
fun provideLocalConfigDataStore(@ApplicationContext appContext: Context): DataStore<LocalConfig> =
DataStoreFactory.create(
serializer = LocalConfigSerializer,
produceFile = { appContext.dataStoreFile("local_config.pb") },
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalConfig.getDefaultInstance() }),
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
)
fun provideLocalConfigDataStore(
@ApplicationContext appContext: Context,
@DataStoreScope scope: CoroutineScope,
): DataStore<LocalConfig> = DataStoreFactory.create(
serializer = LocalConfigSerializer,
produceFile = { appContext.dataStoreFile("local_config.pb") },
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalConfig.getDefaultInstance() }),
scope = scope,
)
@Singleton
@Provides
fun provideModuleConfigDataStore(@ApplicationContext appContext: Context): DataStore<LocalModuleConfig> =
DataStoreFactory.create(
serializer = ModuleConfigSerializer,
produceFile = { appContext.dataStoreFile("module_config.pb") },
corruptionHandler =
ReplaceFileCorruptionHandler(produceNewData = { LocalModuleConfig.getDefaultInstance() }),
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
)
fun provideModuleConfigDataStore(
@ApplicationContext appContext: Context,
@DataStoreScope scope: CoroutineScope,
): DataStore<LocalModuleConfig> = DataStoreFactory.create(
serializer = ModuleConfigSerializer,
produceFile = { appContext.dataStoreFile("module_config.pb") },
corruptionHandler =
ReplaceFileCorruptionHandler(produceNewData = { LocalModuleConfig.getDefaultInstance() }),
scope = scope,
)
@Singleton
@Provides
fun provideChannelSetDataStore(@ApplicationContext appContext: Context): DataStore<ChannelSet> =
DataStoreFactory.create(
serializer = ChannelSetSerializer,
produceFile = { appContext.dataStoreFile("channel_set.pb") },
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { ChannelSet.getDefaultInstance() }),
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
)
fun provideChannelSetDataStore(
@ApplicationContext appContext: Context,
@DataStoreScope scope: CoroutineScope,
): DataStore<ChannelSet> = DataStoreFactory.create(
serializer = ChannelSetSerializer,
produceFile = { appContext.dataStoreFile("channel_set.pb") },
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { ChannelSet.getDefaultInstance() }),
scope = scope,
)
}