mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-02-08 14:13:16 -05:00
Improve handling of worker cancellation
If we keep running our code, but the worker gets cancelled, the system may freeze our code execution making notifications hang. Also, it may cause the code to run more than once at roughly the same time, because the system reschedules our worker.
This commit is contained in:
@@ -128,6 +128,7 @@ class AppInstallManager @Inject constructor(
|
||||
return currentState
|
||||
}
|
||||
val iconDownloadRequest = iconModel as? DownloadRequest
|
||||
currentCoroutineContext().ensureActive()
|
||||
val job = scope.async {
|
||||
startInstall(
|
||||
appMetadata = appMetadata,
|
||||
|
||||
@@ -5,6 +5,8 @@ import android.text.format.Formatter
|
||||
import android.util.Log
|
||||
import androidx.annotation.WorkerThread
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -142,6 +144,7 @@ class RepoUpdateManager(
|
||||
|
||||
@WorkerThread
|
||||
suspend fun updateRepos() {
|
||||
currentCoroutineContext().ensureActive()
|
||||
if (isUpdating.value) {
|
||||
// This is a workaround for what looks like a WorkManager bug.
|
||||
// Sometimes it goes through scheduling/cancellation loops
|
||||
@@ -158,6 +161,7 @@ class RepoUpdateManager(
|
||||
}
|
||||
_isUpdating.value = true
|
||||
try {
|
||||
currentCoroutineContext().ensureActive()
|
||||
var reposUpdated = false
|
||||
val repoErrors = mutableListOf<Pair<Repository, Exception>>()
|
||||
// always get repos fresh from DB, because
|
||||
@@ -167,6 +171,7 @@ class RepoUpdateManager(
|
||||
// it might not be in the FDroidApp list, yet
|
||||
db.getRepositoryDao().getRepositories().forEach { repo ->
|
||||
if (!repo.enabled) return@forEach
|
||||
currentCoroutineContext().ensureActive()
|
||||
|
||||
// show notification
|
||||
if (isUpdateNotificationEnabled) {
|
||||
|
||||
@@ -20,6 +20,8 @@ import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import mu.KotlinLogging
|
||||
@@ -116,6 +118,7 @@ class RepoUpdateWorker @AssistedInject constructor(
|
||||
}
|
||||
val repoId = inputData.getLong("repoId", -1)
|
||||
return try {
|
||||
currentCoroutineContext().ensureActive()
|
||||
if (repoId >= 0) repoUpdateManager.updateRepo(repoId)
|
||||
else repoUpdateManager.updateRepos()
|
||||
Result.success()
|
||||
|
||||
@@ -17,8 +17,11 @@ import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.joinAll
|
||||
import mu.KotlinLogging
|
||||
import org.fdroid.NotificationManager
|
||||
import org.fdroid.NotificationManager.Companion.NOTIFICATION_ID_APP_INSTALLS
|
||||
@@ -96,14 +99,13 @@ class AppUpdateWorker @AssistedInject constructor(
|
||||
log.error(e) { "Error while running setForeground: " }
|
||||
}
|
||||
return try {
|
||||
currentCoroutineContext().ensureActive()
|
||||
nm.cancelAppUpdatesAvailableNotification()
|
||||
// Updating apps will try start a foreground service
|
||||
// and it will "share" the same notification.
|
||||
// This is easier than trying to tell the [AppInstallManager]
|
||||
// not to start a foreground service in this specific case.
|
||||
updatesManager.updateAll().forEach { job ->
|
||||
job.join()
|
||||
}
|
||||
updatesManager.updateAll().joinAll()
|
||||
// show success notification, if at least one app got installed
|
||||
val notificationState = appInstallManager.installNotificationState
|
||||
if (notificationState.numInstalled > 0) {
|
||||
|
||||
@@ -5,6 +5,8 @@ import androidx.core.os.LocaleListCompat
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
@@ -117,10 +119,12 @@ class UpdatesManager @Inject constructor(
|
||||
val concurrencyLimit = min(Runtime.getRuntime().availableProcessors(), 8)
|
||||
val semaphore = Semaphore(concurrencyLimit)
|
||||
return appsToUpdate.map { update ->
|
||||
currentCoroutineContext().ensureActive()
|
||||
// launch a new co-routine for each app to update
|
||||
coroutineScope.launch {
|
||||
// suspend here until we get a permit from the semaphore (there's free workers)
|
||||
semaphore.withPermit {
|
||||
currentCoroutineContext().ensureActive()
|
||||
updateApp(update, canAskPreApprovalNow)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user