diff --git a/coil-core/api/android/coil-core.api b/coil-core/api/android/coil-core.api index b3959b3d8f..cc21416bad 100644 --- a/coil-core/api/android/coil-core.api +++ b/coil-core/api/android/coil-core.api @@ -419,6 +419,7 @@ public abstract interface class coil3/disk/DiskCache { public final class coil3/disk/DiskCache$Builder { public fun ()V public final fun build ()Lcoil3/disk/DiskCache; + public final fun cleanupCoroutineContext (Lkotlin/coroutines/CoroutineContext;)Lcoil3/disk/DiskCache$Builder; public final fun cleanupDispatcher (Lkotlinx/coroutines/CoroutineDispatcher;)Lcoil3/disk/DiskCache$Builder; public final fun directory (Lokio/Path;)Lcoil3/disk/DiskCache$Builder; public final fun fileSystem (Lokio/FileSystem;)Lcoil3/disk/DiskCache$Builder; diff --git a/coil-core/api/coil-core.klib.api b/coil-core/api/coil-core.klib.api index 460e2fc98b..762882a339 100644 --- a/coil-core/api/coil-core.klib.api +++ b/coil-core/api/coil-core.klib.api @@ -170,6 +170,7 @@ abstract interface coil3.disk/DiskCache { // coil3.disk/DiskCache|null[0] constructor () // coil3.disk/DiskCache.Builder.|(){}[0] final fun build(): coil3.disk/DiskCache // coil3.disk/DiskCache.Builder.build|build(){}[0] + final fun cleanupCoroutineContext(kotlin.coroutines/CoroutineContext): coil3.disk/DiskCache.Builder // coil3.disk/DiskCache.Builder.cleanupCoroutineContext|cleanupCoroutineContext(kotlin.coroutines.CoroutineContext){}[0] final fun cleanupDispatcher(kotlinx.coroutines/CoroutineDispatcher): coil3.disk/DiskCache.Builder // coil3.disk/DiskCache.Builder.cleanupDispatcher|cleanupDispatcher(kotlinx.coroutines.CoroutineDispatcher){}[0] final fun directory(okio/Path): coil3.disk/DiskCache.Builder // coil3.disk/DiskCache.Builder.directory|directory(okio.Path){}[0] final fun fileSystem(okio/FileSystem): coil3.disk/DiskCache.Builder // coil3.disk/DiskCache.Builder.fileSystem|fileSystem(okio.FileSystem){}[0] diff --git a/coil-core/api/jvm/coil-core.api b/coil-core/api/jvm/coil-core.api index 3368c2cd33..cd7cfec1dd 100644 --- a/coil-core/api/jvm/coil-core.api +++ b/coil-core/api/jvm/coil-core.api @@ -344,6 +344,7 @@ public abstract interface class coil3/disk/DiskCache { public final class coil3/disk/DiskCache$Builder { public fun ()V public final fun build ()Lcoil3/disk/DiskCache; + public final fun cleanupCoroutineContext (Lkotlin/coroutines/CoroutineContext;)Lcoil3/disk/DiskCache$Builder; public final fun cleanupDispatcher (Lkotlinx/coroutines/CoroutineDispatcher;)Lcoil3/disk/DiskCache$Builder; public final fun directory (Lokio/Path;)Lcoil3/disk/DiskCache$Builder; public final fun fileSystem (Lokio/FileSystem;)Lcoil3/disk/DiskCache$Builder; diff --git a/coil-core/src/commonMain/kotlin/coil3/disk/DiskCache.kt b/coil-core/src/commonMain/kotlin/coil3/disk/DiskCache.kt index b2007baa47..d9f6e0a4c9 100644 --- a/coil-core/src/commonMain/kotlin/coil3/disk/DiskCache.kt +++ b/coil-core/src/commonMain/kotlin/coil3/disk/DiskCache.kt @@ -1,8 +1,9 @@ package coil3.disk import coil3.util.defaultFileSystem -import coil3.util.ioCoroutineDispatcher import coil3.util.remainingFreeSpaceBytes +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext import kotlinx.coroutines.CoroutineDispatcher import okio.FileSystem import okio.Path @@ -115,7 +116,7 @@ interface DiskCache { private var minimumMaxSizeBytes = 10L * 1024 * 1024 // 10MB private var maximumMaxSizeBytes = 250L * 1024 * 1024 // 250MB private var maxSizeBytes = 0L - private var cleanupDispatcher = ioCoroutineDispatcher() + private var cleanupCoroutineContext: CoroutineContext = EmptyCoroutineContext /** * Set the [directory] where the cache stores its data. @@ -171,12 +172,19 @@ interface DiskCache { } /** - * Set the [CoroutineDispatcher] that cache size trim operations will be executed on. + * Set the [CoroutineContext] that cache size trim operations will be executed in. */ - fun cleanupDispatcher(dispatcher: CoroutineDispatcher) = apply { - this.cleanupDispatcher = dispatcher + fun cleanupCoroutineContext(context: CoroutineContext) = apply { + cleanupCoroutineContext = context } + @Deprecated( + message = "Replaced by cleanupCoroutineContext.", + replaceWith = ReplaceWith("cleanupCoroutineContext(dispatcher)"), + ) + fun cleanupDispatcher(dispatcher: CoroutineDispatcher) = + cleanupCoroutineContext(dispatcher) + /** * Create a new [DiskCache] instance. */ @@ -196,7 +204,7 @@ interface DiskCache { maxSize = maxSize, directory = directory, fileSystem = fileSystem, - cleanupDispatcher = cleanupDispatcher, + cleanupCoroutineContext = cleanupCoroutineContext, ) } } diff --git a/coil-core/src/commonMain/kotlin/coil3/disk/DiskLruCache.kt b/coil-core/src/commonMain/kotlin/coil3/disk/DiskLruCache.kt index 664b34fd44..e0a8bfbd66 100644 --- a/coil-core/src/commonMain/kotlin/coil3/disk/DiskLruCache.kt +++ b/coil-core/src/commonMain/kotlin/coil3/disk/DiskLruCache.kt @@ -19,10 +19,12 @@ import coil3.annotation.VisibleForTesting import coil3.util.LruMutableMap import coil3.util.createFile import coil3.util.deleteContents +import coil3.util.dispatcher import coil3.util.forEachIndices +import coil3.util.ioCoroutineDispatcher +import kotlin.coroutines.CoroutineContext import kotlinx.atomicfu.locks.SynchronizedObject import kotlinx.atomicfu.locks.synchronized -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel @@ -77,14 +79,14 @@ import okio.buffer * @constructor Create a cache which will reside in [directory]. This cache is lazily initialized on * first access and will be created if it does not exist. * @param directory a writable directory. - * @param cleanupDispatcher the dispatcher to run cache size trim operations on. + * @param cleanupCoroutineContext the context to run cache size trim operations in. * @param valueCount the number of values per cache entry. Must be positive. * @param maxSize the maximum number of bytes this cache should use to store. */ internal class DiskLruCache( fileSystem: FileSystem, private val directory: Path, - cleanupDispatcher: CoroutineDispatcher, + cleanupCoroutineContext: CoroutineContext, private val maxSize: Long, private val appVersion: Int, private val valueCount: Int, @@ -139,8 +141,11 @@ internal class DiskLruCache( private val journalFileTmp = directory / JOURNAL_FILE_TMP private val journalFileBackup = directory / JOURNAL_FILE_BACKUP private val lruEntries = LruMutableMap() - private val cleanupScope = - CoroutineScope(SupervisorJob() + cleanupDispatcher.limitedParallelism(1)) + private val cleanupScope = CoroutineScope( + cleanupCoroutineContext + + SupervisorJob() + + (cleanupCoroutineContext.dispatcher ?: ioCoroutineDispatcher()).limitedParallelism(1), + ) private val lock = SynchronizedObject() private var size = 0L private var operationsSinceRewrite = 0 diff --git a/coil-core/src/commonMain/kotlin/coil3/disk/RealDiskCache.kt b/coil-core/src/commonMain/kotlin/coil3/disk/RealDiskCache.kt index 0fc19b0149..f75f9dc602 100644 --- a/coil-core/src/commonMain/kotlin/coil3/disk/RealDiskCache.kt +++ b/coil-core/src/commonMain/kotlin/coil3/disk/RealDiskCache.kt @@ -3,7 +3,7 @@ package coil3.disk import coil3.disk.DiskCache.Editor import coil3.disk.DiskCache.Snapshot import coil3.util.closeQuietly -import kotlinx.coroutines.CoroutineDispatcher +import kotlin.coroutines.CoroutineContext import okio.ByteString.Companion.encodeUtf8 import okio.FileSystem import okio.Path @@ -12,13 +12,13 @@ internal class RealDiskCache( override val maxSize: Long, override val directory: Path, override val fileSystem: FileSystem, - cleanupDispatcher: CoroutineDispatcher, + cleanupCoroutineContext: CoroutineContext, ) : DiskCache { private val cache = DiskLruCache( fileSystem = fileSystem, directory = directory, - cleanupDispatcher = cleanupDispatcher, + cleanupCoroutineContext = cleanupCoroutineContext, maxSize = maxSize, appVersion = 3, valueCount = 2, diff --git a/coil-core/src/commonMain/kotlin/coil3/util/utils.kt b/coil-core/src/commonMain/kotlin/coil3/util/utils.kt index 3f716aaa83..8dd4a6f783 100644 --- a/coil-core/src/commonMain/kotlin/coil3/util/utils.kt +++ b/coil-core/src/commonMain/kotlin/coil3/util/utils.kt @@ -12,8 +12,10 @@ import coil3.intercept.RealInterceptorChain import coil3.request.ErrorResult import coil3.request.ImageRequest import coil3.request.NullRequestDataException +import kotlin.coroutines.CoroutineContext import kotlin.experimental.ExperimentalNativeApi import kotlin.reflect.KClass +import kotlinx.coroutines.CoroutineDispatcher import okio.Closeable internal expect fun println(level: Logger.Level, tag: String, message: String) @@ -97,3 +99,7 @@ internal expect class WeakReference(referred: T) { } internal expect fun Image.prepareToDraw() + +@OptIn(ExperimentalStdlibApi::class) +internal val CoroutineContext.dispatcher: CoroutineDispatcher? + get() = get(CoroutineDispatcher)