Skip to content

Commit

Permalink
feat(core): fix some bugs, add test cases.
Browse files Browse the repository at this point in the history
  • Loading branch information
chuanwise committed Jan 22, 2025
1 parent 7da06cf commit 72d3d95
Show file tree
Hide file tree
Showing 14 changed files with 344 additions and 139 deletions.
215 changes: 113 additions & 102 deletions contexts-core/src/main/kotlin/cn/chuanwise/contexts/Context.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import cn.chuanwise.contexts.util.createAllChildrenBreadthFirstSearchIterator
import cn.chuanwise.contexts.util.createAllParentsBreadthFirstSearchIterator
import cn.chuanwise.contexts.util.read
import cn.chuanwise.contexts.util.remove
import cn.chuanwise.contexts.util.withLocks
import java.lang.reflect.Type
import java.util.concurrent.atomic.AtomicBoolean

Expand All @@ -37,21 +38,39 @@ interface Context : MutableBeans, AutoCloseable {
val key: Any?
val contextManager: ContextManager

/**
* 若当前上下文只有一个父上下文时,可以用此获取其值。
*/
val parent: Context
val parents: List<Context>
val allParents: List<Context>
val parentCount: Int

val singleParent: Context
val singleParentOrNull: Context?

val allParents: List<Context>
val allParentCount: Int

val singleAllParent: Context
val singleAllParentOrNull: Context?

val children: List<Context>
val allChildren: List<Context>
val childCount: Int

val singleChild: Context
val singleChildOrNull: Context?

val allChildren: List<Context>
val allChildCount: Int

val singleAllChild: Context
val singleAllChildOrNull: Context?

val contextBeans: List<MutableBean<*>>
val contextBeanValues: List<*>

fun inAllParents(context: Context): Boolean
fun inAllChildren(context: Context): Boolean

fun inParents(context: Context): Boolean
fun inChildren(context: Context): Boolean

/**
* 联通分量里的所有上下文,即包括自己和所有父子上下文。
*/
Expand All @@ -67,7 +86,6 @@ interface Context : MutableBeans, AutoCloseable {
return getParentByKey(key) ?: throw NoSuchElementException("Cannot find parent with key $key.")
}


/**
* 尝试获取一个带有指定对象的子上下文。
*
Expand Down Expand Up @@ -227,18 +245,49 @@ class ContextImpl(
private val mutableParents: MutableCollection<ContextImpl> = mutableListOf()
private val mutableParentsByKey: MutableMap<Any, ContextImpl> = mutableMapOf()

override val parent: Context get() = lock.read { mutableParents.single() }
override val parents: List<Context> get() = lock.read { mutableParents.toList() }
override val allParents: List<Context> get() = createAllParentsBreadthFirstSearchIterator().asSequence().toList()
override val parentCount: Int get() = lock.read { mutableParents.size }

override val singleParent: Context get() = lock.read { mutableParents.single() }
override val singleParentOrNull: Context? get() = lock.read { mutableParents.singleOrNull() }

override fun inParents(context: Context): Boolean {
return lock.read { context in mutableParents }
}

override val allParents: List<Context> get() = createAllParentsBreadthFirstSearchIterator().asSequence().toList()
override val allParentCount: Int get() = lock.read { mutableParents.size }

override val singleAllParent: Context get() = lock.read { allParents.single() }
override val singleAllParentOrNull: Context? get() = lock.read { allParents.singleOrNull() }

override fun inAllParents(context: Context): Boolean {
return context in allParents
}

private val mutableChildren: MutableCollection<ContextImpl> = mutableListOf()
private val mutableChildrenByKey: MutableMap<Any, ContextImpl> = mutableMapOf()

override val children: List<Context> get() = lock.read { mutableChildren.toList() }
override val allChildren: List<Context> get() = createAllChildrenBreadthFirstSearchIterator().asSequence().toList()
override val childCount: Int get() = lock.read { mutableChildren.size }

override val singleChild: Context get() = lock.read { mutableChildren.single() }
override val singleChildOrNull: Context? get() = lock.read { mutableChildren.singleOrNull() }

override fun inChildren(context: Context): Boolean {
return lock.read { context in mutableChildren }
}

override val allChildren: List<Context> get() = createAllChildrenBreadthFirstSearchIterator().asSequence().toList()
override val allChildCount: Int get() = lock.read { mutableChildren.size }

override val singleAllChild: Context get() = lock.read { allChildren.single() }
override val singleAllChildOrNull: Context? get() = lock.read { allChildren.singleOrNull() }

override fun inAllChildren(context: Context): Boolean {
return context in allChildren
}

override val components: List<Context> get() = allParents + allChildren + this

override val contextBeanValues: List<*> get() = beans.beans.getBeanValues(Any::class.java)
Expand Down Expand Up @@ -294,74 +343,43 @@ class ContextImpl(
}

// 连接两个上下文,不检查是否会成环,不加锁,会通知 context modules: PreAdd, PostAdd。
private fun addNoLock(parent: ContextImpl, child: ContextImpl, replace: Boolean, enter: Boolean): Boolean {
val key = child.key
// 如果 check = true,先确保不是父子关系。否则信任调用者。
private fun addNoLock(parent: ContextImpl, child: ContextImpl, replace: Boolean, enter: Boolean, check: Boolean = true): Boolean {
val childKey = child.key
val parentKey = parent.key

if (check && parent.inChildren(child)) {
return false
}

val contextPreAddEvent = ContextPreAddEventImpl(parent, child, enter, contextManager)
if (key != null) {
if (childKey != null || parentKey != null) {
if (replace) {
val oldChild = parent.mutableChildrenByKey[key]
val oldChild = childKey?.let { parent.mutableChildrenByKey[it] }
if (oldChild != null) {
// 如果已经是子上下文了,直接返回。
if (oldChild === child) {
return false
}

// 否则需要先退出原来的子上下文。
val contextPreRemoveEvent = ContextPreRemoveEventImpl(
parent, child, contextPreAddEvent, exit = false, contextManager
)
contextManager.onContextPreRemove(contextPreRemoveEvent)

check(parent.mutableChildren.remove(oldChild))
check(parent.mutableChildrenByKey.remove(key) === oldChild)

check(oldChild.mutableParents.remove(parent))
check(oldChild.mutableParentsByKey.remove(key) === parent)

val contextPostRemoveEvent = ContextPostRemoveEventImpl(
parent, child, contextPreAddEvent, exit = false, contextManager
)
contextManager.onContextPostRemove(contextPostRemoveEvent)
check(removeNoLock(parent, oldChild, exit = false, check = false))
}

val oldParent = child.mutableParentsByKey[key]
val oldParent = parentKey?.let { child.mutableParentsByKey[it] }
if (oldParent != null) {
// 如果它们之间有父子关系,在前面就已经退出了。
// 所以此处必定是同键的两个上下文,但是不是父子关系。
check(oldParent !== parent)

val contextPreRemoveEvent = ContextPreRemoveEventImpl(
parent, child, contextPreAddEvent, exit = false, contextManager
)
contextManager.onContextPreRemove(contextPreRemoveEvent)

check(child.mutableParents.remove(oldParent))
check(child.mutableParentsByKey.remove(key) === oldParent)

check(oldParent.mutableChildren.remove(child))
check(oldParent.mutableChildrenByKey.remove(key) === child)

val contextPostRemoveEvent = ContextPostRemoveEventImpl(
parent, child, contextPreAddEvent, exit = false, contextManager
)
contextManager.onContextPostRemove(contextPostRemoveEvent)
check(removeNoLock(oldParent, child, exit = false, check = false))
}
} else {
val oldChild = parent.mutableChildrenByKey[key]
val oldChild = parent.mutableChildrenByKey[childKey]
if (oldChild != null) {
return false
}

val oldParent = child.mutableParentsByKey[key]
val oldParent = child.mutableParentsByKey[childKey]
if (oldParent != null) {
return false
}
}

contextManager.onContextPreAdd(contextPreAddEvent)

parent.mutableChildrenByKey[key] = child
child.mutableParentsByKey[key] = parent
childKey?.let { parent.mutableChildrenByKey[childKey] = child }
parentKey?.let { child.mutableParentsByKey[parentKey] = parent }
} else {
contextManager.onContextPreAdd(contextPreAddEvent)
}
Expand All @@ -375,36 +393,43 @@ class ContextImpl(
}

// 退出一个子上下文,不加锁,会通知 context modules: PreRemove, PostRemove。
private fun removeNoLock(parent: ContextImpl, child: ContextImpl, exit: Boolean): Boolean {
val key = child.key
if (key != null) {
val oldChild = parent.mutableChildrenByKey[key]

// 如果它们本来就没有父子关系,则退出。
if (oldChild !== child) {
return false
}
// 如果 check = true,先确保是父子关系。否则信任调用者。
private fun removeNoLock(parent: ContextImpl, child: ContextImpl, exit: Boolean, check: Boolean = true): Boolean {
val childKey = child.key
val parentKey = parent.key

val contextPreRemoveEvent = ContextPreRemoveEventImpl(parent, child, null, exit, contextManager)
contextManager.onContextPreRemove(contextPreRemoveEvent)
if (check && !parent.inChildren(child)) {
return false
}

// 它可能为 null 的原因是 alsoExitChildIfItWillBeRoot,
// 使得 PreRemove 事件发出后可能子已经退出。
val removedChild = parent.mutableChildrenByKey.remove(key)
check(removedChild === child || removedChild == null)
if (childKey != null || parentKey != null) {
if (childKey != null) {
val contextPreRemoveEvent = ContextPreRemoveEventImpl(parent, child, null, exit, contextManager)
contextManager.onContextPreRemove(contextPreRemoveEvent)

val removedParent = child.mutableParentsByKey.remove(key)
check(removedParent === parent || removedParent == null)
// 它可能为 null 的原因是 alsoExitChildIfItWillBeRoot,使得 PreRemove 事件发出后可能子已经退出。
val removedChild = parent.mutableChildrenByKey.remove(childKey)
check(removedChild === child || removedChild == null)
}
if (parentKey != null) {
val contextPreRemoveEvent = ContextPreRemoveEventImpl(parent, child, null, exit, contextManager)
contextManager.onContextPreRemove(contextPreRemoveEvent)

// 它可能为 null 的原因是 alsoExitChildIfItWillBeRoot,使得 PreRemove 事件发出后可能子已经退出。
val removedParent = child.mutableParentsByKey.remove(parentKey)
check(removedParent === parent || removedParent == null)
}
} else {
val contextPreRemoveEvent = ContextPreRemoveEventImpl(parent, child, null, exit, contextManager)
contextManager.onContextPreRemove(contextPreRemoveEvent)
}

parent.mutableChildren.remove(child) && child.mutableParents.remove(parent)

val contextPostRemoveEvent = ContextPostRemoveEventImpl(parent, child, null, exit, contextManager)
contextManager.onContextPostRemove(contextPostRemoveEvent)

// 前面虽然检查了状态,但是因为可能 alsoExitChildIfItWillBeRoot,使得此处不一定能 remove 掉。
// 如果能的话再发事件,否则事件已经在此之前发布过了,就不再发送了。
if (parent.mutableChildren.remove(child) && child.mutableParents.remove(parent)) {
val contextPostRemoveEvent = ContextPostRemoveEventImpl(parent, child, null, exit, contextManager)
contextManager.onContextPostRemove(contextPostRemoveEvent)
}
return true
}

Expand All @@ -418,7 +443,7 @@ class ContextImpl(
val contextPreEnterEvent = ContextPreEnterEventImpl(context, contextManager)
contextManager.onContextPreEnter(contextPreEnterEvent)

val result = addNoLock(this, context, replace, enter = true)
val result = addNoLock(this, context, replace, enter = true, check = false)
if (result) {
val contextPostEnterEvent = ContextPostEnterEventImpl(context, contextManager)
contextManager.onContextPostEnter(contextPostEnterEvent)
Expand Down Expand Up @@ -449,30 +474,16 @@ class ContextImpl(
@Suppress("UNCHECKED_CAST")
override fun addChild(child: Context, replace: Boolean): Boolean {
require(child is ContextImpl) { "Only ContextImpl is supported." }

checkNotExited()
child.checkNotExited()

return lock.add {
// 检查是否会形成环。
val allParents = allParents as List<ContextImpl>
require(child !in allParents) { "Cannot connect child to parent, because it will form a cycle." }

// 获取整个连通子图的锁
val components = (components + child.components) as List<ContextImpl>
val componentLocks = components.map { it.lock }
val allParents = allParents as List<ContextImpl>
val locks = allParents.map { it.lock.readLock } + lock.addLock

componentLocks.read {
val contextPreAddEvent = ContextPreAddEventImpl(this, child, isEntering = false, contextManager)
contextManager.onContextPreAdd(contextPreAddEvent)

val result = addNoLock(this, child, enter = false, replace = replace)
if (result) {
val contextPostAddEvent = ContextPostAddEventImpl(this, child, isEntering = false, contextManager)
contextManager.onContextPostAdd(contextPostAddEvent)
}

result
}
return locks.withLocks {
require(child !in allParents) { "Cannot connect child to parent, because it will form a cycle." }
addNoLock(this, child, enter = false, replace = replace)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ interface ContextManager : MutableBeans, AutoCloseable {
* @return 进入的上下文
*/
fun enterRoot(context: Iterable<Any> = emptyList(), key: Any? = null): Context

override fun close()
}

