Skip to content

Commit

Permalink
Fixing race condition in Scope
Browse files Browse the repository at this point in the history
  • Loading branch information
octa-one committed Apr 18, 2023
1 parent 53898e3 commit 1811bb6
Show file tree
Hide file tree
Showing 7 changed files with 28 additions and 16 deletions.
1 change: 1 addition & 0 deletions core/koin-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ kotlin {
dependsOn commonMain
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib-js'
implementation "co.touchlab:stately-concurrency:1.2.2"
}
}

Expand Down
21 changes: 9 additions & 12 deletions core/koin-core/src/commonMain/kotlin/org/koin/core/scope/Scope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import org.koin.ext.getFullName
import org.koin.mp.KoinPlatformTimeTools
import org.koin.mp.KoinPlatformTools
import org.koin.mp.Lockable
import org.koin.mp.ThreadLocal
import kotlin.reflect.KClass

@OptIn(KoinInternalApi::class)
Expand All @@ -56,7 +57,7 @@ data class Scope(
private val _callbacks = arrayListOf<ScopeCallback>()

@KoinInternalApi
val _parameterStack = ArrayDeque<ParametersHolder>()
val _parameterStackLocal = ThreadLocal<ArrayDeque<ParametersHolder>>()

private var _closed: Boolean = false
val logger: Logger get() = _koin.logger
Expand Down Expand Up @@ -221,19 +222,17 @@ data class Scope(
throw ClosedScopeException("Scope '$id' is closed")
}
val parameters = parameterDef?.invoke()
var localDeque: ArrayDeque<ParametersHolder>? = null
if (parameters != null) {
_koin.logger.log(Level.DEBUG){ "| >> parameters $parameters " }
KoinPlatformTools.synchronized(this@Scope) {
_parameterStack.addFirst(parameters)
}
localDeque = _parameterStackLocal.get() ?: ArrayDeque<ParametersHolder>().also(_parameterStackLocal::set)
localDeque.addFirst(parameters)
}
val instanceContext = InstanceContext(_koin.logger, this, parameters)
val value = resolveValue<T>(qualifier, clazz, instanceContext, parameterDef)
if (parameters != null) {
if (localDeque != null) {
_koin.logger.debug("| << parameters")
KoinPlatformTools.synchronized(this@Scope) {
_parameterStack.removeFirstOrNull()
}
localDeque.removeFirstOrNull()
}
return value
}
Expand All @@ -246,7 +245,7 @@ data class Scope(
) = (_koin.instanceRegistry.resolveInstance(qualifier, clazz, this.scopeQualifier, instanceContext)
?: run {
_koin.logger.debug("|- ? t:'${clazz.getFullName()}' - q:'$qualifier' look in injected parameters")
_parameterStack.firstOrNull()?.getOrNull<T>(clazz)
_parameterStackLocal.get()?.firstOrNull()?.getOrNull<T>(clazz)
}
?: run {
_koin.logger.debug("|- ? t:'${clazz.getFullName()}' - q:'$qualifier' look at scope source" )
Expand All @@ -261,9 +260,7 @@ data class Scope(
findInOtherScope<T>(clazz, qualifier, parameterDef)
}
?: run {
KoinPlatformTools.synchronized(this@Scope) {
_parameterStack.clear()
}
_parameterStackLocal.remove()
_koin.logger.debug("|- << parameters" )
throwDefinitionNotFound(qualifier, clazz)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,10 @@ expect object KoinPlatformTools {
fun <K, V> safeHashMap(): MutableMap<K, V>
}

expect open class Lockable()
expect open class Lockable()

expect open class ThreadLocal<T>() {
fun get(): T?
fun set(value: T?)
fun remove()
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ class ParameterStackTest {
koin.get<Simple.MyStringFactory> { parametersOf(KoinPlatformTools.generateId()) }
}

assertTrue(koin.scopeRegistry.rootScope._parameterStack.isEmpty())
assertTrue(koin.scopeRegistry.rootScope._parameterStackLocal.get()!!.isEmpty())
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.koin.mp

import co.touchlab.stately.concurrency.ThreadLocalRef
import org.koin.core.context.GlobalContext
import org.koin.core.context.KoinContext
import org.koin.core.logger.*
Expand Down Expand Up @@ -36,4 +37,6 @@ actual object KoinPlatformTools {
actual fun <K, V> safeHashMap(): MutableMap<K, V> = HashMap()
}

actual typealias Lockable = Any
actual typealias Lockable = Any

actual typealias ThreadLocal<T> = ThreadLocalRef<T>
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ actual object KoinPlatformTools {
actual fun <K, V> safeHashMap(): MutableMap<K, V> = ConcurrentHashMap<K, V>()
}

actual typealias Lockable = Any
actual typealias Lockable = Any

actual typealias ThreadLocal<T> = java.lang.ThreadLocal<T>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.koin.mp

import co.touchlab.stately.concurrency.Lock
import co.touchlab.stately.concurrency.withLock
import co.touchlab.stately.concurrency.ThreadLocalRef
import org.koin.core.context.KoinContext
import org.koin.core.context.globalContextByMemoryModel
import org.koin.core.logger.Level
Expand Down Expand Up @@ -39,3 +40,5 @@ actual object KoinPlatformTools {
actual open class Lockable {
internal val lock = Lock()
}

actual typealias ThreadLocal<T> = ThreadLocalRef<T>

0 comments on commit 1811bb6

Please sign in to comment.