Skip to content

Commit

Permalink
import updates from 3.5 for Scope parallele resolution fix - #1561
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaudgiuliani committed Dec 19, 2023
1 parent 3290c04 commit 4f7ab74
Show file tree
Hide file tree
Showing 14 changed files with 144 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
*/
package org.koin.core.instance

import org.koin.core.Koin
import org.koin.core.parameter.ParametersDefinition
import org.koin.core.logger.Logger
import org.koin.core.parameter.ParametersHolder
import org.koin.core.scope.Scope

Expand All @@ -26,7 +25,7 @@ import org.koin.core.scope.Scope
* Help support DefinitionContext & DefinitionParameters when resolving definition function
*/
class InstanceContext(
val koin: Koin,
val logger: Logger,
val scope: Scope,
val parameters: ParametersHolder? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ abstract class InstanceFactory<T>(val beanDefinition: BeanDefinition<T>) : Locka
* @return T
*/
open fun create(context: InstanceContext): T {
val koin = context.koin
if (koin.logger.isAt(Level.DEBUG)){
koin.logger.debug("| create instance for $beanDefinition")
val logger = context.logger
if (logger.isAt(Level.DEBUG)){
logger.debug("| create instance for $beanDefinition")
}
try {
val parameters: ParametersHolder = context.parameters ?: emptyParametersHolder()
Expand All @@ -57,7 +57,7 @@ abstract class InstanceFactory<T>(val beanDefinition: BeanDefinition<T>) : Locka
)
} catch (e: Exception) {
val stack = KoinPlatformTools.getStackTrace(e)
koin.logger.error("Instance creation error : could not create instance for $beanDefinition: $stack")
logger.error("Instance creation error : could not create instance for $beanDefinition: $stack")
throw InstanceCreationException("Could not create instance for $beanDefinition", e)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,5 @@ package org.koin.core.logger
*/
class EmptyLogger : Logger(Level.NONE) {

override fun log(level: Level, msg: MESSAGE) {
}
override fun display(level: Level, msg: MESSAGE) {}
}
37 changes: 17 additions & 20 deletions core/koin-core/src/commonMain/kotlin/org/koin/core/logger/Logger.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2021 the original author or authors.
* Copyright 2017-Present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,42 +22,39 @@ package org.koin.core.logger
*/
abstract class Logger(var level: Level = Level.INFO) {

abstract fun log(level: Level, msg: MESSAGE)

private fun canLog(level: Level): Boolean = this.level <= level

private fun doLog(level: Level, msg: MESSAGE) {
if (canLog(level)) {
log(level, msg)
}
}
abstract fun display(level: Level, msg: MESSAGE)

fun debug(msg: MESSAGE) {
doLog(Level.DEBUG, msg)
log(Level.DEBUG, msg)
}

fun info(msg: MESSAGE) {
doLog(Level.INFO, msg)
log(Level.INFO, msg)
}

fun warn(msg: MESSAGE) {
log(Level.WARNING, msg)
}

fun error(msg: MESSAGE) {
doLog(Level.ERROR, msg)
log(Level.ERROR, msg)
}

fun isAt(lvl: Level): Boolean = this.level <= lvl

fun log(lvl: Level, msg : () -> String){
if (isAt(lvl)) doLog(lvl,msg())
fun log(lvl: Level, msg: String) {
if (isAt(lvl)) display(lvl, msg)
}

fun log(lvl: Level, msg: () -> String) {
if (isAt(lvl)) display(lvl, msg())
}
}

const val KOIN_TAG = "[Koin]"

/**
* Log Level
*/
enum class Level {
DEBUG, INFO, ERROR, NONE
DEBUG, INFO, WARNING, ERROR, NONE
}

typealias MESSAGE = String
typealias MESSAGE = String
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class InstanceRegistry(val _koin: Koin) {
if (_koin.logger.isAt(Level.DEBUG)) {
_koin.logger.debug("Creating eager instances ...")
}
val defaultContext = InstanceContext(_koin, _koin.scopeRegistry.rootScope)
val defaultContext = InstanceContext(_koin.logger, _koin.scopeRegistry.rootScope)
eagerInstances.forEach { factory ->
factory.get(defaultContext)
}
Expand Down
80 changes: 43 additions & 37 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 @@ -27,10 +27,13 @@ import org.koin.core.logger.Logger
import org.koin.core.parameter.ParametersDefinition
import org.koin.core.parameter.ParametersHolder
import org.koin.core.qualifier.Qualifier
import org.koin.core.time.Timer
import org.koin.core.time.measureDurationForResult
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 @@ -54,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 @@ -195,12 +198,15 @@ data class Scope(
): T {
return if (_koin.logger.isAt(Level.DEBUG)) {
val qualifierString = qualifier?.let { " with qualifier '$qualifier'" } ?: ""
_koin.logger.debug("+- '${clazz.getFullName()}'$qualifierString")
val (instance: T, duration: Double) = measureDurationForResult {
resolveInstance<T>(qualifier, clazz, parameters)
}
_koin.logger.debug("|- '${clazz.getFullName()}' in $duration ms")
return instance
_koin.logger.display(Level.DEBUG, "|- '${clazz.getFullName()}'$qualifierString ...")

val start = KoinPlatformTimeTools.getTimeInNanoSeconds()
val instance = resolveInstance<T>(qualifier, clazz, parameters)
val stop = KoinPlatformTimeTools.getTimeInNanoSeconds()
val duration = (stop - start) / Timer.NANO_TO_MILLI

_koin.logger.display(Level.DEBUG, "|- '${clazz.getFullName()}' in $duration ms")
instance
} else {
resolveInstance(qualifier, clazz, parameters)
}
Expand All @@ -216,15 +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) { "| put parameters on stack $parameters " }
_parameterStack.addFirst(parameters)
_koin.logger.log(Level.DEBUG) { "| >> parameters $parameters " }
localDeque = _parameterStackLocal.get() ?: ArrayDeque<ParametersHolder>().also(_parameterStackLocal::set)
localDeque.addFirst(parameters)
}
val instanceContext = InstanceContext(_koin, this, parameters)
val instanceContext = InstanceContext(_koin.logger, this, parameters)
val value = resolveValue<T>(qualifier, clazz, instanceContext, parameterDef)
if (parameters != null) {
_koin.logger.log(Level.DEBUG) { "| remove parameters from stack" }
_parameterStack.removeFirstOrNull()
if (localDeque != null) {
_koin.logger.debug("| << parameters")
localDeque.removeFirstOrNull()
}
return value
}
Expand All @@ -234,26 +242,31 @@ data class Scope(
clazz: KClass<*>,
instanceContext: InstanceContext,
parameterDef: ParametersDefinition?
) = (_koin.instanceRegistry.resolveInstance(qualifier, clazz, this.scopeQualifier, instanceContext)
) = (
_koin.instanceRegistry.resolveInstance(qualifier, clazz, this.scopeQualifier, instanceContext)
?: run {
_koin.logger.log(Level.DEBUG) { "- lookup? t:'${clazz.getFullName()}' - q:'$qualifier' look in injected parameters" }
_parameterStack.firstOrNull()?.getOrNull<T>(clazz)
_koin.logger.debug("|- ? t:'${clazz.getFullName()}' - q:'$qualifier' look in injected parameters")
_parameterStackLocal.get()?.firstOrNull()?.getOrNull<T>(clazz)
}
?: run {
_koin.logger.log(Level.DEBUG) { "- lookup? t:'${clazz.getFullName()}' - q:'$qualifier' look at scope source" }
_source?.let {
if (clazz.isInstance(it)) {
_source as? T
} else null
}
if (!isRoot){
_koin.logger.debug("|- ? t:'${clazz.getFullName()}' - q:'$qualifier' look at scope source" )
_source?.let { source ->
if (clazz.isInstance(source) && qualifier == null) {
_source as? T
} else null
}
} else null
}
?: run {
_koin.logger.log(Level.DEBUG) { "- lookup? t:'${clazz.getFullName()}' - q:'$qualifier' look in other scopes" }
_koin.logger.debug("|- ? t:'${clazz.getFullName()}' - q:'$qualifier' look in other scopes" )
findInOtherScope<T>(clazz, qualifier, parameterDef)
}
?: run {
_parameterStack.clear()
_koin.logger.log(Level.DEBUG) { "| clear parameter stack" }
if (parameterDef != null) {
_parameterStackLocal.remove()
_koin.logger.debug("|- << parameters")
}
throwDefinitionNotFound(qualifier, clazz)
})

Expand Down Expand Up @@ -340,7 +353,7 @@ data class Scope(
* @return list of instances of type T
*/
fun <T> getAll(clazz: KClass<*>): List<T> {
val context = InstanceContext(_koin, this)
val context = InstanceContext(_koin.logger, this)
return _koin.instanceRegistry.getAll<T>(clazz, context) + linkedScopes.flatMap { scope -> scope.getAll(clazz) }
}

Expand Down Expand Up @@ -396,19 +409,12 @@ data class Scope(
* Close all instances from this scope
*/
fun close() = KoinPlatformTools.synchronized(this) {
_closed = true
clearData()
_koin.scopeRegistry.deleteScope(this)
}

private fun clearData() {
_source = null
if (_koin.logger.isAt(Level.DEBUG)) {
_koin.logger.info("closing scope:'$id'")
}
// call on close from callbacks
_koin.logger.debug("|- (-) Scope - id:'$id'")
_callbacks.forEach { it.onScopeClose(this) }
_callbacks.clear()
_source = null
_closed = true
_koin.scopeRegistry.deleteScope(this)
}

override fun toString(): String {
Expand Down
31 changes: 31 additions & 0 deletions core/koin-core/src/commonMain/kotlin/org/koin/core/time/Timer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.koin.core.time

import org.koin.mp.KoinPlatformTimeTools
import kotlin.time.Duration
import kotlin.time.Duration.Companion.ZERO
import kotlin.time.DurationUnit
import kotlin.time.toDuration

class Timer {

val start: Duration = KoinPlatformTimeTools.getTimeInNanoSeconds().toDuration(DurationUnit.NANOSECONDS)
var end: Duration = ZERO
private set
private var time: Duration = ZERO

fun stop() {
if (end == ZERO) {
end = KoinPlatformTimeTools.getTimeInNanoSeconds().toDuration(DurationUnit.NANOSECONDS)
time = end - start
}
}

fun getTimeInSeconds() = time.toDouble(DurationUnit.SECONDS)
fun getTimeInMillis() = time.toDouble(DurationUnit.MILLISECONDS)
fun getTimeInNanos() = time.toDouble(DurationUnit.NANOSECONDS)

companion object {
const val NANO_TO_MILLI = 1_000_000.0
fun start(): Timer = Timer()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,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 @@ -21,9 +21,7 @@ package org.koin.core.logger
*/
class PrintLogger(level: Level = Level.INFO) : Logger(level) {

override fun log(level: Level, msg: MESSAGE) {
if (this.level <= level) {
println("[$level] $KOIN_TAG $msg")
}
override fun display(level: Level, msg: MESSAGE) {
println("[$level] $KOIN_TAG $msg")
}
}
25 changes: 24 additions & 1 deletion core/koin-core/src/jsMain/kotlin/org/koin/mp/PlatformTools.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,27 @@ 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>

// Import from Stately
var <T> ThreadLocalRef<T>.value: T?
get() = get()
set(value) {
set(value)
}

open class ThreadLocalRef<T> {
private var localValue: T? = null

fun remove() {
value = null
}

fun get(): T? = localValue

fun set(value: T?) {
localValue = value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ package org.koin.core.logger
*/
class PrintLogger(level: Level = Level.INFO) : Logger(level) {

override fun log(level: Level, msg: MESSAGE) {
if (this.level <= level) {
val printer = if (level >= Level.ERROR) System.err else System.out
printer.println("[$level] $KOIN_TAG $msg")
}
private val printer = if (level >= Level.WARNING) System.err else System.out

override fun display(level: Level, msg: MESSAGE) {
printer.println("[$level] $KOIN_TAG $msg")
}
}
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 @@ -21,9 +21,7 @@ package org.koin.core.logger
*/
class PrintLogger(level: Level = Level.INFO) : Logger(level) {

override fun log(level: Level, msg: MESSAGE) {
if (this.level <= level) {
println("[$level] $KOIN_TAG $msg")
}
override fun display(level: Level, msg: MESSAGE) {
println("[$level] $KOIN_TAG $msg")
}
}
Loading

0 comments on commit 4f7ab74

Please sign in to comment.