@ContextsInternalApi
Expand All @@ -136,7 +138,7 @@ class ContextManagerImpl(
) : ContextManager, MutableBeans by beans {
private val mutableContexts: MutableList<Context> = CopyOnWriteArrayList()
override val contexts: List<Context> get() = mutableContexts
override val roots: List<Context> get() = mutableContexts.filter { it.parentCount <= 0 }
override val roots: List<Context> get() = mutableContexts.filter { it.allParentCount <= 0 }

private val mutableModuleEntries: MutableList<ModuleEntry> = CopyOnWriteArrayList()
override val modules: List<Module> get() = mutableModuleEntries.map { it.value }
Expand Down Expand Up @@ -428,7 +430,7 @@ class ContextManagerImpl(
onPreEvent { it.onContextPreRemove(event) }
} finally {
// event.child.parentCount == 1 的原因是此时还没有真正移除,所以 parentCount 还是 1。
if (event.exit && event.child.parentCount == 1 && event.alsoExitChildIfItWillBeRoot) {
if (event.child.allParentCount == 1 && event.exitChildIfItWillBeRoot) {
event.child.exit()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ interface ContextRemoveEvent : ContextEvent {

interface ContextPreRemoveEvent: ContextRemoveEvent {
/**
* 如果 [exit] 为 `true`,则在移除后也退出那些没有父上下文的子上下文
* 移除后也退出那些没有父上下文的子上下文
*/
var alsoExitChildIfItWillBeRoot: Boolean
var exitChildIfItWillBeRoot: Boolean
}
interface ContextPostRemoveEvent: ContextRemoveEvent

Expand All @@ -45,7 +45,7 @@ data class ContextPreRemoveEventImpl(
override val replace: ContextAddEvent?,
override val exit: Boolean,
override val contextManager: ContextManager,
override var alsoExitChildIfItWillBeRoot: Boolean = true
override var exitChildIfItWillBeRoot: Boolean = true
) : ContextPreRemoveEvent

@ContextsInternalApi
Expand Down
Loading

0 comments on commit 72d3d95

Please sign in to comment.