diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/domain/Tracker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/domain/Tracker.kt index 2c004ca9d..115fc4dfb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/domain/Tracker.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/domain/Tracker.kt @@ -6,6 +6,7 @@ import org.koitharu.kotatsu.core.model.getPreferredBranch import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.core.util.CompositeMutex2 import org.koitharu.kotatsu.history.data.HistoryRepository import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.tracker.domain.model.MangaTracking @@ -13,6 +14,8 @@ import org.koitharu.kotatsu.tracker.domain.model.MangaUpdates import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels import org.koitharu.kotatsu.tracker.work.TrackingItem import javax.inject.Inject +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract class Tracker @Inject constructor( private val settings: AppSettings, @@ -77,7 +80,10 @@ class Tracker @Inject constructor( repository.gc() } - suspend fun fetchUpdates(track: MangaTracking, commit: Boolean): MangaUpdates.Success { + suspend fun fetchUpdates( + track: MangaTracking, + commit: Boolean + ): MangaUpdates.Success = withMangaLock(track.manga.id) { val repo = mangaRepositoryFactory.create(track.manga.source) require(repo is RemoteMangaRepository) { "Repository ${repo.javaClass.simpleName} is not supported" } val manga = repo.getDetails(track.manga, CachePolicy.WRITE_ONLY) @@ -99,7 +105,7 @@ class Tracker @Inject constructor( } @VisibleForTesting - suspend fun deleteTrack(mangaId: Long) { + suspend fun deleteTrack(mangaId: Long) = withMangaLock(mangaId) { repository.deleteTrack(mangaId) } @@ -137,4 +143,21 @@ class Tracker @Inject constructor( } } } + + private companion object { + + private val mangaMutex = CompositeMutex2() + + suspend inline fun withMangaLock(id: Long, action: () -> T): T { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + } + mangaMutex.lock(id) + try { + return action() + } finally { + mangaMutex.unlock(id) + } + } + } }