@@ -5,7 +5,9 @@ package fr.frankois944.ktorfilecaching
55import io.ktor.client.plugins.cache.storage.CacheStorage
66import io.ktor.client.plugins.cache.storage.CachedResponseData
77import io.ktor.http.Url
8+ import io.ktor.util.collections.ConcurrentMap
89import kotlinx.coroutines.CoroutineDispatcher
10+ import kotlinx.coroutines.coroutineScope
911import kotlinx.coroutines.sync.Mutex
1012import kotlinx.coroutines.sync.withLock
1113import kotlinx.coroutines.withContext
@@ -36,7 +38,7 @@ internal class FileCacheStorage(
3638 val dispatcher : CoroutineDispatcher ,
3739 val fileSystem : FileSystem = filesystem(),
3840) : CacheStorage {
39- private val lock = Mutex () // Coroutine-friendly lock for thread safety
41+ private val mutexes = ConcurrentMap < String , Mutex >()
4042 private val baseDir = " $directoryPath${Path .DIRECTORY_SEPARATOR }$storedCacheDirectory "
4143
4244 init {
@@ -74,25 +76,26 @@ internal class FileCacheStorage(
7476 private suspend fun writeCache (
7577 urlHex : String ,
7678 caches : List <CachedResponseData >,
77- ) = withContext(dispatcher) {
78- lock.withLock {
79+ ) = coroutineScope {
80+ val mutex = mutexes.computeIfAbsent(urlHex) { Mutex () }
81+ mutex.withLock {
7982 val filePath = " $baseDir${Path .DIRECTORY_SEPARATOR }$urlHex " .toPath()
8083 val serializedData = Cbor .encodeToByteArray(caches.map { SerializableCachedResponseData (it) })
8184 fileSystem.write(filePath) { write(serializedData) }
8285 }
8386 }
8487
85- private suspend fun readCache (urlHex : String ): List <CachedResponseData > =
86- withContext(dispatcher) {
87- lock.withLock {
88- val filePath = " $baseDir${Path .DIRECTORY_SEPARATOR }$urlHex " .toPath()
89- if (! fileSystem.exists(filePath)) return @withContext emptyList()
90- return @withContext try {
91- val bytes = fileSystem.read(filePath) { readByteArray() }
92- Cbor .decodeFromByteArray<List <SerializableCachedResponseData >>(bytes).map { it.cachedResponseData }
93- } catch (e: Exception ) {
94- emptyList()
95- }
88+ private suspend fun readCache (urlHex : String ): List <CachedResponseData > {
89+ val mutex = mutexes.computeIfAbsent(urlHex) { Mutex () }
90+ return mutex.withLock {
91+ val filePath = " $baseDir${Path .DIRECTORY_SEPARATOR }$urlHex " .toPath()
92+ if (! fileSystem.exists(filePath)) return emptyList()
93+ try {
94+ val bytes = fileSystem.read(filePath) { readByteArray() }
95+ Cbor .decodeFromByteArray<List <SerializableCachedResponseData >>(bytes).map { it.cachedResponseData }
96+ } catch (e: Exception ) {
97+ emptyList()
9698 }
9799 }
100+ }
98101}
0 commit comments