Repository: InsertKoinIO/koin
File: projects/core/koin-core/src/commonMain/kotlin/org/koin/core/instance/ScopedInstanceFactory.kt (Kotlin)

Code before:
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.koin.core.instance

import org.koin.core.definition.BeanDefinition
import org.koin.core.error.MissingScopeValueException
import org.koin.core.scope.Scope
import org.koin.core.scope.ScopeID
import org.koin.mp.KoinPlatformTools

/**
 * Scope instance holder
 * @author Arnaud Giuliani
 */
class ScopedInstanceFactory<T>(beanDefinition: BeanDefinition<T>, val holdInstance : Boolean = true) :
    InstanceFactory<T>(beanDefinition) {

    private var values = KoinPlatformTools.safeHashMap<ScopeID, T>()

    fun size() = values.size

    @PublishedApi
    internal fun saveValue(id : ScopeID, value : T){
        KoinPlatformTools.synchronized(this) {
            values[id] = value
        }
    }

    override fun isCreated(context: ResolutionContext?): Boolean = KoinPlatformTools.synchronized(this) { (values[context?.scope?.id] != null) }

    override fun drop(scope: Scope?) {
        scope?.let { s ->
            KoinPlatformTools.synchronized(this) {
                val v = values.remove(s.id)
                beanDefinition.callbacks.onClose?.invoke(v)
            }
        }
    }

    override fun create(context: ResolutionContext): T {
        val existing = values[context.scope.id]
        return if (existing != null) {
            existing
        } else {
            val created = super.create(context)

Review comment (suggestion):
**Missing synchronization in `size()` method**: The `size()` method reads from `values` without synchronization, which could result in stale or inconsistent reads in a multi-threaded environment. While `values` uses a `safeHashMap` (thread-safe map), the size could change between the call and the use of the result.

**Suggested fix**:
```kotlin
fun size() = KoinPlatformTools.synchronized(this) { values.size }
```
```suggestion
    fun size() = KoinPlatformTools.synchronized(this) { values.size }
```

Code after:
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.koin.core.instance

import org.koin.core.definition.BeanDefinition
import org.koin.core.error.MissingScopeValueException
import org.koin.core.scope.Scope
import org.koin.core.scope.ScopeID
import org.koin.mp.KoinPlatformTools

/**
 * Scope instance holder
 * @author Arnaud Giuliani
 */
class ScopedInstanceFactory<T>(beanDefinition: BeanDefinition<T>, val holdInstance : Boolean = true) :
    InstanceFactory<T>(beanDefinition) {

    private var values = KoinPlatformTools.safeHashMap<ScopeID, T>()

    fun size() = KoinPlatformTools.synchronized(this) { values.size }

    @PublishedApi
    internal fun saveValue(id : ScopeID, value : T){
        KoinPlatformTools.synchronized(this) {
            values[id] = value
        }
    }

    override fun isCreated(context: ResolutionContext?): Boolean = KoinPlatformTools.synchronized(this) { (values[context?.scope?.id] != null) }

    override fun drop(scope: Scope?) {
        scope?.let { s ->
            KoinPlatformTools.synchronized(this) {
                val v = values.remove(s.id)
                beanDefinition.callbacks.onClose?.invoke(v)
            }
        }
    }

    override fun create(context: ResolutionContext): T {
        val existing = values[context.scope.id]
        return if (existing != null) {
            existing
        } else {
            val created = super.create(context)