diff --git a/BTree.sln b/BTree.sln new file mode 100644 index 0000000..8d29775 --- /dev/null +++ b/BTree.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wjybxx.BTree.Core", "Wjybxx.BTree.Core\Wjybxx.BTree.Core.csproj", "{2ECEA7E5-B68A-4F50-9400-A02BB6A0EE27}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wjybxx.BTree.Tests", "Wjybxx.BTree.Tests\Wjybxx.BTree.Tests.csproj", "{4FE2D04C-11E0-4B95-B5ED-36BB45AF88F9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2ECEA7E5-B68A-4F50-9400-A02BB6A0EE27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2ECEA7E5-B68A-4F50-9400-A02BB6A0EE27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2ECEA7E5-B68A-4F50-9400-A02BB6A0EE27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2ECEA7E5-B68A-4F50-9400-A02BB6A0EE27}.Release|Any CPU.Build.0 = Release|Any CPU + {4FE2D04C-11E0-4B95-B5ED-36BB45AF88F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FE2D04C-11E0-4B95-B5ED-36BB45AF88F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FE2D04C-11E0-4B95-B5ED-36BB45AF88F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FE2D04C-11E0-4B95-B5ED-36BB45AF88F9}.Release|Any CPU.Build.0 = Release|Any CPU + {2FED286F-CEE1-4AEB-9A6F-3C044F4DB30B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2FED286F-CEE1-4AEB-9A6F-3C044F4DB30B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2FED286F-CEE1-4AEB-9A6F-3C044F4DB30B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2FED286F-CEE1-4AEB-9A6F-3C044F4DB30B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/HEADER b/HEADER new file mode 100644 index 0000000..3727acd --- /dev/null +++ b/HEADER @@ -0,0 +1,13 @@ +Copyright 2024 wjybxx(845740757@qq.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + 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. \ No newline at end of file diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..cfcc246 --- /dev/null +++ b/NOTICE @@ -0,0 +1,2 @@ +BTree +Copyright 2024 wjybxx(845740757@qq.com) \ No newline at end of file diff --git a/README.md b/README.md index a7ef1a9..d9ffec6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,196 @@ # FastestBtree -这是传统的心跳驱动行为树,但性能超越事件驱动行为树,这也将是市面上性能最好的行为树实现。 + +这是传统的心跳驱动行为树,但性能超越事件驱动行为树,这也将是市面上性能最高的行为树实现。 + +得益于偶然的灵感,使得心跳驱动的内联优化可以比事件驱动更优,因此其极限性能要要优于我commons仓库的事件驱动的行为树; +不过,虽然这版的行为树性能很极致,但由于其不支持事件驱动,因此适用场景有限,在AI领域适用是可以的,在其它领域就要慎重。 + +该仓库是为了验证算法的其可行性,大家如果想使用也可以使用;如果想要适用性更广的通用任务树,可从我的commons库下载事件驱动行为树的源码。 + +PS: +1. 想了解详细的原理,可研究`Task`类源码;也可关注公众号,或直接微信搜索《行为树:极限性能优化》。 +2. 该仓库没有任何外部依赖,可独立使用;测试用例也可以直接运行。 +3. 从事件驱动改为心跳驱动,共1个commit,大家可以看见整个过程的修改 -- 我尽量保持了最小变化。 +4. 限于精力,这里没有提供提供心跳驱动版的FSM节点 -- 在心跳驱动框架下不是很有意义。 + +# TaskTree/Btree(任务树/行为树) + +对于行为树,我有非常多想写的内容,但限于篇幅,这里只对核心部分进行说明。 +在开始以前,我要强调一下,我这里的提供的其实是通用的任务树,而不是局限于游戏AI的结构,你可以用它做你一切想要的逻辑,技能脚本、副本脚本、任务脚本。。。 + +仓库说明:该仓库和Dson仓库一样,都是从Bigcat仓库分离出来的,因为行为树的核心逻辑是与业务无关的,可以很通用。 + +## Task(任务) + +Task代表一个异步任务,不过与一般异步任务不同的是:Task不会自动运行,需要用户每帧调用`TaskEntry`的`update`方法来驱动任务的执行。 + +可简单表示为: + +``` java + public void mainLoop() { + int frame = 0; + while (true) { // 这个死循环指线程的死循环,在游戏开发中称为主循环 + frame++; + taskEntry.update(frame); + } + } +``` + +## Context(上下文) + +Task的上下文由三部分组成:黑板、取消令牌、共享属性。 +每个Task都可以有独立的上下文,当未指定Task的上下文时,会默认从父节点中捕获缺失的上下文。 + +```java + class Task { + Object blackboard; + CancelToken cancelToken; + Object sharedProps; + } +``` + +### Blackboard(黑板) + +有过行为树编程经验的,一定对黑板很熟悉。简单说:**黑板就是一块共享内存**,Task通过黑板共享信息,Task既从黑板中读取(部分) +输入,也将(部分)输出写入黑板。 + +在我们的实现中,每个Task都可以有独立的黑板,未显示绑定的情况下,默认从父节点继承;Task并未限定黑板的类型,黑板完全由用户自身实现。 + +#### 黑板实现的指导 + +1. 黑板通常需要实现父子关系,在当前黑板查询失败时自动从父黑板中查询 -- (local + share 或 local + parent)。 +2. 不要在黑板中提供过多的功能,尽量只保持简单的数据读写,就好像我们使用内存一样。 +3. 如果要实现数据更新广播,可参考volatile字段的设计,即只有特定标记的字段更新才派发事件 -- 避免垃圾事件风暴。 + +### CancelToken(取消令牌) + +由于Task是复杂的树形结构,而取消要作用于一组任务,而不是单个任务,因此这些任务必须共享一个上下文来实现监听; +我考虑过将取消信号放入黑板,但这可能导致糟糕的API,或限制黑板的扩展;而通过额外的字段来共享对象将大幅简化设计,而没有明显的缺陷。 + +#### 协作式取消 + +任何的异步任务,不论单线程还是多线程,立即取消通常都是不安全的,因为取消命令的发起者通常不清楚任务当前执行的代码位置; +安全的取消通常需要任务自身检测取消信号来实现,只有任务自身知道如何停止。 + +在默认情况下,Task只会在心跳模板方法中检测取消信号,而不会向CancelToken注册监听器;不过我提供了控制位允许用户启用自动监听, +通常而言,我们的大多数业务只在TaskEntry启用自动监听接口,因为通常是取消整个任务。 + +ps:jdk的Future的取消接口,在异步编程如火如荼的今天,已经不太适合了,我后面会写一套自己的Future。 + +### SharedProps(共享属性/配置) + +共享属性用于解决**数据和行为分离** +架构下的配置需求,主要解决策划的配置问题,减少维护工作量。我以技能配置为例,进行简单说明。 +在许多项目中,角色技能是有等级的,或是按公式计算,或是每一级单独配置,但不论选择那种方案,技能数值最好都抽离出来; +就好像脚本一样,技能的数值全部在脚本的开头定义,而逻辑则放在后面,这样策划在调整数值的时候就比较方便。 +当我们使用复杂的树形结构来做技能脚本时,就需要每个Task都需要能读取到这部分属性,共享属性就是为这样的目的服务的。 + +ps:你可以将共享属性理解为另一个黑板,只是我们只读不写。 + +## 心跳+事件驱动 + +行为树虽然提供了事件驱动支持,但**心跳驱动为主,事件驱动为辅**。 +我一直反对纯粹的事件驱动,有几个重要的理由: + +1. 子节点将自己加入某个调度队列,导致父节点对子节点丧失控制权,也丧失时序保证 -- Unity开发者尤为严重。 +2. 事件驱动会大幅增加代码的复杂度,**因为事件是跳跃性思维,而心跳是顺序思维**。 +3. 事件也是一种信号,错失信号可能导致程序陷入错误无法恢复 -- 你玩游戏碰见的很多bug都源于此。 +4. 不要老拿性能说问题,首先大多数的行为树不深;另外,**脱离你掌控的代码一定不是好代码**。 +5. 本库对行为树做了较多的性能优化,其核心优化为**内联**。 + +事件驱动分两部分:child的状态更新事件 和 外部事件。 +child状态更新事件,主要是子节点进入完成状态的事件,父节点通常在处理该事件中计算是否结束; +外部事件是指外部通过`onEvent`派发给TaskEntry的事件,叶子节点一般直接处理事件,非并行节点一般转发给运行中的子节点, +并行节点一般派发给主节点(第一个子节点);如果有特殊的需求,则需要用户自己扩展。 + +## 内联 + +行为树性能问题通常来源于自顶向下的update,这会有较多的无效路径。市面上也有一些解决方案,比如:ScheduleMgr,将所有运行中的子节点收集到调度管理器,由调度管理器进行调度。 +这个方法我很早就了解了,但我一直没有选择,因为它有两个问题: + +1. 它可能改变update的顺序,从而导致逻辑上的变化,不安全。 +2. 如果ScheduleMgr要保持和自顶向下的迭代顺序,那么启动和停止节点的开销就会变大,而行为树的节点变化频率是比较高的。 + +有一天工作的时候,我忽然想到:像Sequence/Selector这类简单的节点,**能不能让它的父节点跳过它,直接驱动它的子节点呢**? +这个Idea真的太重要了,在进行多次尝试和思考后,我发现多数的非并行节点(含装饰节点)都是可以被内联的,这使得我们的行为树的Update路径大幅缩短。 + +相比SchedulerMgr,内联实现的优点: + +1. 保持了自顶向下update的时序,保证了逻辑的一致性! +2. 实现简单,开销小。 + +下面给出一段内联的实现: + +```java + // Inverter类 + protected override int Execute() { + Task? inlinedChild = inlineHelper.GetInlinedChild(); + if (inlinedChild != null) { + inlinedChild.Template_ExecuteInlined(ref inlineHelper, child); + } else if (child.IsRunning) { + child.Template_Execute(true); + } else { + Template_StartChild(child, true, ref inlineHelper); + } + return child.IsCompleted ? TaskStatus.Invert(child.Status) : TaskStatus.RUNNING; + } +``` + +PS:关于行为树的更多优化,以及更详细的解释,会在公众号上分享。 + +## reentry(重入) + +重入的概念:重入是指Task上一次的执行还未完全退出,就又被父节点再次启动。 +以状态机为例(状态机中最常见),假设现在有一个状态A,在`execute`时检测到条件满足,请求状态机再次切换为自己; +由于上一次的执行尚未完全退出,因此现在**有"两个"状态A都在execute代码块**,我们称这种情况为重入。 + +重入的危险性:调用状态机的`changeState`会触发当前状态的`exit`方法,然后触发新状态的`enter`方法,对于前一个状态A的执行而言,task的上下文已彻底变更; +如果前一个状态A的执行没有立即return,就可能访问到错误的数据,从而造成错误 -- +*在以往的工作中便出现过忘记return导致NPE的情况*。 + +ps:想到一个经常遇见的问题,List在迭代的时候删除元素。 + +```java + public void enter() { + // 初始化一些数据 + } + + public void execute() { + if (cond()) { + stateMachine.changeState(this); // 自己切换自己的情况不常见(但存在),更多的情况是不知不觉中绕一圈。 + return; // 这里如果没有return是有风险的 + } + } + + public void exit() { + // 清理一些数据 + } +``` + +### 重入检测 + +在说解决方案前,我先说一点心得: +**代码是逻辑,是不确定的东西,你永远无法判断用户逻辑的正确性,代码应该怎样运行,应当由用户说了算。** +你不能因为担心出错,而禁止用户的合法需求;另外,你认为可能是错误的东西,可能是在用户的掌控中,而是正确的。 +因此,要么你什么也不做,要么就帮助用户检测错误。如果你的系统在某些情况下会出错,而其它情况下不会出错,那这个系统就不是一个可靠的系统。 + +**解决方案**: +我们在每个Task上记录一个`reentryId`,在Task的生命周期发生变更时+1;用户在执行不确定代码前,先将reentryId保存下来,执行不确定代码后, +通过检查重入id的相等性就可得知Task的生命周期是否发生了变更,以及是否已经被重入。 + +ActionTask的模板方法如下: + +```java + public void execute() { + int reentryId = getReentryId(); + int status = executeImpl(); + if (isExited(reentryId)) { // 当前任务已退出 + return; + } + // ... 更新状态 + } +``` + +## 个人公众号(游戏开发) + +![写代码的诗人](https://github.com/hl845740757/commons/blob/dev/docs/res/qrcode_for_wjybxx.jpg) \ No newline at end of file diff --git a/Wjybxx.BTree.Core/AssemblyInfo.cs b/Wjybxx.BTree.Core/AssemblyInfo.cs new file mode 100644 index 0000000..56fb7d8 --- /dev/null +++ b/Wjybxx.BTree.Core/AssemblyInfo.cs @@ -0,0 +1,21 @@ +#region LICENSE + +// Copyright 2024 wjybxx(845740757@qq.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to iBn 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. + +#endregion + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("BTree.Tests")] \ No newline at end of file diff --git a/Wjybxx.BTree.Core/Wjybxx.BTree.Core.csproj b/Wjybxx.BTree.Core/Wjybxx.BTree.Core.csproj new file mode 100644 index 0000000..f90aa03 --- /dev/null +++ b/Wjybxx.BTree.Core/Wjybxx.BTree.Core.csproj @@ -0,0 +1,44 @@ + + + + disable + annotations + 9 + Wjybxx.BTree + + Wjybxx.BTree.Core + Wjybxx.BTree.Core + 1.0.0 + + Wjybxx.BTree + BTree-非典型的行为树实现 + wjybxx;task;btree;game-ai; + + wjybxx(845740757@qq.com) + wjybxx(845740757@qq.com) + Copyright 2024 wjybxx(845740757@qq.com) + Apache-2.0 + https://github.com/hl845740757/BTree + https://github.com/hl845740757/BTree/releases + + true + zh-Hans + + README.md + true + true + net6.0; + + + + 1701;1702;1591; + + + + 1701;1702;1591; + + + + + + \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Branch/ActiveSelector.cs b/Wjybxx.BTree.Core/src/Branch/ActiveSelector.cs index 0530db9..bb0b5bc 100644 --- a/Wjybxx.BTree.Core/src/Branch/ActiveSelector.cs +++ b/Wjybxx.BTree.Core/src/Branch/ActiveSelector.cs @@ -35,7 +35,7 @@ public ActiveSelector() { public ActiveSelector(List>? children) : base(children) { } - protected override void Execute() { + protected override int Execute() { Task childToRun = null; int childIndex = -1; for (int idx = 0; idx < children.Count; idx++) { @@ -50,8 +50,7 @@ protected override void Execute() { if (childToRun == null) { Stop(this.runningChild); // 不清理index,允许退出后查询最后一次运行的child - SetFailed(TaskStatus.ERROR); - return; + return TaskStatus.ERROR; } Task runningChild = this.runningChild; @@ -62,7 +61,7 @@ protected override void Execute() { } else if (runningChild.IsRunning) { runningChild.Template_Execute(true); } else { - Template_StartChild(runningChild, false); + Template_StartChild(runningChild, false, ref inlineHelper); } } else { if (runningChild != null) { @@ -71,18 +70,13 @@ protected override void Execute() { } this.runningChild = childToRun; this.runningIndex = childIndex; - Template_StartChild(childToRun, false); + Template_StartChild(childToRun, false, ref inlineHelper); } + return childToRun.Status; } - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - runningChild = null; - inlineHelper.StopInline(); - SetCompleted(child.Status, true); + protected override int OnChildCompleted(Task child) { + throw new System.NotImplementedException(); } } } \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Branch/Foreach.cs b/Wjybxx.BTree.Core/src/Branch/Foreach.cs index 8ac16fb..6ababd7 100644 --- a/Wjybxx.BTree.Core/src/Branch/Foreach.cs +++ b/Wjybxx.BTree.Core/src/Branch/Foreach.cs @@ -33,27 +33,21 @@ public Foreach() { public Foreach(List>? children) : base(children) { } - protected override void Enter(int reentryId) { + protected override int Enter() { if (children.Count == 0) { - SetSuccess(); + return TaskStatus.SUCCESS; } + return TaskStatus.RUNNING; } - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - runningChild = null; - inlineHelper.StopInline(); + protected override int OnChildCompleted(Task child) { if (child.IsCancelled) { - SetCancelled(); - return; + return TaskStatus.CANCELLED; } if (IsAllChildCompleted) { - SetSuccess(); + return TaskStatus.SUCCESS; } else { - Template_Execute(false); + return TaskStatus.RUNNING; } } } diff --git a/Wjybxx.BTree.Core/src/Branch/Join.cs b/Wjybxx.BTree.Core/src/Branch/Join.cs index 47859d5..3af93c4 100644 --- a/Wjybxx.BTree.Core/src/Branch/Join.cs +++ b/Wjybxx.BTree.Core/src/Branch/Join.cs @@ -56,16 +56,16 @@ protected override void BeforeEnter() { policy.BeforeEnter(this); } - protected override void Enter(int reentryId) { + protected override int Enter() { // 记录子类上下文 -- 由于beforeEnter可能改变子节点信息,因此在enter时处理 InitChildHelpers(IsCancelTokenPerChild); - policy.Enter(this); + return policy.Enter(this); } - protected override void Execute() { + protected override int Execute() { List> children = this.children; if (children.Count == 0) { - return; + return TaskStatus.RUNNING; } int reentryId = ReentryId; for (int i = 0; i < children.Count; i++) { @@ -82,32 +82,28 @@ protected override void Execute() { child.Template_Execute(true); } else { SetChildCancelToken(child, childHelper.cancelToken); // 运行前赋值取消令牌 - Template_StartChild(child, true); + Template_StartChild(child, true, ref childHelper.Unwrap()); + } + if (child.IsCompleted) { + UnsetChildCancelToken(child); // 运行结束删除令牌 + // 尝试计算结果 + completedCount++; + if (child.IsSucceeded) { + succeededCount++; + } + int result = policy.OnChildCompleted(this, child); + if (result != TaskStatus.RUNNING) { + return result; + } } if (CheckCancel(reentryId)) { - return; + return TaskStatus.CANCELLED; } } if (completedCount >= children.Count) { // child全部执行,但没得出结果 throw new IllegalStateException(); } - } - - protected override void OnChildRunning(Task child) { - ParallelChildHelper childHelper = GetChildHelper(child); - childHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - ParallelChildHelper childHelper = GetChildHelper(child); - childHelper.StopInline(); - UnsetChildCancelToken(child); // 删除分配的token - - completedCount++; - if (child.IsSucceeded) { - succeededCount++; - } - policy.OnChildCompleted(this, child); + return TaskStatus.RUNNING; } protected override void OnEventImpl(object eventObj) { diff --git a/Wjybxx.BTree.Core/src/Branch/Join/JoinAnyOf.cs b/Wjybxx.BTree.Core/src/Branch/Join/JoinAnyOf.cs index cff346b..dcedd75 100644 --- a/Wjybxx.BTree.Core/src/Branch/Join/JoinAnyOf.cs +++ b/Wjybxx.BTree.Core/src/Branch/Join/JoinAnyOf.cs @@ -36,15 +36,16 @@ public void ResetForRestart() { public void BeforeEnter(Join join) { } - public void Enter(Join join) { + public int Enter(Join join) { // 不能成功,失败也不能 if (join.ChildCount == 0) { TaskLogger.Info("JonAnyOf: children is empty"); } + return TaskStatus.RUNNING; } - public void OnChildCompleted(Join join, Task child) { - join.SetCompleted(child.Status, true); + public int OnChildCompleted(Join join, Task child) { + return child.Status; } public void OnEvent(Join join, object eventObj) { diff --git a/Wjybxx.BTree.Core/src/Branch/Join/JoinMain.cs b/Wjybxx.BTree.Core/src/Branch/Join/JoinMain.cs index 51f641c..7c148c7 100644 --- a/Wjybxx.BTree.Core/src/Branch/Join/JoinMain.cs +++ b/Wjybxx.BTree.Core/src/Branch/Join/JoinMain.cs @@ -38,17 +38,19 @@ public void ResetForRestart() { public void BeforeEnter(Join join) { } - public void Enter(Join join) { + public int Enter(Join join) { if (join.ChildCount == 0) { - join.SetFailed(TaskStatus.CHILDLESS); + return TaskStatus.CHILDLESS; } + return TaskStatus.RUNNING; } - public void OnChildCompleted(Join join, Task child) { + public int OnChildCompleted(Join join, Task child) { Task mainTask = join.GetFirstChild(); if (child == mainTask) { - join.SetCompleted(child.Status, true); + return child.Status; } + return TaskStatus.RUNNING; } public void OnEvent(Join join, object eventObj) { diff --git a/Wjybxx.BTree.Core/src/Branch/Join/JoinSelector.cs b/Wjybxx.BTree.Core/src/Branch/Join/JoinSelector.cs index 83d93e0..8937ef6 100644 --- a/Wjybxx.BTree.Core/src/Branch/Join/JoinSelector.cs +++ b/Wjybxx.BTree.Core/src/Branch/Join/JoinSelector.cs @@ -35,18 +35,21 @@ public void ResetForRestart() { public void BeforeEnter(Join join) { } - public void Enter(Join join) { + public int Enter(Join join) { if (join.ChildCount == 0) { - join.SetFailed(TaskStatus.CHILDLESS); + return TaskStatus.CHILDLESS; } + return TaskStatus.RUNNING; } - public void OnChildCompleted(Join join, Task child) { + public int OnChildCompleted(Join join, Task child) { if (child.IsSucceeded) { - join.SetSuccess(); - } else if (join.IsAllChildCompleted) { - join.SetFailed(TaskStatus.ERROR); + return TaskStatus.SUCCESS; } + if (join.IsAllChildCompleted) { + return TaskStatus.ERROR; + } + return TaskStatus.RUNNING; } public void OnEvent(Join join, object eventObj) { diff --git a/Wjybxx.BTree.Core/src/Branch/Join/JoinSelectorN.cs b/Wjybxx.BTree.Core/src/Branch/Join/JoinSelectorN.cs index 0b1f2f1..b179033 100644 --- a/Wjybxx.BTree.Core/src/Branch/Join/JoinSelectorN.cs +++ b/Wjybxx.BTree.Core/src/Branch/Join/JoinSelectorN.cs @@ -48,22 +48,27 @@ public void BeforeEnter(Join join) { sequence = Math.Clamp(sequence, 0, required); } - public void Enter(Join join) { + public int Enter(Join join) { if (required <= 0) { - join.SetSuccess(); - } else if (join.ChildCount == 0) { - join.SetFailed(TaskStatus.CHILDLESS); - } else if (CheckFailFast(join)) { - join.SetFailed(TaskStatus.INSUFFICIENT_CHILD); + return TaskStatus.SUCCESS; } + if (join.ChildCount == 0) { + return TaskStatus.CHILDLESS; + } + if (CheckFailFast(join)) { + return TaskStatus.INSUFFICIENT_CHILD; + } + return TaskStatus.RUNNING; } - public void OnChildCompleted(Join join, Task child) { + public int OnChildCompleted(Join join, Task child) { if (join.SucceededCount >= required && CheckSequence(join)) { - join.SetSuccess(); - } else if (join.IsAllChildCompleted || CheckFailFast(join)) { - join.SetFailed(TaskStatus.ERROR); + return TaskStatus.SUCCESS; + } + if (join.IsAllChildCompleted || CheckFailFast(join)) { + return TaskStatus.ERROR; } + return TaskStatus.RUNNING; } private bool CheckSequence(Join join) { diff --git a/Wjybxx.BTree.Core/src/Branch/Join/JoinSequence.cs b/Wjybxx.BTree.Core/src/Branch/Join/JoinSequence.cs index 6de5e3a..1cf5f0d 100644 --- a/Wjybxx.BTree.Core/src/Branch/Join/JoinSequence.cs +++ b/Wjybxx.BTree.Core/src/Branch/Join/JoinSequence.cs @@ -35,18 +35,21 @@ public void ResetForRestart() { public void BeforeEnter(Join join) { } - public void Enter(Join join) { + public int Enter(Join join) { if (join.ChildCount == 0) { - join.SetSuccess(); + return TaskStatus.SUCCESS; } + return TaskStatus.RUNNING; } - public void OnChildCompleted(Join join, Task child) { + public int OnChildCompleted(Join join, Task child) { if (!child.IsSucceeded) { - join.SetCompleted(child.Status, true); - } else if (join.IsAllChildSucceeded) { - join.SetSuccess(); + return child.Status; } + if (join.IsAllChildSucceeded) { + return TaskStatus.SUCCESS; + } + return TaskStatus.RUNNING; } public void OnEvent(Join join, object eventObj) { diff --git a/Wjybxx.BTree.Core/src/Branch/Join/JoinWaitAll.cs b/Wjybxx.BTree.Core/src/Branch/Join/JoinWaitAll.cs index 06d0ee2..4871f0c 100644 --- a/Wjybxx.BTree.Core/src/Branch/Join/JoinWaitAll.cs +++ b/Wjybxx.BTree.Core/src/Branch/Join/JoinWaitAll.cs @@ -36,16 +36,18 @@ public void ResetForRestart() { public void BeforeEnter(Join join) { } - public void Enter(Join join) { + public int Enter(Join join) { if (join.ChildCount == 0) { - join.SetSuccess(); + return TaskStatus.SUCCESS; } + return TaskStatus.RUNNING; } - public void OnChildCompleted(Join join, Task child) { + public int OnChildCompleted(Join join, Task child) { if (join.IsAllChildCompleted) { - join.SetSuccess(); + return TaskStatus.SUCCESS; } + return TaskStatus.RUNNING; } public void OnEvent(Join join, object eventObj) { diff --git a/Wjybxx.BTree.Core/src/Branch/JoinPolicy.cs b/Wjybxx.BTree.Core/src/Branch/JoinPolicy.cs index 6719ea5..1204af1 100644 --- a/Wjybxx.BTree.Core/src/Branch/JoinPolicy.cs +++ b/Wjybxx.BTree.Core/src/Branch/JoinPolicy.cs @@ -30,11 +30,20 @@ public interface JoinPolicy where T : class /** 启动前初始化 */ void BeforeEnter(Join join); - /** 启动 */ - void Enter(Join join); + /// + /// 启动 + /// + /// + /// 最新状态 + int Enter(Join join); - /** Join在调用该方法前更新了完成计数和成功计数 */ - void OnChildCompleted(Join join, Task child); + /// + /// Join在调用该方法前更新了完成计数和成功计数 + /// + /// + /// + /// 最新状态 + int OnChildCompleted(Join join, Task child); /** join节点收到外部事件 */ void OnEvent(Join join, object eventObj); diff --git a/Wjybxx.BTree.Core/src/Branch/ParallelBranch.cs b/Wjybxx.BTree.Core/src/Branch/ParallelBranch.cs index 5c65570..4bf3768 100644 --- a/Wjybxx.BTree.Core/src/Branch/ParallelBranch.cs +++ b/Wjybxx.BTree.Core/src/Branch/ParallelBranch.cs @@ -90,13 +90,5 @@ protected void ResetHelpers() { } } } - - /// - /// 1.并发节点通常不需要在该事件中将自己更新为运行状态,而是应该在方法的末尾更新 - /// 2.实现类可以在该方法中内联子节点 - /// - /// - protected override void OnChildRunning(Task child) { - } } } \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Branch/ParallelChildHelper.cs b/Wjybxx.BTree.Core/src/Branch/ParallelChildHelper.cs index f7b1952..7042a4a 100644 --- a/Wjybxx.BTree.Core/src/Branch/ParallelChildHelper.cs +++ b/Wjybxx.BTree.Core/src/Branch/ParallelChildHelper.cs @@ -16,6 +16,8 @@ #endregion +using System.Runtime.CompilerServices; + namespace Wjybxx.BTree.Branch { /// @@ -45,14 +47,17 @@ public virtual void Reset() { public ref TaskInlineHelper Unwrap() => ref _inlineHelper; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Task GetInlinedChild() { return _inlineHelper.GetInlinedChild(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void StopInline() { _inlineHelper.StopInline(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void InlineChild(Task runningChild) { _inlineHelper.InlineChild(runningChild); } diff --git a/Wjybxx.BTree.Core/src/Branch/Selector.cs b/Wjybxx.BTree.Core/src/Branch/Selector.cs index d10d81a..306e4bb 100644 --- a/Wjybxx.BTree.Core/src/Branch/Selector.cs +++ b/Wjybxx.BTree.Core/src/Branch/Selector.cs @@ -36,39 +36,32 @@ public Selector(List>? children) : base(children) { public Selector(Task first, Task? second) : base(first, second) { } - protected override void Enter(int reentryId) { + protected override int Enter() { if (children.Count == 0) { - SetFailed(TaskStatus.CHILDLESS); + return TaskStatus.CHILDLESS; } else if (IsCheckingGuard()) { // 条件检测性能优化 for (int i = 0; i < children.Count; i++) { Task child = children[i]; if (Template_CheckGuard(child)) { - SetSuccess(); - return; + return TaskStatus.SUCCESS; } } - SetFailed(TaskStatus.ERROR); + return TaskStatus.ERROR; } + return TaskStatus.RUNNING; } - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - runningChild = null; - inlineHelper.StopInline(); + protected override int OnChildCompleted(Task child) { if (child.IsCancelled) { - SetCancelled(); - return; + return TaskStatus.CANCELLED; } if (child.IsSucceeded) { - SetSuccess(); + return TaskStatus.SUCCESS; } else if (IsAllChildCompleted) { - SetFailed(TaskStatus.ERROR); + return TaskStatus.ERROR; } else { - Template_Execute(false); + return TaskStatus.RUNNING; } } } diff --git a/Wjybxx.BTree.Core/src/Branch/SelectorN.cs b/Wjybxx.BTree.Core/src/Branch/SelectorN.cs index dada1b6..204cafa 100644 --- a/Wjybxx.BTree.Core/src/Branch/SelectorN.cs +++ b/Wjybxx.BTree.Core/src/Branch/SelectorN.cs @@ -49,43 +49,36 @@ protected override void BeforeEnter() { count = 0; } - protected override void Enter(int reentryId) { + protected override int Enter() { if (required < 1) { - SetSuccess(); + return TaskStatus.SUCCESS; } else if (ChildCount == 0) { - SetFailed(TaskStatus.CHILDLESS); + return TaskStatus.CHILDLESS; } else if (CheckFailFast()) { - SetFailed(TaskStatus.INSUFFICIENT_CHILD); + return TaskStatus.INSUFFICIENT_CHILD; } else if (IsCheckingGuard()) { // 条件检测性能优化 for (int i = 0; i < children.Count; i++) { Task child = children[i]; if (Template_CheckGuard(child) && ++count >= required) { - SetSuccess(); - return; + return TaskStatus.SUCCESS; } } - SetFailed(TaskStatus.ERROR); + return TaskStatus.ERROR; } + return TaskStatus.RUNNING; } - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - runningChild = null; - inlineHelper.StopInline(); + protected override int OnChildCompleted(Task child) { if (child.IsCancelled) { - SetCancelled(); - return; + return TaskStatus.CANCELLED; } if (child.IsSucceeded && ++count >= required) { - SetSuccess(); + return TaskStatus.SUCCESS; } else if (IsAllChildCompleted || CheckFailFast()) { - SetFailed(TaskStatus.ERROR); + return TaskStatus.ERROR; } else { - Template_Execute(false); + return TaskStatus.RUNNING; } } diff --git a/Wjybxx.BTree.Core/src/Branch/Sequence.cs b/Wjybxx.BTree.Core/src/Branch/Sequence.cs index a49c966..7fc6a86 100644 --- a/Wjybxx.BTree.Core/src/Branch/Sequence.cs +++ b/Wjybxx.BTree.Core/src/Branch/Sequence.cs @@ -36,39 +36,32 @@ public Sequence(List>? children) : base(children) { public Sequence(Task first, Task? second) : base(first, second) { } - protected override void Enter(int reentryId) { + protected override int Enter() { if (children.Count == 0) { - SetSuccess(); + return TaskStatus.SUCCESS; } else if (IsCheckingGuard()) { // 条件检测性能优化 for (int i = 0; i < children.Count; i++) { Task child = children[i]; if (!Template_CheckGuard(child)) { - SetCompleted(child.Status, true); - return; + return child.Status; } } - SetSuccess(); + return TaskStatus.SUCCESS; } + return TaskStatus.RUNNING; } - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - runningChild = null; - inlineHelper.StopInline(); + protected override int OnChildCompleted(Task child) { if (child.IsCancelled) { - SetCancelled(); - return; + return TaskStatus.CANCELLED; } if (child.IsFailed) { // 失败码有传递的价值 - SetCompleted(child.Status, true); + return child.Status; } else if (IsAllChildCompleted) { - SetSuccess(); + return TaskStatus.SUCCESS; } else { - Template_Execute(false); + return TaskStatus.RUNNING; } } } diff --git a/Wjybxx.BTree.Core/src/Branch/ServiceParallel.cs b/Wjybxx.BTree.Core/src/Branch/ServiceParallel.cs index 7b4c7b3..454e784 100644 --- a/Wjybxx.BTree.Core/src/Branch/ServiceParallel.cs +++ b/Wjybxx.BTree.Core/src/Branch/ServiceParallel.cs @@ -35,11 +35,12 @@ public ServiceParallel() { public ServiceParallel(List>? children) : base(children) { } - protected override void Enter(int reentryId) { + protected override int Enter() { InitChildHelpers(false); + return TaskStatus.RUNNING; } - protected override void Execute() { + protected override int Execute() { List> children = this.children; for (int idx = 0; idx < children.Count; idx++) { Task child = children[idx]; @@ -50,19 +51,10 @@ protected override void Execute() { } else if (child.IsRunning) { child.Template_Execute(true); } else { - Template_StartChild(child, true); + Template_StartChild(child, true, ref childHelper.Unwrap()); } } - } - - protected override void OnChildRunning(Task child) { - ParallelChildHelper childHelper = GetChildHelper(child); - childHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - ParallelChildHelper childHelper = GetChildHelper(child); - childHelper.StopInline(); + return TaskStatus.RUNNING; } protected override void OnEventImpl(object eventObj) { diff --git a/Wjybxx.BTree.Core/src/Branch/SimpleParallel.cs b/Wjybxx.BTree.Core/src/Branch/SimpleParallel.cs index a690a38..83a7ddd 100644 --- a/Wjybxx.BTree.Core/src/Branch/SimpleParallel.cs +++ b/Wjybxx.BTree.Core/src/Branch/SimpleParallel.cs @@ -35,11 +35,12 @@ public SimpleParallel() { public SimpleParallel(List>? children) : base(children) { } - protected override void Enter(int reentryId) { + protected override int Enter() { InitChildHelpers(false); + return TaskStatus.RUNNING; } - protected override void Execute() { + protected override int Execute() { List> children = this.children; int reentryId = ReentryId; for (int idx = 0; idx < children.Count; idx++) { @@ -52,28 +53,19 @@ protected override void Execute() { child.Template_Execute(true); } else { SetChildCancelToken(child, childHelper.cancelToken); // 运行前赋值取消令牌 - Template_StartChild(child, true); + Template_StartChild(child, true, ref childHelper.Unwrap()); + } + if (child.IsCompleted) { + UnsetChildCancelToken(child); // 运行结束删除令牌 + if (idx == 0) { + return child.Status; + } } if (CheckCancel(reentryId)) { // 得出结果或取消 - return; + return TaskStatus.CANCELLED; } } - } - - protected override void OnChildRunning(Task child) { - ParallelChildHelper childHelper = GetChildHelper(child); - childHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - ParallelChildHelper childHelper = GetChildHelper(child); - childHelper.StopInline(); - UnsetChildCancelToken(child); - - Task mainTask = children[0]; - if (child == mainTask) { - SetCompleted(child.Status, true); - } + return TaskStatus.RUNNING; } protected override void OnEventImpl(object eventObj) { diff --git a/Wjybxx.BTree.Core/src/Branch/SingleRunningChildBranch.cs b/Wjybxx.BTree.Core/src/Branch/SingleRunningChildBranch.cs index b72c154..46e0162 100644 --- a/Wjybxx.BTree.Core/src/Branch/SingleRunningChildBranch.cs +++ b/Wjybxx.BTree.Core/src/Branch/SingleRunningChildBranch.cs @@ -36,8 +36,7 @@ public abstract class SingleRunningChildBranch : BranchTask where T : clas /// /// 被内联运行的子节点 - /// 1.该字段定义在这里是为了减少抽象层次,该类并不提供功能。 - /// 2.子类要支持实现内联优化时,应当在维护字段引用。 + /// 该字段定义在这里是为了减少抽象层次,该类并不提供功能,需要子类在Start子节点的时候启用内联。 /// [NonSerialized] protected TaskInlineHelper inlineHelper = new TaskInlineHelper(); @@ -121,19 +120,29 @@ protected override void OnEventImpl(object eventObj) { } } - protected override void Execute() { - Task? runningChild = this.runningChild; - if (runningChild == null) { - this.runningChild = runningChild = NextChild(); - Template_StartChild(runningChild, true); - } else { - Task? inlinedChild = inlineHelper.GetInlinedChild(); - if (inlinedChild != null) { - inlinedChild.Template_ExecuteInlined(ref inlineHelper, runningChild); - } else if (runningChild.IsRunning) { - runningChild.Template_Execute(true); + protected override int Execute() { + while (true) { + Task? runningChild = this.runningChild; // onCompleted时会被清理 + if (runningChild == null) { + this.runningChild = runningChild = NextChild(); + Template_StartChild(runningChild, true, ref inlineHelper); } else { - Template_StartChild(runningChild, true); // 可能继续运行前一个节点 + Task? inlinedChild = inlineHelper.GetInlinedChild(); + if (inlinedChild != null) { + inlinedChild.Template_ExecuteInlined(ref inlineHelper, runningChild); + } else if (runningChild.IsRunning) { + runningChild.Template_Execute(true); + } else { + Template_StartChild(runningChild, true, ref inlineHelper); // 可能继续运行前一个节点 + } + } + if (runningChild.IsRunning) { + return TaskStatus.RUNNING; + } + this.runningChild = null; + int result = OnChildCompleted(runningChild); + if (result != TaskStatus.RUNNING) { + return result; } } } @@ -153,32 +162,11 @@ protected string IllegalStateMsg() { return $"numChildren: {children.Count}, currentIndex: {runningIndex}"; } - /** 子类如果支持内联,则重写该方法 */ - protected override void OnChildRunning(Task child) { - runningChild = child; // 子类可能未赋值 - } - /// - /// 子类的实现模板: - /// - /// protected void OnChildCompleted(Task child) { - /// runningChild = null; - /// inlineHelper.StopInline(); - /// // 尝试计算结果(记得处理取消) - /// ... - /// // 如果未得出结果 - /// if (!IsExecuting()) { - /// Template_Execute(); - /// } - /// } - /// - /// ps: 推荐子类重复编码避免调用base + /// 尝试计算结果 /// /// - protected override void OnChildCompleted(Task child) { - runningChild = null; - inlineHelper.StopInline(); - } + protected abstract int OnChildCompleted(Task child); #endregion } diff --git a/Wjybxx.BTree.Core/src/Branch/Switch.cs b/Wjybxx.BTree.Core/src/Branch/Switch.cs index b434403..bbc362f 100644 --- a/Wjybxx.BTree.Core/src/Branch/Switch.cs +++ b/Wjybxx.BTree.Core/src/Branch/Switch.cs @@ -40,27 +40,31 @@ public Switch() { public Switch(List>? children) : base(children) { } - protected override void Execute() { + protected override int Enter() { if (runningChild == null) { int index = SelectChild(); if (index < 0) { runningIndex = -1; runningChild = null; - SetFailed(TaskStatus.ERROR); - return; + return TaskStatus.ERROR; } runningIndex = index; runningChild = children[index]; } + return TaskStatus.RUNNING; + } + protected override int Execute() { + Task runningChild = this.runningChild; // 完成时会被清理 Task inlinedChild = inlineHelper.GetInlinedChild(); if (inlinedChild != null) { inlinedChild.Template_ExecuteInlined(ref inlineHelper, runningChild); } else if (runningChild.IsRunning) { runningChild.Template_Execute(true); } else { - Template_StartChild(runningChild, false); + Template_StartChild(runningChild, false, ref inlineHelper); } + return runningChild.Status; } private int SelectChild() { @@ -69,21 +73,16 @@ private int SelectChild() { } for (int idx = 0; idx < children.Count; idx++) { Task child = children[idx]; - if (Template_CheckGuard(child.Guard)) { - return idx; + if (!Template_CheckGuard(child.Guard)) { + continue; } + return idx; } return -1; } - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - runningChild = null; - inlineHelper.StopInline(); - SetCompleted(child.Status, true); + protected override int OnChildCompleted(Task child) { + throw new System.NotImplementedException(); } public ISwitchHandler? Handler { diff --git a/Wjybxx.BTree.Core/src/Commons/AssertionError.cs b/Wjybxx.BTree.Core/src/Commons/AssertionError.cs new file mode 100644 index 0000000..1e4c255 --- /dev/null +++ b/Wjybxx.BTree.Core/src/Commons/AssertionError.cs @@ -0,0 +1,37 @@ +#region LICENSE + +// Copyright 2023-2024 wjybxx(845740757@qq.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// 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. + +#endregion + +using System; + +namespace Wjybxx.Commons +{ +/// +/// 该异常表示断言错误,不应该发送的错误发生了。 +/// +public class AssertionError : Exception +{ + public AssertionError() { + } + + public AssertionError(string? message) : base(message) { + } + + public AssertionError(string? message, Exception? innerException) : base(message, innerException) { + } +} +} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Commons/CancelCodes.cs b/Wjybxx.BTree.Core/src/Commons/CancelCodes.cs new file mode 100644 index 0000000..d7ae5db --- /dev/null +++ b/Wjybxx.BTree.Core/src/Commons/CancelCodes.cs @@ -0,0 +1,150 @@ +#region LICENSE + +// Copyright 2023-2024 wjybxx(845740757@qq.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// 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. + +#endregion + +using System; +using System.Runtime.CompilerServices; + +namespace Wjybxx.Commons.Concurrent +{ +/// +/// 取消码辅助类 +/// +public static class CancelCodes +{ + /** + * 原因的掩码 + * 1.如果cancelCode不包含其它信息,就等于reason + * 2.设定为20位,可达到100W + */ + public const int MASK_REASON = 0xFFFFF; + /** 紧迫程度的掩码(4it)-- 0表示未指定 */ + public const int MASK_DEGREE = 0x00F0_0000; + /** 预留4bit */ + public const int MASK_REVERSED = 0x0F00_0000; + /** 中断的掩码 (1bit) */ + public const int MASK_INTERRUPT = 1 << 28; + /** 告知任务无需执行删除逻辑 -- 慎用 */ + public const int MASK_WITHOUT_REMOVE = 1 << 29; + /** 表示取消信号来自Future的取消接口 -- c#端无用 */ + public const int MASK_FROM_FUTURE = 1 << 30; + + /** 最大取消原因 */ + public const int MAX_REASON = MASK_REASON; + /** 最大紧急程度 */ + public const int MAX_DEGREE = 15; + + /** 取消原因的偏移量 */ + public const int OFFSET_REASON = 0; + /** 紧急度的偏移量 */ + public const int OFFSET_DEGREE = 20; + + /** 默认原因 */ + public const int REASON_DEFAULT = 1; + /** 执行超时 -- {@link ICancelTokenSource#cancelAfter(int, long, TimeUnit)}就可使用 */ + public const int REASON_TIMEOUT = 2; + /** IExecutor关闭 -- IExecutor关闭不一定会取消任务 */ + public const int REASON_SHUTDOWN = 3; + /** 执行超时,触发次数限制 */ + public const int REASON_TRIGGER_COUNT_LIMIT = 4; + + #region query + + /** 计算取消码中的原因 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetReason(int code) { + return code & MASK_REASON; + } + + /** 计算取消码终归的紧急程度 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetDegree(int code) { + return (code & MASK_DEGREE) >> OFFSET_DEGREE; + } + + /** 取消指令中是否要求了中断线程 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInterruptible(int code) { + return (code & MASK_INTERRUPT) != 0; + } + + /** 取消指令中是否要求了无需删除 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsWithoutRemove(int code) { + return (code & MASK_WITHOUT_REMOVE) != 0; + } + + /** 取消信号是否来自future接口 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFromFuture(int code) { + return (code & MASK_FROM_FUTURE) != 0; + } + + #endregion + + #region util + + /** 设置紧急程度 */ + public static int SetDegree(int code, int value) { + if (value < 0 || value > MAX_DEGREE) { + throw new ArgumentException("degree"); + } + code &= (~MASK_DEGREE); + code |= (value << OFFSET_DEGREE); + return code; + } + + /** 设置取消原因 */ + public static int SetReason(int code, int value) { + if (value <= 0 || value > MAX_REASON) { + throw new ArgumentException("reason"); + } + code &= (~MASK_REASON); + code |= value; + return code; + } + + /** 设置中断标记 */ + public static int SetInterruptible(int code, bool value) { + return value + ? code | MASK_INTERRUPT + : code & (~MASK_INTERRUPT); + } + + /** 设置是否不立即删除 */ + public static int SetWithoutRemove(int code, bool value) { + return value + ? code | MASK_WITHOUT_REMOVE + : code & (~MASK_WITHOUT_REMOVE); + } + + #endregion + + /** + * 检查取消码的合法性 + * + * @return argument + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CheckCode(int code) { + if (GetReason(code) == 0) { + throw new ArgumentException("reason is absent"); + } + return code; + } +} +} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Commons/CollectionUtil.cs b/Wjybxx.BTree.Core/src/Commons/CollectionUtil.cs new file mode 100644 index 0000000..36689b0 --- /dev/null +++ b/Wjybxx.BTree.Core/src/Commons/CollectionUtil.cs @@ -0,0 +1,145 @@ +#region LICENSE + +// // Copyright 2024 wjybxx(845740757@qq.com) +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // 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. + +#endregion + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Wjybxx.Commons.Collections +{ +internal static class CollectionUtil +{ + /// + /// 全局共享random + /// + public static readonly Random SharedRandom = new Random(); + +#nullable disable + + #region indexref + + /** 查询List中是否包含指定对象引用 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ContainsRef(this IList list, T element) where T : class { + return IndexOfRef(list, element, 0, list.Count) >= 0; + } + + /** 查对象引用在数组中的下标 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfRef(this IList list, object element) where T : class { + return IndexOfRef(list, element, 0, list.Count); + } + + /** 反向查对象引用在数组中的下标 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOfRef(this IList list, object element) where T : class { + return LastIndexOfRef(list, element, 0, list.Count); + } + + /// + /// 查对象引用在数组中的下标 + /// + /// 数组 + /// 要查找的元素 + /// 开始下标,包含 + /// 结束下标,不包含 + /// + public static int IndexOfRef(this IList list, object element, int start, int end) where T : class { + if (list == null) throw new ArgumentNullException(nameof(list)); + if (element == null) { + for (int i = start; i < end; i++) { + if (list[i] == null) { + return i; + } + } + } else { + for (int i = start; i < end; i++) { + if (element == list[i]) { + return i; + } + } + } + return -1; + } + + /// + /// 反向查对象引用在数组中的下标 + /// + /// 数组 + /// 要查找的元素 + /// 开始下标,包含 + /// 结束下标,不包含 + /// + public static int LastIndexOfRef(this IList list, object element, int start, int end) where T : class { + if (element == null) { + for (int i = end - 1; i >= start; i--) { + if (list[i] == null) { + return i; + } + } + } else { + for (int i = end - 1; i >= start; i--) { + if (element == list[i]) { + return i; + } + } + } + return -1; + } + + /** 从List中删除指定引用 */ + public static bool RemoveRef(this IList list, object element) where T : class { + int index = IndexOfRef(list, element); + if (index < 0) { + return false; + } + list.RemoveAt(index); + return true; + } + + #endregion + + /// + /// 交换两个位置的元素 + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Swap(this IList list, int i, int j) { + T a = list[i]; + T b = list[j]; + list[i] = b; + list[j] = a; + } + +#nullable enable + /// + /// 洗牌算法 + /// 1.尽量只用于数组列表 + /// 2.DotNet8开始自带洗牌算法 + /// + /// 要打乱的列表 + /// 随机种子 + /// + public static void Shuffle(IList list, Random? rnd = null) { + rnd ??= SharedRandom; + int size = list.Count; + for (int i = size; i > 1; i--) { + Swap(list, i - 1, rnd.Next(i)); + } + } +} +} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Commons/IllegalStateException.cs b/Wjybxx.BTree.Core/src/Commons/IllegalStateException.cs new file mode 100644 index 0000000..d86760b --- /dev/null +++ b/Wjybxx.BTree.Core/src/Commons/IllegalStateException.cs @@ -0,0 +1,41 @@ +#region LICENSE + +// // Copyright 2024 wjybxx(845740757@qq.com) +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // 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. + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Wjybxx.Commons +{ +/// +/// 该异常表示对象的状态错误 +/// +public class IllegalStateException : InvalidOperationException +{ + public IllegalStateException() { + } + + protected IllegalStateException(SerializationInfo info, StreamingContext context) : base(info, context) { + } + + public IllegalStateException(string? message) : base(message) { + } + + public IllegalStateException(string? message, Exception? innerException) : base(message, innerException) { + } +} +} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Commons/InfiniteLoopException.cs b/Wjybxx.BTree.Core/src/Commons/InfiniteLoopException.cs new file mode 100644 index 0000000..1861397 --- /dev/null +++ b/Wjybxx.BTree.Core/src/Commons/InfiniteLoopException.cs @@ -0,0 +1,41 @@ +#region LICENSE + +// // Copyright 2024 wjybxx(845740757@qq.com) +// // +// // Licensed under the Apache License, Version 2.0 (the "License"); +// // you may not use this file except in compliance with the License. +// // You may obtain a copy of the License at +// // +// // 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. + +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Wjybxx.Commons.Ex +{ +/// +/// 死循环预防 +/// +public class InfiniteLoopException : Exception +{ + public InfiniteLoopException() { + } + + public InfiniteLoopException(string? message) : base(message) { + } + + public InfiniteLoopException(string? message, Exception? innerException) : base(message, innerException) { + } + + protected InfiniteLoopException(SerializationInfo info, StreamingContext context) : base(info, context) { + } +} +} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Decorator.cs b/Wjybxx.BTree.Core/src/Decorator.cs index 8686ec7..037d75e 100644 --- a/Wjybxx.BTree.Core/src/Decorator.cs +++ b/Wjybxx.BTree.Core/src/Decorator.cs @@ -32,8 +32,7 @@ public abstract class Decorator : Task where T : class /// /// 被内联运行的子节点 - /// 1.该字段定义在这里是为了减少抽象层次,该类并不提供功能。 - /// 2.子类要支持实现内联优化时,应当在维护字段引用。 + /// 该字段定义在这里是为了减少抽象层次,该类并不提供功能,需要子类在Start子节点的时候启用内联。 /// [NonSerialized] protected TaskInlineHelper inlineHelper = new TaskInlineHelper(); @@ -74,10 +73,6 @@ protected override void OnEventImpl(object eventObj) { } } - /** 子类如果支持内联,则重写该方法(超类不能安全内联) */ - protected override void OnChildRunning(Task child) { - } - #endregion #nullable disable diff --git a/Wjybxx.BTree.Core/src/Decorator/AlwaysCheckGuard.cs b/Wjybxx.BTree.Core/src/Decorator/AlwaysCheckGuard.cs index 471fa09..3c8abe7 100644 --- a/Wjybxx.BTree.Core/src/Decorator/AlwaysCheckGuard.cs +++ b/Wjybxx.BTree.Core/src/Decorator/AlwaysCheckGuard.cs @@ -31,7 +31,7 @@ public AlwaysCheckGuard() { public AlwaysCheckGuard(Task child) : base(child) { } - protected override void Execute() { + protected override int Execute() { if (Template_CheckGuard(child.Guard)) { Task? inlinedChild = inlineHelper.GetInlinedChild(); if (inlinedChild != null) { @@ -39,22 +39,14 @@ protected override void Execute() { } else if (child.IsRunning) { child.Template_Execute(true); } else { - Template_StartChild(child, false); + Template_StartChild(child, false, ref inlineHelper); } + return child.Status; } else { child.Stop(); inlineHelper.StopInline(); // help gc - SetFailed(TaskStatus.ERROR); + return TaskStatus.ERROR; } } - - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); - SetCompleted(child.Status, true); - } } } \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Decorator/AlwaysFail.cs b/Wjybxx.BTree.Core/src/Decorator/AlwaysFail.cs index ce56acd..c60c609 100644 --- a/Wjybxx.BTree.Core/src/Decorator/AlwaysFail.cs +++ b/Wjybxx.BTree.Core/src/Decorator/AlwaysFail.cs @@ -33,10 +33,9 @@ public AlwaysFail() { public AlwaysFail(Task child) : base(child) { } - protected override void Execute() { + protected override int Execute() { if (child == null) { - SetFailed(TaskStatus.ToFailure(failureStatus)); - return; + return TaskStatus.ToFailure(failureStatus); } Task? inlinedChild = inlineHelper.GetInlinedChild(); if (inlinedChild != null) { @@ -44,17 +43,9 @@ protected override void Execute() { } else if (child.IsRunning) { child.Template_Execute(true); } else { - Template_StartChild(child, true); + Template_StartChild(child, true, ref inlineHelper); } - } - - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); - SetCompleted(TaskStatus.ToFailure(child.Status), true); // 错误码有传播的价值 + return child.IsCompleted ? TaskStatus.ToFailure(child.Status) : TaskStatus.RUNNING; } /// diff --git a/Wjybxx.BTree.Core/src/Decorator/AlwaysRunning.cs b/Wjybxx.BTree.Core/src/Decorator/AlwaysRunning.cs index edc590d..433c451 100644 --- a/Wjybxx.BTree.Core/src/Decorator/AlwaysRunning.cs +++ b/Wjybxx.BTree.Core/src/Decorator/AlwaysRunning.cs @@ -41,13 +41,13 @@ protected override void BeforeEnter() { started = false; } - protected override void Execute() { + protected override int Execute() { Task child = this.child; if (child == null) { - return; + return TaskStatus.RUNNING; } if (started && child.IsCompleted) { // 勿轻易调整 - return; + return TaskStatus.RUNNING; } Task? inlinedChild = inlineHelper.GetInlinedChild(); if (inlinedChild != null) { @@ -56,20 +56,10 @@ protected override void Execute() { child.Template_Execute(true); } else { started = true; - Template_StartChild(child, true); - } - } - - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); - // 不响应其它状态,但还是需要响应取消... - if (child.IsCancelled) { - SetCancelled(); + Template_StartChild(child, true, ref inlineHelper); } + // 需要响应取消 + return child.IsCancelled ? TaskStatus.CANCELLED : TaskStatus.RUNNING; } } } \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Decorator/AlwaysSuccess.cs b/Wjybxx.BTree.Core/src/Decorator/AlwaysSuccess.cs index 05bfe30..dab793b 100644 --- a/Wjybxx.BTree.Core/src/Decorator/AlwaysSuccess.cs +++ b/Wjybxx.BTree.Core/src/Decorator/AlwaysSuccess.cs @@ -31,10 +31,9 @@ public AlwaysSuccess() { public AlwaysSuccess(Task child) : base(child) { } - protected override void Execute() { + protected override int Execute() { if (child == null) { - SetSuccess(); - return; + return TaskStatus.SUCCESS; } Task? inlinedChild = inlineHelper.GetInlinedChild(); if (inlinedChild != null) { @@ -42,17 +41,9 @@ protected override void Execute() { } else if (child.IsRunning) { child.Template_Execute(true); } else { - Template_StartChild(child, true); + Template_StartChild(child, true, ref inlineHelper); } - } - - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); - SetSuccess(); + return child.IsCompleted ? TaskStatus.SUCCESS : TaskStatus.RUNNING; } } } \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Decorator/Inverter.cs b/Wjybxx.BTree.Core/src/Decorator/Inverter.cs index 634786f..2939c23 100644 --- a/Wjybxx.BTree.Core/src/Decorator/Inverter.cs +++ b/Wjybxx.BTree.Core/src/Decorator/Inverter.cs @@ -36,31 +36,24 @@ public Inverter() { public Inverter(Task child) : base(child) { } - protected override void Enter(int reentryId) { + protected override int Enter() { if (IsCheckingGuard()) { Template_CheckGuard(child); - SetCompleted(TaskStatus.Invert(child.Status), true); + return TaskStatus.Invert(child.Status); } + return TaskStatus.RUNNING; } - protected override void Execute() { + protected override int Execute() { Task? inlinedChild = inlineHelper.GetInlinedChild(); if (inlinedChild != null) { inlinedChild.Template_ExecuteInlined(ref inlineHelper, child); } else if (child.IsRunning) { child.Template_Execute(true); } else { - Template_StartChild(child, true); + Template_StartChild(child, true, ref inlineHelper); } - } - - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); - SetCompleted(TaskStatus.Invert(child.Status), true); + return child.IsCompleted ? TaskStatus.Invert(child.Status) : TaskStatus.RUNNING; } } } \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Decorator/LoopDecorator.cs b/Wjybxx.BTree.Core/src/Decorator/LoopDecorator.cs index dd08a36..e5bd5d4 100644 --- a/Wjybxx.BTree.Core/src/Decorator/LoopDecorator.cs +++ b/Wjybxx.BTree.Core/src/Decorator/LoopDecorator.cs @@ -43,18 +43,34 @@ protected override void BeforeEnter() { curLoop = 0; } - protected override void Execute() { - Task? inlinedChild = inlineHelper.GetInlinedChild(); - if (inlinedChild != null) { - inlinedChild.Template_ExecuteInlined(ref inlineHelper, child); - } else if (child.IsRunning) { - child.Template_Execute(true); - } else { - curLoop++; - Template_StartChild(child, true); + protected override int Execute() { + while (true) { + Task? inlinedChild = inlineHelper.GetInlinedChild(); + if (inlinedChild != null) { + inlinedChild.Template_ExecuteInlined(ref inlineHelper, child); + } else if (child.IsRunning) { + child.Template_Execute(true); + } else { + curLoop++; + Template_StartChild(child, true, ref inlineHelper); + } + + if (child.IsRunning) { + return TaskStatus.RUNNING; + } + int result = OnChildCompleted(child); + if (result != TaskStatus.RUNNING) { + return result; + } } } + /// + /// 尝试计算结果 + /// + /// + protected abstract int OnChildCompleted(Task child); + /** 是否还有下一次循环 */ public bool HasNextLoop() { return maxLoop <= 0 || curLoop < maxLoop; diff --git a/Wjybxx.BTree.Core/src/Decorator/OnlyOnce.cs b/Wjybxx.BTree.Core/src/Decorator/OnlyOnce.cs index 81e3f79..e3e3b12 100644 --- a/Wjybxx.BTree.Core/src/Decorator/OnlyOnce.cs +++ b/Wjybxx.BTree.Core/src/Decorator/OnlyOnce.cs @@ -33,10 +33,9 @@ public OnlyOnce() { public OnlyOnce(Task child) : base(child) { } - protected override void Execute() { + protected override int Execute() { if (child.IsCompleted) { - SetCompleted(child.Status, true); - return; + return child.Status; } Task? inlinedChild = inlineHelper.GetInlinedChild(); if (inlinedChild != null) { @@ -44,17 +43,9 @@ protected override void Execute() { } else if (child.IsRunning) { child.Template_Execute(true); } else { - Template_StartChild(child, true); + Template_StartChild(child, true, ref inlineHelper); } - } - - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); - SetCompleted(child.Status, true); + return child.Status; } } } \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Decorator/Repeat.cs b/Wjybxx.BTree.Core/src/Decorator/Repeat.cs index 0d6cd2a..6fe57d4 100644 --- a/Wjybxx.BTree.Core/src/Decorator/Repeat.cs +++ b/Wjybxx.BTree.Core/src/Decorator/Repeat.cs @@ -63,22 +63,17 @@ protected override void BeforeEnter() { count = 0; } - protected override void Enter(int reentryId) { - base.Enter(reentryId); + protected override int Enter() { + base.Enter(); if (required == 0) { - SetSuccess(); + return TaskStatus.SUCCESS; } + return TaskStatus.RUNNING; } - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); + protected override int OnChildCompleted(Task child) { if (child.IsCancelled) { - SetCancelled(); - return; + return TaskStatus.CANCELLED; } bool match = countMode switch { @@ -90,15 +85,13 @@ protected override void OnChildCompleted(Task child) { if (match) { count++; if (required >= 0 && count >= required) { - SetSuccess(); - return; + return TaskStatus.SUCCESS; } } - if (!HasNextLoop()) { - SetFailed(TaskStatus.MAX_LOOP_LIMIT); + return TaskStatus.MAX_LOOP_LIMIT; } else { - Template_Execute(false); + return TaskStatus.RUNNING; } } diff --git a/Wjybxx.BTree.Core/src/Decorator/SubtreeRef.cs b/Wjybxx.BTree.Core/src/Decorator/SubtreeRef.cs index 7cd74a3..f94328e 100644 --- a/Wjybxx.BTree.Core/src/Decorator/SubtreeRef.cs +++ b/Wjybxx.BTree.Core/src/Decorator/SubtreeRef.cs @@ -44,24 +44,16 @@ protected override void BeforeEnter() { } } - protected override void Execute() { + protected override int Execute() { Task? inlinedChild = inlineHelper.GetInlinedChild(); if (inlinedChild != null) { inlinedChild.Template_ExecuteInlined(ref inlineHelper, child); } else if (child.IsRunning) { child.Template_Execute(true); } else { - Template_StartChild(child, true); + Template_StartChild(child, true, ref inlineHelper); } - } - - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); - SetCompleted(child.Status, true); + return child.Status; } /// diff --git a/Wjybxx.BTree.Core/src/Decorator/UntilCond.cs b/Wjybxx.BTree.Core/src/Decorator/UntilCond.cs index 7c96c96..b3a051d 100644 --- a/Wjybxx.BTree.Core/src/Decorator/UntilCond.cs +++ b/Wjybxx.BTree.Core/src/Decorator/UntilCond.cs @@ -21,7 +21,6 @@ namespace Wjybxx.BTree.Decorator /// /// 循环子节点直到给定的条件达成 /// -/// [TaskInlinable] public class UntilCond : LoopDecorator where T : class { @@ -35,22 +34,16 @@ public override void ResetForRestart() { } } - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); + protected override int OnChildCompleted(Task child) { if (child.IsCancelled) { - SetCancelled(); - return; + return TaskStatus.CANCELLED; } if (Template_CheckGuard(cond)) { - SetSuccess(); + return TaskStatus.SUCCESS; } else if (!HasNextLoop()) { - SetFailed(TaskStatus.MAX_LOOP_LIMIT); + return (TaskStatus.MAX_LOOP_LIMIT); } else { - Template_Execute(false); + return TaskStatus.RUNNING; } } diff --git a/Wjybxx.BTree.Core/src/Decorator/UntilFail.cs b/Wjybxx.BTree.Core/src/Decorator/UntilFail.cs index 74e5596..81ce540 100644 --- a/Wjybxx.BTree.Core/src/Decorator/UntilFail.cs +++ b/Wjybxx.BTree.Core/src/Decorator/UntilFail.cs @@ -21,7 +21,6 @@ namespace Wjybxx.BTree.Decorator /// /// 重复运行子节点,直到该任务失败 /// -/// [TaskInlinable] public class UntilFail : LoopDecorator where T : class { @@ -31,22 +30,16 @@ public UntilFail() { public UntilFail(Task child) : base(child) { } - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); + protected override int OnChildCompleted(Task child) { if (child.IsCancelled) { - SetCancelled(); - return; + return TaskStatus.CANCELLED; } if (child.IsFailed) { - SetSuccess(); + return TaskStatus.SUCCESS; } else if (!HasNextLoop()) { - SetFailed(TaskStatus.MAX_LOOP_LIMIT); + return TaskStatus.MAX_LOOP_LIMIT; } else { - Template_Execute(false); + return TaskStatus.RUNNING; } } } diff --git a/Wjybxx.BTree.Core/src/Decorator/UntilSuccess.cs b/Wjybxx.BTree.Core/src/Decorator/UntilSuccess.cs index fcc1fb6..8215c80 100644 --- a/Wjybxx.BTree.Core/src/Decorator/UntilSuccess.cs +++ b/Wjybxx.BTree.Core/src/Decorator/UntilSuccess.cs @@ -30,22 +30,16 @@ public UntilSuccess() { public UntilSuccess(Task child) : base(child) { } - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); + protected override int OnChildCompleted(Task child) { if (child.IsCancelled) { - SetCancelled(); - return; + return TaskStatus.CANCELLED; } if (child.IsSucceeded) { - SetSuccess(); + return TaskStatus.SUCCESS; } else if (!HasNextLoop()) { - SetFailed(TaskStatus.MAX_LOOP_LIMIT); + return TaskStatus.MAX_LOOP_LIMIT; } else { - Template_Execute(false); + return TaskStatus.RUNNING; } } } diff --git a/Wjybxx.BTree.Core/src/FSM/ChangeStateArgs.cs b/Wjybxx.BTree.Core/src/FSM/ChangeStateArgs.cs deleted file mode 100644 index d8d4932..0000000 --- a/Wjybxx.BTree.Core/src/FSM/ChangeStateArgs.cs +++ /dev/null @@ -1,120 +0,0 @@ -#region LICENSE - -// Copyright 2024 wjybxx(845740757@qq.com) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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. - -#endregion - -namespace Wjybxx.BTree.FSM -{ -/// -/// 状态切换参数 -/// 建议用户通过原型对象的{@link #withExtraInfo(object)}等方法创建 -/// -public class ChangeStateArgs -{ - public const byte CMD_NONE = 0; - public const byte CMD_UNDO = 1; - public const byte CMD_REDO = 2; - - /// - /// 不延迟 - /// 1.delayArg为当前状态要设置的结果,大于0有效 -- 用于更好的支持FSM。 - /// 2.通常用于状态主动退出时,可避免自身进入被取消状态。 - /// - public const byte DELAY_NONE = 0; - /// - /// 在当前子节点完成的时候切换 - /// 1.其它延迟模式也会在当前状态完成时触发 - /// 2.通常用于状态主动退出时,可避免自身进入被取消状态 -- 先调用changeState,然后setSuccess - /// - public const byte DELAY_CURRENT_COMPLETED = 1; - - #region 共享原型 - - public static readonly ChangeStateArgs PLAIN = new(0, 0, 0, null); - public static readonly ChangeStateArgs PLAIN_WHEN_COMPLETED = new(0, DELAY_CURRENT_COMPLETED, 0, null); - - public static readonly ChangeStateArgs PLAIN_SUCCESS = new(0, 0, TaskStatus.SUCCESS, null); - public static readonly ChangeStateArgs PLAIN_CANCELLED = new(0, 0, TaskStatus.CANCELLED, null); - public static readonly ChangeStateArgs PLAIN_ERROR = new(0, 0, TaskStatus.ERROR, null); - - public static readonly ChangeStateArgs UNDO = new(CMD_UNDO, 0, 0, null); - public static readonly ChangeStateArgs UNDO_WHEN_COMPLETED = new(CMD_UNDO, DELAY_CURRENT_COMPLETED, 0, null); - - public static readonly ChangeStateArgs REDO = new(CMD_REDO, 0, 0, null); - public static readonly ChangeStateArgs REDO_WHEN_COMPLETED = new(CMD_REDO, DELAY_CURRENT_COMPLETED, 0, null); - - #endregion - - /** 切换命名 */ - public readonly byte cmd; - /** 延迟模式 -- 允许用户扩展 */ - public readonly byte delayMode; - /** 期望开始运行的帧号;-1表示尚未指定 */ - public readonly int delayArg; - /** 期望传递给Listener的数据 */ - public readonly object? extraInfo; - - /** 通过原型对象创建 */ - private ChangeStateArgs(byte cmd, byte delayMode, int delayArg, object? extraInfo) { -// checkCmd(cmd); // 封闭构造方法后可不校验 - this.delayMode = delayMode; - this.cmd = cmd; - this.delayArg = delayArg; - this.extraInfo = extraInfo; - } - - public bool IsPlain() { - return cmd == 0; - } - - public bool IsUndo() { - return cmd == CMD_UNDO; - } - - public bool IsRedo() { - return cmd == CMD_REDO; - } - - #region 原型方法 - - public ChangeStateArgs With(byte delayMode, int delayArg = 0) { - if (delayMode == this.delayMode && delayArg == this.delayArg) { - return this; - } - return new ChangeStateArgs(cmd, delayMode, delayArg, extraInfo); - } - - public ChangeStateArgs With(byte delayMode, int delayArg, object? extraInfo) { - return new ChangeStateArgs(cmd, delayMode, delayArg, extraInfo); - } - - public ChangeStateArgs WithArg(int delayArg) { - if (delayArg == this.delayArg) { - return this; - } - return new ChangeStateArgs(cmd, delayMode, delayArg, extraInfo); - } - - public ChangeStateArgs WithExtraInfo(object? extraInfo) { - if (extraInfo == this.extraInfo) { - return this; - } - return new ChangeStateArgs(cmd, delayMode, delayArg, extraInfo); - } - - #endregion -} -} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/FSM/ChangeStateTask.cs b/Wjybxx.BTree.Core/src/FSM/ChangeStateTask.cs deleted file mode 100644 index be21ab0..0000000 --- a/Wjybxx.BTree.Core/src/FSM/ChangeStateTask.cs +++ /dev/null @@ -1,114 +0,0 @@ -#region LICENSE - -// Copyright 2024 wjybxx(845740757@qq.com) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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. - -#endregion - -using System; -using Wjybxx.Commons; - -namespace Wjybxx.BTree.FSM -{ -/// -/// 切换状态任务 -/// -/// -public class ChangeStateTask : LeafTask where T : class -{ - /** 下一个状态的guid -- 延迟加载 */ - private string? nextStateGuid; - /** 下一个状态的对象缓存,通常延迟加载以避免循环引用 */ - [NonSerialized] private Task? nextState; - /** 目标状态的属性 */ - private object? stateProps; - - /** 目标状态机的名字,以允许切换更顶层的状态机 */ - private string? machineName; - /** 延迟模式 */ - private byte delayMode; - /** 延迟参数 */ - private int delayArg; - - public ChangeStateTask() { - } - - public ChangeStateTask(Task nextState) { - this.nextState = nextState; - } - - public override void ResetForRestart() { - base.ResetForRestart(); - if (nextState != null && nextState.Control == null) { - nextState.ResetForRestart(); - } - } - - protected override void Execute() { - if (nextState == null) { - if (string.IsNullOrEmpty(nextStateGuid)) { - throw new IllegalStateException("guid is empty"); - } - nextState = TaskEntry.TreeLoader.LoadRootTask(nextStateGuid); - } - if (stateProps != null) { - nextState.SharedProps = stateProps; - } - - int reentryId = ReentryId; - StateMachineTask stateMachine = StateMachineTask.FindStateMachine(this, machineName); - if (delayMode == 0) { - stateMachine.ChangeState(nextState, delayArg); - } else { - stateMachine.ChangeState(nextState, ChangeStateArgs.PLAIN.With(delayMode, delayArg)); - } - if (!IsExited(reentryId)) { - SetSuccess(); - } - } - - protected override void OnEventImpl(object eventObj) { - } - - public string? NextStateGuid { - get => nextStateGuid; - set => nextStateGuid = value; - } - - public Task? NextState { - get => nextState; - set => nextState = value; - } - - public object? StateProps { - get => stateProps; - set => stateProps = value; - } - - public string? MachineName { - get => machineName; - set => machineName = value; - } - - public byte DelayMode { - get => delayMode; - set => delayMode = value; - } - - public int DelayArg { - get => delayArg; - set => delayArg = value; - } -} -} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/FSM/StackStateMachineTask.cs b/Wjybxx.BTree.Core/src/FSM/StackStateMachineTask.cs deleted file mode 100644 index 209244d..0000000 --- a/Wjybxx.BTree.Core/src/FSM/StackStateMachineTask.cs +++ /dev/null @@ -1,199 +0,0 @@ -#region LICENSE - -// Copyright 2024 wjybxx(845740757@qq.com) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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. - -#endregion - -using System; -using Wjybxx.Commons.Collections; - -namespace Wjybxx.BTree.FSM -{ -/// -/// 状态机节点 -/// -/// -public class StackStateMachineTask : StateMachineTask where T : class -{ - private const int QUEUE_CAPACITY = 5; - // 需要支持编辑器设置 - private int undoQueueCapacity = QUEUE_CAPACITY; - private int redoQueueCapacity = QUEUE_CAPACITY; - [NonSerialized] - private readonly BoundedArrayDeque> undoQueue = new(0, DequeOverflowBehavior.DiscardHead); - [NonSerialized] - private readonly BoundedArrayDeque> redoQueue = new(0, DequeOverflowBehavior.DiscardTail); - - #region api - - /** 查看undo对应的state */ - public Task? PeekUndoState() { - return undoQueue.TryPeekLast(out Task r) ? r : null; - } - - /** 查看redo对应的state */ - public Task? PeekRedoState() { - return redoQueue.TryPeekFirst(out Task r) ? r : null; - } - - /// - /// - /// - /// 最大大小;0表示禁用;大于0启用 - /// 最新的queue - public void SetUndoQueueCapacity(int capacity) { - if (capacity < 0) throw new ArgumentException("capacity: " + capacity); - this.undoQueueCapacity = capacity; - undoQueue.SetCapacity(capacity, DequeOverflowBehavior.DiscardHead); - } - - /// - /// - /// - /// 最大大小;0表示禁用;大于0启用 - /// 最新的queue - public void SetRedoQueueCapacity(int capacity) { - if (capacity < 0) throw new ArgumentException("capacity: " + capacity); - this.redoQueueCapacity = capacity; - redoQueue.SetCapacity(capacity, DequeOverflowBehavior.DiscardTail); - } - - /// - /// 向undo队列中添加一个状态 - /// - /// - /// 是否添加成功 - public bool AddUndoState(Task curState) { - if (undoQueueCapacity < 1) { - return false; - } - undoQueue.AddLast(curState); - return true; - } - - /// - /// 向redo队列中添加一个状态 - /// - /// 是否添加成功 - /// - public bool AddRedoState(Task curState) { - if (redoQueueCapacity < 1) { - return false; - } - redoQueue.AddFirst(curState); - return true; - } - - /// - /// 撤销到前一个状态 - /// - /// 状态切换参数 - /// 如果有前一个状态则返回true - public override bool UndoChangeState(ChangeStateArgs changeStateArgs) { - if (!changeStateArgs.IsUndo()) { - throw new ArgumentException(); - } - // 真正切换以后再删除 - if (!undoQueue.TryPeekLast(out Task prevState)) { - return false; - } - ChangeState(prevState, changeStateArgs); - return true; - } - - /// - /// 重新进入到下一个状态 - /// - /// 状态切换参数 - /// 如果有下一个状态则返回true - public override bool RedoChangeState(ChangeStateArgs changeStateArgs) { - if (!changeStateArgs.IsRedo()) { - throw new ArgumentException(); - } - // 真正切换以后再删除 - if (!redoQueue.TryPeekFirst(out Task nextState)) { - return false; - } - ChangeState(nextState, changeStateArgs); - return true; - } - - #endregion - - #region logic - - public override void ResetForRestart() { - base.ResetForRestart(); - undoQueue.Clear(); - redoQueue.Clear(); - // 不重写beforeEnter,是因为考虑保留用户的初始队列设置 - } - - protected override void Exit() { - undoQueue.Clear(); - redoQueue.Clear(); - base.Exit(); - } - - protected override void BeforeChangeState(Task? curState, Task? nextState) { - if (nextState == null) { - AddUndoState(curState!); - return; - } - ChangeStateArgs changeStateArgs = (ChangeStateArgs)nextState.ControlData; - switch (changeStateArgs.cmd) { - case ChangeStateArgs.CMD_UNDO: { - undoQueue.TryRemoveLast(out _); - if (curState != null) { - AddRedoState(curState); - } - break; - } - case ChangeStateArgs.CMD_REDO: { - redoQueue.TryRemoveFirst(out _); - if (curState != null) { - AddUndoState(curState); - } - break; - } - default: { - // 进入新状态,需要清理redo队列 - redoQueue.Clear(); - if (curState != null) { - AddUndoState(curState); - } - break; - } - } - base.BeforeChangeState(curState, nextState); - } - - #endregion - - #region 序列化 - - public int UndoQueueCapacity { - get => undoQueueCapacity; - set => SetUndoQueueCapacity(value); - } - - public int RedoQueueCapacity { - get => redoQueueCapacity; - set => SetRedoQueueCapacity(value); - } - - #endregion -} -} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/FSM/StateMachineHandler.cs b/Wjybxx.BTree.Core/src/FSM/StateMachineHandler.cs deleted file mode 100644 index 3792ff1..0000000 --- a/Wjybxx.BTree.Core/src/FSM/StateMachineHandler.cs +++ /dev/null @@ -1,86 +0,0 @@ -#region LICENSE - -// Copyright 2024 wjybxx(845740757@qq.com) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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. - -#endregion - -namespace Wjybxx.BTree.FSM -{ -/// -/// 状态机扩展处理器 -/// -/// -public interface IStateMachineHandler where T : class -{ - /// - /// handler可能也有需要重置的数据 - /// - /// - void ResetForRestart(StateMachineTask stateMachineTask) { - } - - /// - /// handler可能也有需要初始化的数据 - /// - /// - void BeforeEnter(StateMachineTask stateMachineTask) { - } - - /// - /// 是否可以切换到下一个状态 - /// - /// 状态机 - /// 当前状态 - /// 要切换的状态 - /// - bool IsReady(StateMachineTask stateMachineTask, Task? curState, Task nextState) { - if (curState == null || curState.IsCompleted) { - return true; - } - ChangeStateArgs changeStateArgs = (ChangeStateArgs)nextState.ControlData; - return changeStateArgs.delayMode == ChangeStateArgs.DELAY_NONE; - } - - /// - /// 该方法在进入新状态前调用 - /// - /// 1.两个参数最多一个为null - /// 2.可以设置新状态的黑板和其它数据 - /// 3.用户此时可为新状态分配上下文(黑板、取消令牌、共享属性);同时清理前一个状态的上下文 - /// 4.用户此时可拿到新状态的状态切换参数,后续则不可 - /// 5.如果task需要感知redo和undo,则由用户将信息写入黑板 - /// - /// 状态机 - /// 当前状态 - /// 下一个状态 - void BeforeChangeState(StateMachineTask stateMachineTask, Task? curState, Task? nextState) { - } - - /// - /// 当状态机没有下一个状态时调用该方法,以避免无可用状态 - /// - /// 注意: - /// 1.状态机启动时不会调用该方法 - /// 2.如果该方法返回后仍无可用状态,将触发无状态逻辑 - /// - /// 状态机 - /// 前一个状态,用于计算下一个状态 - /// 用户是否执行了【状态切换】或【停止状态机】 - bool OnNextStateAbsent(StateMachineTask stateMachineTask, Task preState) { - stateMachineTask.SetCompleted(preState.Status, true); - return true; - } -} -} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/FSM/StateMachineHandlers.cs b/Wjybxx.BTree.Core/src/FSM/StateMachineHandlers.cs deleted file mode 100644 index 6056896..0000000 --- a/Wjybxx.BTree.Core/src/FSM/StateMachineHandlers.cs +++ /dev/null @@ -1,119 +0,0 @@ -#region LICENSE - -// Copyright 2024 wjybxx(845740757@qq.com) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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. - -#endregion - -using System; - -namespace Wjybxx.BTree.FSM -{ -public static class StateMachineHandlers -{ - public static IStateMachineHandler DefaultHandler() where T : class { - return DefaultStateMachineHandler.Inst; - } - - public static IStateMachineHandler RedoHandler() where T : class { - return CRedoHandler.Inst; - } - - public static IStateMachineHandler UndoHandler() where T : class { - return CUndoHandler.Inst; - } - - public static IStateMachineHandler OfListener(StateMachineListener listener) where T : class { - if (listener == null) throw new ArgumentNullException(nameof(listener)); - return new ListenerHandler(listener); - } - - private class DefaultStateMachineHandler : IStateMachineHandler where T : class - { - internal static readonly DefaultStateMachineHandler Inst = new DefaultStateMachineHandler(); - - public void ResetForRestart(StateMachineTask stateMachineTask) { - } - - public void BeforeEnter(StateMachineTask stateMachineTask) { - } - - public void BeforeChangeState(StateMachineTask stateMachineTask, Task? curState, Task? nextState) { - } - } - - private class ListenerHandler : IStateMachineHandler where T : class - { - private readonly StateMachineListener _listener; - - public ListenerHandler(StateMachineListener listener) { - _listener = listener; - } - - public void BeforeChangeState(StateMachineTask stateMachineTask, Task? curState, Task? nextState) { - _listener.Invoke(stateMachineTask, curState, nextState); - } - - public void ResetForRestart(StateMachineTask stateMachineTask) { - } - - public void BeforeEnter(StateMachineTask stateMachineTask) { - } - } - - private class CRedoHandler : IStateMachineHandler where T : class - { - internal static readonly CRedoHandler Inst = new CRedoHandler(); - - public bool OnNextStateAbsent(StateMachineTask stateMachineTask, Task preState) { - if (stateMachineTask.RedoChangeState()) { - return true; - } - stateMachineTask.SetCompleted(preState.Status, true); - return true; - } - - public void ResetForRestart(StateMachineTask stateMachineTask) { - } - - public void BeforeEnter(StateMachineTask stateMachineTask) { - } - - public void BeforeChangeState(StateMachineTask stateMachineTask, Task? curState, Task? nextState) { - } - } - - private class CUndoHandler : IStateMachineHandler where T : class - { - internal static readonly CUndoHandler Inst = new CUndoHandler(); - - public bool OnNextStateAbsent(StateMachineTask stateMachineTask, Task preState) { - if (stateMachineTask.UndoChangeState()) { - return true; - } - stateMachineTask.SetCompleted(preState.Status, true); - return true; - } - - public void ResetForRestart(StateMachineTask stateMachineTask) { - } - - public void BeforeEnter(StateMachineTask stateMachineTask) { - } - - public void BeforeChangeState(StateMachineTask stateMachineTask, Task? curState, Task? nextState) { - } - } -} -} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/FSM/StateMachineListener.cs b/Wjybxx.BTree.Core/src/FSM/StateMachineListener.cs deleted file mode 100644 index 3ddd543..0000000 --- a/Wjybxx.BTree.Core/src/FSM/StateMachineListener.cs +++ /dev/null @@ -1,34 +0,0 @@ -#region LICENSE - -// Copyright 2024 wjybxx(845740757@qq.com) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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. - -#endregion - -namespace Wjybxx.BTree.FSM -{ -/// -/// 该方法在进入新状态前调用 -/// -/// 1.两个参数最多一个为null -/// 2.可以设置新状态的黑板和其它数据 -/// 3.用户此时可为新状态分配上下文;同时清理前一个状态的上下文 -/// 4.用户此时可拿到新状态的状态切换参数,后续则不可 -/// 5.如果task需要感知redo和undo,则由用户将信息写入黑板 -/// -/// -public delegate void StateMachineListener(StateMachineTask stateMachineTask, - Task? curState, - Task? nextState) where T : class; -} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/FSM/StateMachineTask.cs b/Wjybxx.BTree.Core/src/FSM/StateMachineTask.cs deleted file mode 100644 index e5599de..0000000 --- a/Wjybxx.BTree.Core/src/FSM/StateMachineTask.cs +++ /dev/null @@ -1,336 +0,0 @@ -#region LICENSE - -// Copyright 2024 wjybxx(845740757@qq.com) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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. - -#endregion - -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using Wjybxx.BTree.Branch; -using Wjybxx.Commons; - -namespace Wjybxx.BTree.FSM -{ -/// -/// 状态机节点 -/// ps:以我的经验来看,状态机是最重要的节点,则是仅次于状态机的节点 -- 不能以使用数量而定。 -/// -/// -public class StateMachineTask : Decorator where T : class -{ - /** 状态机名字 */ - private string? name; - /** 初始状态 */ - private Task? initState; - /** 初始状态的属性 */ - private object? initStateProps; - - /** 待切换的状态,主要用于支持当前状态退出后再切换 */ - [NonSerialized] private Task? tempNextState; - /** 默认不序列化 -- 删除了Listener委托,因为不能反序列化 */ - [NonSerialized] private IStateMachineHandler handler = StateMachineHandlers.DefaultHandler(); - - #region api - - /** 获取当前状态 */ - public Task? CurState => child; - - /** 获取临时的下一个状态 */ - public Task? TempNextState => tempNextState; - - /** 丢弃未切换的临时状态 */ - public Task? DiscardNextState() { - Task? r = tempNextState; - if (r != null) tempNextState = null; - return r; - } - - /// - /// 撤销到前一个状态 - /// - /// 如果有前一个状态则返回true - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool UndoChangeState() { - return UndoChangeState(ChangeStateArgs.UNDO); - } - - /// - /// 撤销到前一个状态 - /// - /// 状态切换参数 - /// 如果有前一个状态则返回true - public virtual bool UndoChangeState(ChangeStateArgs changeStateArgs) { - return false; - } - - /// - /// 重新进入到下一个状态 - /// - /// 如果有下一个状态则返回true - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool RedoChangeState() { - return RedoChangeState(ChangeStateArgs.REDO); - } - - /// - /// 重新进入到下一个状态 - /// - /// 状态切换参数 - /// 如果有下一个状态则返回true - public virtual bool RedoChangeState(ChangeStateArgs changeStateArgs) { - return false; - } - - /** 切换状态 -- 如果状态机处于运行中,则立即切换 */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ChangeState(Task nextState) { - ChangeState(nextState, ChangeStateArgs.PLAIN); - } - - /// - /// 切换状态 -- 如果状态机处于运行中,则立即切换 - /// - /// 要进入的下一个状态 - /// 当前状态的结果 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ChangeState(Task nextState, int curStateResult) { - ChangeStateArgs changeStateArgs = curStateResult switch - { - TaskStatus.SUCCESS => ChangeStateArgs.PLAIN_SUCCESS, - TaskStatus.CANCELLED => ChangeStateArgs.PLAIN_CANCELLED, - TaskStatus.ERROR => ChangeStateArgs.PLAIN_ERROR, - _ => ChangeStateArgs.PLAIN.WithArg(curStateResult) - }; - ChangeState(nextState, changeStateArgs); - } - - /// - /// 切换状态 - /// 1.如果当前有一个待切换的状态,则会被悄悄丢弃(todo 可以增加一个通知) - /// 2.无论何种模式,在当前状态进入完成状态时一定会触发 - /// 3.如果状态机未运行,则仅仅保存在那里,等待下次运行的时候执行。 - /// 4.关于如何避免当前状态被取消,可参考 - /// - /// 要进入的下一个状态 - /// 状态切换参数 - /// - public void ChangeState(Task nextState, ChangeStateArgs changeStateArgs) { - if (nextState == null) throw new ArgumentNullException(nameof(nextState)); - if (changeStateArgs == null) throw new ArgumentNullException(nameof(changeStateArgs)); - - nextState.ControlData = changeStateArgs; - tempNextState = nextState; - - if (IsRunning && handler.IsReady(this, child, nextState)) { - Template_Execute(false); - } - } - - #endregion - - #region logic - - public override void ResetForRestart() { - base.ResetForRestart(); - handler.ResetForRestart(this); - if (initState != null) { - initState.ResetForRestart(); - } - if (child != null) { - RemoveChild(0); - } - tempNextState = null; - } - - protected override void BeforeEnter() { - // base.BeforeEnter(); - handler.BeforeEnter(this); - if (initState != null && initStateProps != null) { - initState.SharedProps = initStateProps; - } - if (tempNextState == null && initState != null) { - tempNextState = initState; - } - if (tempNextState != null && tempNextState.ControlData == null) { - tempNextState.ControlData = ChangeStateArgs.PLAIN; - } - // 不清理child是因为允许用户提前指定初始状态 - } - - protected override void Exit() { - tempNextState = null; - if (child != null) { - RemoveChild(0); - } - base.Exit(); - } - - protected override void Execute() { - Task? curState = this.child; - Task? nextState = this.tempNextState; - if (nextState != null && handler.IsReady(this, curState, nextState)) { - StopCurState(curState, (ChangeStateArgs)nextState.ControlData); - - this.tempNextState = null; - if (curState != null) { - SetChild(0, nextState); - } else { - AddChild(nextState); - } - - BeforeChangeState(curState, nextState); - nextState.ControlData = null; // 用户需要提前将数据填充到黑板 - Template_StartChild(nextState, true); // 启动新状态 - return; - } - if (curState == null) { - return; - } - // 继续运行或新状态enter;在尾部才能保证安全 - Task? inlinedChild = inlineHelper.GetInlinedChild(); - if (inlinedChild != null) { - inlinedChild.Template_ExecuteInlined(ref inlineHelper, curState); - } else if (curState.IsRunning) { - curState.Template_Execute(true); - } else { - Template_StartChild(curState, true); - } - } - - private void StopCurState(Task? curState, ChangeStateArgs changeStateArgs) { - if (curState == null) return; - if (changeStateArgs.delayMode == 0 && changeStateArgs.delayArg > 0) { - curState.Stop(changeStateArgs.delayArg); - } else { - curState.Stop(); - } - inlineHelper.StopInline(); // help gc - } - - protected virtual void BeforeChangeState(Task? curState, Task? nextState) { - Debug.Assert(curState != null || nextState != null); - handler.BeforeChangeState(this, curState, nextState); - } - - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - Debug.Assert(this.child == child); - inlineHelper.StopInline(); - - // 先判断是否有下一个状态,保持和changeState调用相同的逻辑 - if (tempNextState != null) { - Template_Execute(false); - return; - } - if (handler.OnNextStateAbsent(this, child)) { - return; - } - RemoveChild(0); - BeforeChangeState(child, null); - } - - #endregion - - #region find - - /** - * 查找task最近的状态机节点 - * 1.仅递归查询父节点和长兄节点 - * 2.优先查找附近的,然后测试长兄节点 - 状态机作为第一个节点的情况比较常见 - */ - public static StateMachineTask FindStateMachine(Task task) { - Task control; - while ((control = task.Control) != null) { - // 父节点 - if (control is StateMachineTask stateMachineTask1) { - return stateMachineTask1; - } - // 长兄节点 - Task eldestBrother = control.GetChild(0); - if (eldestBrother is StateMachineTask stateMachineTask2) { - return stateMachineTask2; - } - task = control; - } - throw new IllegalStateException("cant find stateMachine from controls"); - } - - /** - * 查找task最近的状态机节点 - * 1.名字不为空的情况下,支持从兄弟节点中查询 - * 2.优先测试父节点,然后测试兄弟节点 - */ - public static StateMachineTask FindStateMachine(Task task, string? name) { - if (string.IsNullOrWhiteSpace(name)) { - return FindStateMachine(task); - } - Task? control; - StateMachineTask? stateMachine; - while ((control = task.Control) != null) { - // 父节点 - if ((stateMachine = CastAsStateMachine(control, name)) != null) { - return stateMachine; - } - // 兄弟节点 - for (int i = 0, n = control.ChildCount; i < n; i++) { - Task brother = control.GetChild(i); - if ((stateMachine = CastAsStateMachine(brother, name)) != null) { - return stateMachine; - } - } - task = control; - } - throw new IllegalStateException("cant find stateMachine from controls and brothers"); - } - - private static StateMachineTask? CastAsStateMachine(Task task, string name) { - if (task is StateMachineTask stateMachineTask - && (name == stateMachineTask.name)) { - return stateMachineTask; - } - return null; - } - - #endregion - - #region 序列化 - - public string? Name { - get => name; - set => name = value; - } - - public Task? InitState { - get => initState; - set => initState = value; - } - - public object? InitStateProps { - get => initStateProps; - set => initStateProps = value; - } - - public IStateMachineHandler? Handler { - get => handler; - set => handler = value ?? StateMachineHandlers.DefaultHandler(); - } - - #endregion -} -} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Leaf/ActionTask.cs b/Wjybxx.BTree.Core/src/Leaf/ActionTask.cs deleted file mode 100644 index 5c5f37d..0000000 --- a/Wjybxx.BTree.Core/src/Leaf/ActionTask.cs +++ /dev/null @@ -1,64 +0,0 @@ -#region LICENSE - -// Copyright 2024 wjybxx(845740757@qq.com) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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. - -#endregion - -using Wjybxx.Commons; - -namespace Wjybxx.BTree.Leaf -{ -/// -/// 行为节点抽象 -/// (并非所有行为节点都需要继承该类) -/// -/// -public abstract class ActionTask : LeafTask where T : class -{ - protected sealed override void Execute() { - int reentryId = ReentryId; - int status = ExecuteImpl(); - if (IsExited(reentryId)) { - return; - } - switch (status) { - case TaskStatus.NEW: { - throw new IllegalStateException("Illegal action status: " + status); - } - case TaskStatus.RUNNING: { - break; - } - case TaskStatus.SUCCESS: { - SetSuccess(); - break; - } - case TaskStatus.CANCELLED: { - SetCancelled(); - break; - } - default: { - SetFailed(status); - break; - } - } - } - - /// - /// 我们的大多数行为节点逻辑都较为简单,不需要事件驱动特性,因而可以转换为同步返回的节点。 - /// - /// 状态码 - protected abstract int ExecuteImpl(); -} -} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Leaf/ConditionTask.cs b/Wjybxx.BTree.Core/src/Leaf/ConditionTask.cs deleted file mode 100644 index 15d65c9..0000000 --- a/Wjybxx.BTree.Core/src/Leaf/ConditionTask.cs +++ /dev/null @@ -1,76 +0,0 @@ -#region LICENSE - -// Copyright 2024 wjybxx(845740757@qq.com) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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. - -#endregion - -using Wjybxx.Commons; - -namespace Wjybxx.BTree.Leaf -{ -/// -/// 条件节点 -/// 注意:并非条件节点必须继承该类。 -/// -///

开销问题

-/// Task类是比较大的,如果项目中有大量的条件,需要考虑开销问题。 -/// 一种解决方案是:使用Task类做壳,作为条件测试的入口,内部使用自定义类型。 -/// -/// public class ConditionEntry<T> extends LeafTask<T> { -/// private int type; -/// private List<ICondition> children = new List<ICondition>(4); -/// } -/// -/// -/// ps:该类其实可以直接继承Task类,以减少继承层次。 -///
-/// -public abstract class ConditionTask : LeafTask where T : class -{ - protected sealed override void Execute() { - int status = Test(); - switch (status) { - case TaskStatus.NEW: - case TaskStatus.RUNNING: - case TaskStatus.CANCELLED: { - throw new IllegalStateException("Illegal condition status: " + status); - } - case TaskStatus.SUCCESS: { - SetSuccess(); - break; - } - default: { - SetFailed(status); - break; - } - } - } - - /// - /// 检查条件 -- 同步返回 - /// - /// 状态码 - protected abstract int Test(); - - /** 条件节点正常情况下不会触发事件 */ - public override bool CanHandleEvent(object _) { - return false; - } - - /** 条件节点正常情况下不会触发事件 */ - protected override void OnEventImpl(object _) { - } -} -} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/Leaf/Failure.cs b/Wjybxx.BTree.Core/src/Leaf/Failure.cs index 4516071..d9d790a 100644 --- a/Wjybxx.BTree.Core/src/Leaf/Failure.cs +++ b/Wjybxx.BTree.Core/src/Leaf/Failure.cs @@ -26,8 +26,8 @@ public class Failure : LeafTask where T : class { private int failureStatus; - protected override void Execute() { - SetFailed(TaskStatus.ToFailure(failureStatus)); + protected override int Execute() { + return TaskStatus.ToFailure(failureStatus); } protected override void OnEventImpl(object eventObj) { diff --git a/Wjybxx.BTree.Core/src/Leaf/Running.cs b/Wjybxx.BTree.Core/src/Leaf/Running.cs index d6dab81..a3b2c69 100644 --- a/Wjybxx.BTree.Core/src/Leaf/Running.cs +++ b/Wjybxx.BTree.Core/src/Leaf/Running.cs @@ -24,7 +24,8 @@ namespace Wjybxx.BTree.Leaf /// public class Running : LeafTask where T : class { - protected override void Execute() { + protected override int Execute() { + return TaskStatus.RUNNING; } protected override void OnEventImpl(object eventObj) { diff --git a/Wjybxx.BTree.Core/src/Leaf/SimpleRandom.cs b/Wjybxx.BTree.Core/src/Leaf/SimpleRandom.cs index 75cd2db..df85236 100644 --- a/Wjybxx.BTree.Core/src/Leaf/SimpleRandom.cs +++ b/Wjybxx.BTree.Core/src/Leaf/SimpleRandom.cs @@ -17,6 +17,7 @@ #endregion using Wjybxx.Commons; +using Wjybxx.Commons.Collections; namespace Wjybxx.BTree.Leaf { @@ -36,11 +37,11 @@ public SimpleRandom(float p = 0.5f) { this.p = p; } - protected override void Execute() { - if (MathCommon.SharedRandom.NextDouble() <= p) { - SetSuccess(); + protected override int Execute() { + if (CollectionUtil.SharedRandom.NextDouble() <= p) { + return TaskStatus.SUCCESS; } else { - SetFailed(TaskStatus.ERROR); + return TaskStatus.ERROR; } } diff --git a/Wjybxx.BTree.Core/src/Leaf/Success.cs b/Wjybxx.BTree.Core/src/Leaf/Success.cs index 5cbd59d..c0662cd 100644 --- a/Wjybxx.BTree.Core/src/Leaf/Success.cs +++ b/Wjybxx.BTree.Core/src/Leaf/Success.cs @@ -24,8 +24,8 @@ namespace Wjybxx.BTree.Leaf /// public class Success : LeafTask where T : class { - protected override void Execute() { - SetSuccess(); + protected override int Execute() { + return TaskStatus.SUCCESS; } protected override void OnEventImpl(object eventObj) { diff --git a/Wjybxx.BTree.Core/src/Leaf/WaitFrame.cs b/Wjybxx.BTree.Core/src/Leaf/WaitFrame.cs index fdb09eb..1f44ddd 100644 --- a/Wjybxx.BTree.Core/src/Leaf/WaitFrame.cs +++ b/Wjybxx.BTree.Core/src/Leaf/WaitFrame.cs @@ -33,10 +33,11 @@ public WaitFrame(int required) { this.required = required; } - protected override void Execute() { + protected override int Execute() { if (RunFrames >= required) { - SetSuccess(); + return TaskStatus.SUCCESS; } + return TaskStatus.RUNNING; } protected override void OnEventImpl(object eventObj) { diff --git a/Wjybxx.BTree.Core/src/LeafTask.cs b/Wjybxx.BTree.Core/src/LeafTask.cs index 5fd1410..fdb311c 100644 --- a/Wjybxx.BTree.Core/src/LeafTask.cs +++ b/Wjybxx.BTree.Core/src/LeafTask.cs @@ -27,14 +27,6 @@ namespace Wjybxx.BTree /// public abstract class LeafTask : Task where T : class { - protected sealed override void OnChildRunning(Task child) { - throw new AssertionError(); - } - - protected sealed override void OnChildCompleted(Task child) { - throw new AssertionError(); - } - #nullable disable #region child diff --git a/Wjybxx.BTree.Core/src/Task.cs b/Wjybxx.BTree.Core/src/Task.cs index d61177f..1fa2e21 100644 --- a/Wjybxx.BTree.Core/src/Task.cs +++ b/Wjybxx.BTree.Core/src/Task.cs @@ -42,6 +42,8 @@ namespace Wjybxx.BTree /// Task是泛型的,我的Dson库将支持泛型类的序列化;BTree.Codec中的编解码器由Dson库的注解处理器生成。 /// (脚本在测试用例模块) /// +/// 提供了条件编译参数 TASK_MANUAL_CHECK_CANCEL 可关闭模板方法中的大量取消信号检测 +/// /// 黑板的类型 ///
public abstract class Task : ICancelTokenListener where T : class @@ -122,6 +124,8 @@ public abstract class Task : ICancelTokenListener where T : class [NonSerialized] private int exitFrame; /** 重入Id,只增不减 -- 用于事件驱动下检测冲突(递归);reset时不重置,甚至也增加 */ [NonSerialized] private short reentryId; + /** 被内联执行时的状态 */ + [NonSerialized] private int delayedStatus; /// /// 任务绑定的前置条件(precondition太长...) @@ -307,17 +311,20 @@ protected virtual void BeforeEnter() { /// 2.如果要初始化子节点,也放到方法; /// 3.允许更新自己为完成状态 /// - /// 用于判断父类是否使任务进入了完成状态;虽然也可先捕获再调用超类方法,但传入会方便许多。 - protected virtual void Enter(int reentryId) { + /// 最新状态 + protected virtual int Enter() { + return TaskStatus.RUNNING; } /// /// Task的心跳方法,在Task进入完成状态之前会反复执行。 - /// 1.运行中可通过将自己更新为完成状态。 - /// 2.如果不想和同步执行,可通过 实现。 - /// 3.不建议直接调用该方法,而是通过模板方法运行。 + /// 1.如果不想和同步执行,可通过 实现。 + /// 2.不建议直接调用该方法,而是通过模板方法运行。 + /// + /// 注意:如果是支持被内联的Task,第一行应当是运行处于运行状态的子节点,包含。 /// - protected abstract void Execute(); + /// 最新状态 + protected abstract int Execute(); /// /// 该方法在Task进入完成状态时执行 @@ -328,60 +335,37 @@ protected virtual void Enter(int reentryId) { protected virtual void Exit() { } - /** 设置为运行成功 */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetSuccess() { - Debug.Assert(this.status == TaskStatus.RUNNING); - this.status = TaskStatus.SUCCESS; - Template_Exit(); - if ((ctl & MASK_DISABLE_NOTIFY) == 0 && control != null) { - control.OnChildCompleted(this); - } - } - - /** 设置为取消 */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetCancelled() { - SetCompleted(TaskStatus.CANCELLED, false); - } - - /** 设置为执行失败 */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetFailed(int status) { - if (status < TaskStatus.ERROR) { - throw new ArgumentException("status " + status); - } - SetCompleted(status, false); - } - - /// - /// 设置为前置条件测试失败 - /// 1.该方法仅适用于control测试child的guard失败,令child在未运行的情况下直接失败的情况。 - /// 2.对于运行中的child,如果发现child的guard失败,不能继续运行,应当取消子节点的执行(stop)。 - /// - /// 由于task未运行,其control可能尚未赋值,因此要传入 - /// 是否是钩子任务,钩子任务则禁用回调 + /** 设置为前提失败 */ [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetGuardFailed(Task control, bool isHook) { + private void SetGuardFailed(Task control, bool isHook) { Debug.Assert(this.status != TaskStatus.RUNNING); - SetCtlBit(MASK_DISABLE_NOTIFY, isHook); // 需要覆盖旧值 + // SetCtlBit(MASK_DISABLE_NOTIFY, isHook); // 需要覆盖旧值 SetControl(control); - SetCompleted(TaskStatus.GUARD_FAILED, false); + SetCompleted(TaskStatus.GUARD_FAILED); } - /** 设置为完成 -- 通常用于通过子节点的结果设置自己 */ - public void SetCompleted(int status, bool fromChild) { + /** 设置为完成 */ + private int SetCompleted(int status) { if (status < TaskStatus.SUCCESS) throw new ArgumentException(); - if (status == TaskStatus.GUARD_FAILED && fromChild) { - status = TaskStatus.ERROR; // GUARD_FAILED 不能向上传播 + if (status == TaskStatus.GUARD_FAILED) { + status = TaskStatus.ERROR; // 心跳驱动下使用GUARD_FAILED不安全 } int prevStatus = this.status; if (prevStatus == TaskStatus.RUNNING) { - if (status == TaskStatus.GUARD_FAILED) { - throw new ArgumentException("Running task cant fail with 'GUARD_FAILED'"); - } this.status = status; - Template_Exit(); + this.delayedStatus = 0; + + // 内联template_exit + if ((ctl & MASK_REGISTERED_LISTENER) != 0) { + cancelToken.RemListener(this); + } + exitFrame = taskEntry.CurFrame; + StopRunningChildren(); // 停止有特殊顺序,不能使用访问器 + if ((ctl & TaskOverrides.MASK_EXIT) != 0) { + Exit(); + } + ReleaseContext(); + reentryId++; } else { // 未调用Enter和Exit,需要补偿 -- 保留当前的ctl会更好 this.PrevStatus = prevStatus; @@ -390,32 +374,11 @@ public void SetCompleted(int status, bool fromChild) { ctl |= MASK_STILLBORN; this.status = status; + this.delayedStatus = 0; } - if ((ctl & MASK_DISABLE_NOTIFY) == 0 && control != null) { - control.OnChildCompleted(this); - } + return status; } - /// - /// 子节点还需要继续运行 - /// 1.child在一次运行期间只会通知一次 - /// 2.该方法不应该触发状态迁移,即不应该使自己进入完成状态 - /// - /// - protected abstract void OnChildRunning(Task child); - - /// - /// 子节点进入完成状态 - /// 1.避免方法数太多,实现类测试task的status即可 - /// 2.有助于switch测试 - /// 3.task可能是取消状态,甚至可能没运行过直接失败(前置条件失败) - /// 4.钩子任务和guard不会调用该方法 - /// 5.同一子节点连续通知的情况下,completed的逻辑应当覆盖的影响。 - /// 6.任何的回调和事件方法中都由用户自身检测取消信号 - /// - /// - protected abstract void OnChildCompleted(Task child); - /// /// Task收到外部事件 /// @see #onEventImpl(Object) @@ -468,7 +431,7 @@ public virtual bool CanHandleEvent(object eventObj) { /// /// 进入取消状态的取消令牌 public virtual void OnCancelRequested(CancelToken cancelToken) { - if (IsRunning) SetCompleted(TaskStatus.CANCELLED, false); + // 心跳驱动下不能立即取消 } /// @@ -484,8 +447,8 @@ public void Stop(int result = TaskStatus.CANCELLED) { } // 被显式调用stop的task不能通知父节点,只要任务执行过就需要标记 if (this.status == TaskStatus.RUNNING) { - ctl |= MASK_DISABLE_NOTIFY; - SetCompleted(result, false); + // ctl |= MASK_DISABLE_NOTIFY; + SetCompleted(result); } } @@ -532,6 +495,7 @@ public virtual void ResetForRestart() { ctl &= MASK_OVERRIDES; // 保留Overrides信息 enterFrame = 0; exitFrame = 0; + delayedStatus = 0; reentryId++; // 上下文变动,和之前的执行分开 } @@ -620,7 +584,7 @@ public bool CheckCancel(int rid) { return true; } if (cancelToken.IsCancelRequested) { // 这里是手动检查 - SetCompleted(TaskStatus.CANCELLED, false); + SetCompleted(TaskStatus.CANCELLED); return true; } return false; @@ -781,7 +745,7 @@ public bool IsBreakInline { #region 模板方法 /** start方法不暴露,否则以后难以改动 */ - internal void Template_Start(Task? control, int initMask) { + internal int Template_Start(Task? control, int initMask) { Debug.Assert(status != TaskStatus.RUNNING); // 初始化基础上下文后才可以检测取消 if (control != null) { @@ -795,8 +759,7 @@ internal void Template_Start(Task? control, int initMask) { CancelToken cancelToken = this.cancelToken; if (cancelToken.IsCancelRequested) { // 胎死腹中 ReleaseContext(); - SetCompleted(TaskStatus.CANCELLED, false); - return; + return SetCompleted(TaskStatus.CANCELLED); } int prevStatus = Math.Min(TaskStatus.MAX_PREV_STATUS, this.status); @@ -804,7 +767,7 @@ internal void Template_Start(Task? control, int initMask) { ctl = initMask; status = TaskStatus.RUNNING; // 先更新为running状态,以避免执行过程中外部查询task的状态时仍处于上一次的结束status enterFrame = exitFrame = taskEntry.CurFrame; - int reentryId = ++this.reentryId; // 和上次执行的exit分开 + ++reentryId; // 和上次执行的exit分开 // beforeEnter if ((initMask & TaskOverrides.MASK_BEFORE_ENTER) != 0) { BeforeEnter(); // 这里用户可能修改控制流标记 @@ -819,38 +782,32 @@ internal void Template_Start(Task? control, int initMask) { } } // enter + int tempStatus; if ((initMask & TaskOverrides.MASK_ENTER) != 0) { - Enter(reentryId); // enter可能导致结束和取消信号,也可能修改ctl - if (reentryId != this.reentryId) { - return; + tempStatus = Enter(); // enter可能导致结束和取消信号,也可能修改ctl + if (TaskStatus.IsCompleted(tempStatus)) { + return SetCompleted(tempStatus); } #if !TASK_MANUAL_CHECK_CANCEL if (cancelToken.IsCancelRequested && IsAutoCheckCancel) { - SetCompleted(TaskStatus.CANCELLED, false); - return; + return SetCompleted(TaskStatus.CANCELLED); } #endif } if (CheckSlowStart(ctl)) { // 需要使用最新的ctl - if ((initMask & MASK_DISABLE_NOTIFY) == 0 && control != null) { - control.OnChildRunning(this); - } - return; + return TaskStatus.RUNNING; } // execute - Execute(); - if (reentryId != this.reentryId) { - return; + tempStatus = Execute(); + if (TaskStatus.IsCompleted(tempStatus)) { + return SetCompleted(tempStatus); } #if !TASK_MANUAL_CHECK_CANCEL if (cancelToken.IsCancelRequested && IsAutoCheckCancel) { - SetCompleted(TaskStatus.CANCELLED, false); - return; + return SetCompleted(TaskStatus.CANCELLED); } #endif - if ((initMask & MASK_DISABLE_NOTIFY) == 0 && control != null) { - control.OnChildRunning(this); - } + return TaskStatus.RUNNING; } /// @@ -858,29 +815,34 @@ internal void Template_Start(Task? control, int initMask) { /// (通过参数的方式,有助于我们统一代码,也简化子类实现;同时避免遗漏) /// /// 是否由父节点调用 + /// 最新状态 [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Template_Execute(bool fromControl) { + public int Template_Execute(bool fromControl) { Debug.Assert(status == TaskStatus.RUNNING); - // 事件驱动下无法精确判断是否是心跳走到这里,因此只要处于非激活状态就拒绝父节点请求(装死) + // 处理被内联Task的结果 + if (delayedStatus > TaskStatus.RUNNING) { + return SetCompleted(delayedStatus); + } + // 心跳驱动框架下,fromControl就一定是心跳 if (fromControl && (ctl & MASK_NOT_ACTIVE_IN_HIERARCHY) != 0) { - return; + return TaskStatus.RUNNING; } + #if !TASK_MANUAL_CHECK_CANCEL if (cancelToken.IsCancelRequested && IsAutoCheckCancel) { - SetCompleted(TaskStatus.CANCELLED, false); - return; + return SetCompleted(TaskStatus.CANCELLED); } - int reentryId = this.reentryId; - Execute(); - if (reentryId != this.reentryId) { - return; +#endif + int tempStatus = Execute(); + if (TaskStatus.IsCompleted(tempStatus)) { + return SetCompleted(tempStatus); } +#if !TASK_MANUAL_CHECK_CANCEL if (cancelToken.IsCancelRequested && IsAutoCheckCancel) { - SetCompleted(TaskStatus.CANCELLED, false); + return SetCompleted(TaskStatus.CANCELLED); } -#else - Execute(); #endif + return TaskStatus.RUNNING; } /// @@ -888,53 +850,69 @@ public void Template_Execute(bool fromControl) { /// /// 存储被内联子节点的对象 /// 被内联前的Task + /// source的最新状态!source的最新状态!source的最新状态! [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Template_ExecuteInlined(ref TaskInlineHelper helper, Task source) { - Debug.Assert(status == TaskStatus.RUNNING && this != source); + public int Template_ExecuteInlined(ref TaskInlineHelper helper, Task source) { + Debug.Assert(status == TaskStatus.RUNNING && this != source && delayedStatus == 0); if ((ctl & MASK_NOT_ACTIVE_IN_HIERARCHY) != 0) { - return; + return TaskStatus.RUNNING; } - // 理论上这里可以先检查一下source的取消令牌,但如果source收到取消信号,则被内联的节点的子节点也一定收到取消信号 - int sourceReentryId = source.reentryId; - int reentryId = this.reentryId; - // 内联template_execute逻辑 + int tempStatus; { #if !TASK_MANUAL_CHECK_CANCEL if (cancelToken.IsCancelRequested && IsAutoCheckCancel) { - SetCompleted(TaskStatus.CANCELLED, false); + tempStatus = TaskStatus.CANCELLED; goto outer; } - Execute(); - if (reentryId != this.reentryId) { +#endif + tempStatus = Execute(); + if (TaskStatus.IsCompleted(tempStatus)) { goto outer; } +#if !TASK_MANUAL_CHECK_CANCEL if (cancelToken.IsCancelRequested && IsAutoCheckCancel) { - SetCompleted(TaskStatus.CANCELLED, false); + tempStatus = TaskStatus.CANCELLED; } -#else - Execute(); #endif } outer: { - // 如果被内联子节点退出,而源任务未退出,则重新内联(source可能自身进行了新的内联) - if (reentryId != this.reentryId && sourceReentryId == source.reentryId && helper.GetInlinedChild() == null) { - helper.InlineChild(source); + if (tempStatus == TaskStatus.RUNNING) { + return TaskStatus.RUNNING; } + // 如果被内联的子节点执行完毕,则延迟完成 + Debug.Assert(TaskStatus.IsCompleted(tempStatus)); + delayedStatus = tempStatus; + ProcessInlinedResult(this, ref helper, source); } - } + return source.status; + } + + /** 声明为静态方法,避免手滑访问到this属性 */ + private static void ProcessInlinedResult(Task inlined, ref TaskInlineHelper helper, Task source) { + // 在内联执行的情况下,进入完成状态时先暂存结果,也不调用Exit,然后从source开始向下Execute处理结果 + // 如果直接调用 source.Template_Execute(true),会产生较深的调用栈 + // 按照我们对可内联节点的要求,我们可以反转这个过程,从而转换为普通循环,这是使心跳驱动版本超越事件驱动版本的关键 + Task tempControl = inlined.control; + do { + int tempStatus = tempControl.Execute(); // 直接调用Execute + if (tempStatus == TaskStatus.RUNNING) { // 父节点不能进入完成状态 + break; + } + Debug.Assert(TaskStatus.IsCompleted(tempStatus)); + tempControl.delayedStatus = tempStatus; // 父节点的结果也暂存,再由其父节点处理 - private void Template_Exit() { - if ((ctl & MASK_REGISTERED_LISTENER) != 0) { - cancelToken.RemListener(this); - } - exitFrame = taskEntry.CurFrame; - StopRunningChildren(); // 停止有特殊顺序,不能使用访问器 - if ((ctl & TaskOverrides.MASK_EXIT) != 0) { - Exit(); - } - ReleaseContext(); - reentryId++; + if (tempControl == source) { + helper.StopInline(); // help gc + source.SetCompleted(tempStatus); // 如果source也进入完成状态,则直接完成 + return; + } + + tempControl = tempControl.control; + } while (true); + + // 如果被内联的子节点进入了完成状态,但source没有进入完成状态,则source需要重新内联 + helper.InlineChild(source); } /// @@ -943,32 +921,34 @@ private void Template_Exit() { /// 普通子节点,或需要接收通知的钩子任务 /// 是否检查子节点的前置条件 [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Template_StartChild(Task child, bool checkGuard) { + private int Template_StartChild(Task child, bool checkGuard) { if (!checkGuard || child.guard == null || Template_CheckGuard(child.guard)) { int initMask = (ctl & MASK_CHECKING_GUARD) == 0 ? 0 : MASK_GUARD_BASE_OPTIONS; - child.Template_Start(this, initMask); + return child.Template_Start(this, initMask); } else { child.SetGuardFailed(this, false); + return child.status; } } /// - /// 启动钩子节点 - /// 1.钩子任务不会触发 - /// 2.前置条件其实是特殊的钩子任务 - /// 3.条件分支通常不应该有钩子任务 + /// 启动子节点 /// - /// 钩子任务,或不需要接收事件通知的子节点 + /// 普通子节点,或需要接收通知的钩子任务 /// 是否检查子节点的前置条件 + /// 内联工具类 [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Template_StartHook(Task hook, bool checkGuard) { - if (!checkGuard || hook.guard == null || Template_CheckGuard(hook.guard)) { - int initMask = (ctl & MASK_CHECKING_GUARD) == 0 - ? MASK_DISABLE_NOTIFY - : MASK_DISABLE_NOTIFY | MASK_GUARD_BASE_OPTIONS; - hook.Template_Start(this, initMask); + public int Template_StartChild(Task child, bool checkGuard, ref TaskInlineHelper inlineHelper) { + if (!checkGuard || child.guard == null || Template_CheckGuard(child.guard)) { + int initMask = (ctl & MASK_CHECKING_GUARD) == 0 ? 0 : MASK_GUARD_BASE_OPTIONS; + int childStatus = child.Template_Start(this, initMask); + if (childStatus == TaskStatus.RUNNING) { + inlineHelper.InlineChild(child); + } + return childStatus; } else { - hook.SetGuardFailed(this, true); + child.SetGuardFailed(this, false); + return child.status; } } @@ -993,8 +973,7 @@ public bool Template_CheckGuard(Task? guard) { try { // 极少情况下会有前置的前置,更推荐组合节点,更清晰;guard的guard也是检测当前上下文 if (guard.guard != null && !Template_CheckGuard(guard.guard)) { - guard.ctl |= MASK_DISABLE_NOTIFY; - guard.SetCompleted(inverted ? TaskStatus.SUCCESS : TaskStatus.GUARD_FAILED, false); + guard.SetCompleted(inverted ? TaskStatus.SUCCESS : TaskStatus.GUARD_FAILED); return inverted; } guard.Template_Start(this, MASK_DISABLE_NOTIFY | MASK_GUARD_BASE_OPTIONS); diff --git a/Wjybxx.BTree.Core/src/TaskAwaiter.cs b/Wjybxx.BTree.Core/src/TaskAwaiter.cs deleted file mode 100644 index 38044c0..0000000 --- a/Wjybxx.BTree.Core/src/TaskAwaiter.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using Wjybxx.Commons; - -namespace Wjybxx.BTree -{ -/// -/// C# await语法支持 -/// -/// -public readonly struct TaskAwaiter : ICriticalNotifyCompletion where T : class -{ - private readonly TaskEntry taskEntry; - private readonly int reentryId; - - public TaskAwaiter(TaskEntry taskEntry) { - this.taskEntry = taskEntry; - this.reentryId = this.taskEntry.ReentryId; - } - - // 1.IsCompleted - public bool IsCompleted => taskEntry.IsCompleted; - - // 2. GetResult - // TaskEntry不抛出一次 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void GetResult() { - if (reentryId != taskEntry.ReentryId) { - throw new IllegalStateException(); - } - } - - // 3. OnCompleted - /// - /// 添加一个Future完成时的回调。 - /// ps:通常而言,该接口由StateMachine调用,因此接口参数为。 - /// - /// 回调任务 - public void OnCompleted(Action continuation) { - if (taskEntry.Handler == null) { - throw new IllegalStateException(); - } - taskEntry.Handler.AwaitOnCompleted(taskEntry, continuation); - } - - public void UnsafeOnCompleted(Action continuation) { - if (taskEntry.Handler == null) { - throw new IllegalStateException(); - } - taskEntry.Handler.AwaitOnCompleted(taskEntry, continuation); - } -} -} \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/TaskEntry.cs b/Wjybxx.BTree.Core/src/TaskEntry.cs index b217b46..d9aff39 100644 --- a/Wjybxx.BTree.Core/src/TaskEntry.cs +++ b/Wjybxx.BTree.Core/src/TaskEntry.cs @@ -18,7 +18,6 @@ using System; using System.Diagnostics; -using Wjybxx.BTree.FSM; using Wjybxx.Commons; namespace Wjybxx.BTree @@ -50,8 +49,6 @@ public class TaskEntry : Task where T : class [NonSerialized] protected ITreeLoader treeLoader; /** 当前帧号 */ [NonSerialized] private int curFrame; - /** 用于Entry的事件驱动 */ - [NonSerialized] protected ITaskEntryHandler? handler; /** 用于内联优化 */ [NonSerialized] protected TaskInlineHelper inlineHelper = new TaskInlineHelper(); @@ -93,11 +90,6 @@ public ITreeLoader TreeLoader { set => treeLoader = value ?? ITreeLoader.NullLoader(); } - public ITaskEntryHandler? Handler { - get => handler; - set => handler = value; - } - public new object? Entity { get => entity; set => entity = value; @@ -109,25 +101,6 @@ public ITaskEntryHandler? Handler { #region logic - /// - /// C# await语法支持 - /// - /// - public TaskAwaiter GetAwaiter() => new TaskAwaiter(this); - - /// - /// 获取根状态机 - /// 状态机太重要了,值得我们为其提供各种快捷方法 - /// - /// - /// - public StateMachineTask GetRootStateMachine() { - if (rootTask is StateMachineTask stateMachine) { - return stateMachine; - } - throw new IllegalStateException("rootTask is not state machine task"); - } - /// /// 普通Update /// @@ -142,28 +115,6 @@ public void Update(int curFrame) { } } - /// - /// 以内联的方式Update。 - /// 一般情况下,TaskEntry除了驱动root节点运行外,便没有额外逻辑,因此以内联的方式运行可省一些不必要的调用栈。 - /// - /// 当前帧号 - public void UpdateInlined(int curFrame) { - this.curFrame = curFrame; - if (IsRunning) { - Task? inlinedChild = inlineHelper.GetInlinedChild(); - if (inlinedChild != null) { - inlinedChild.Template_ExecuteInlined(ref inlineHelper, rootTask!); - } else if (rootTask!.IsRunning) { - rootTask.Template_Execute(true); - } else { - Template_StartChild(rootTask, true); - } - } else { - Debug.Assert(IsInited()); - Template_Start(null, 0); - } - } - /** 如果行为树代表的是一个条件树,则可以调用该方法;失败的情况下可以通过Status获取错误码 */ public bool Test() { Debug.Assert(IsInited()); @@ -171,15 +122,16 @@ public bool Test() { return IsSucceeded; } - protected override void Execute() { + protected override int Execute() { Task? inlinedChild = inlineHelper.GetInlinedChild(); if (inlinedChild != null) { inlinedChild.Template_ExecuteInlined(ref inlineHelper, rootTask!); } else if (rootTask!.IsRunning) { rootTask.Template_Execute(true); } else { - Template_StartChild(rootTask, true); + Template_StartChild(rootTask, true, ref inlineHelper); } + return rootTask.Status; } protected override void Exit() { @@ -187,19 +139,6 @@ protected override void Exit() { cancelToken.Reset(); // 避免内存泄漏 } - protected override void OnChildRunning(Task child) { - inlineHelper.InlineChild(child); - } - - protected override void OnChildCompleted(Task child) { - inlineHelper.StopInline(); - - SetCompleted(child.Status, true); - if (handler != null) { - handler.OnCompleted(this); - } - } - public override bool CanHandleEvent(object eventObj) { return blackboard != null && rootTask != null; // 只测isInited的关键属性即可 } diff --git a/Wjybxx.BTree.Core/src/TaskInlineHelper.cs b/Wjybxx.BTree.Core/src/TaskInlineHelper.cs index 47c5167..84fe769 100644 --- a/Wjybxx.BTree.Core/src/TaskInlineHelper.cs +++ b/Wjybxx.BTree.Core/src/TaskInlineHelper.cs @@ -37,9 +37,8 @@ public static class TaskInlineHelper /// /// 内联工具类 /// 1.只有不能被内联的节点,才需要该工具类。 -/// 2.实现内联优化时,应当在时开启内联和时停止内联。 -/// 3.在时也调用一次停止内联可避免内存泄漏(不必要的引用)。 -/// 4.在时应当尝试将事件转发给被内联的子节点,可使用工具方法。 +/// 2.在时也调用一次停止内联可避免内存泄漏(不必要的引用)。 +/// 3.在时应当尝试将事件转发给被内联的子节点,可使用工具方法。 /// /// ps:就是标准实现。 /// diff --git a/Wjybxx.BTree.Core/src/TaskLogger.cs b/Wjybxx.BTree.Core/src/TaskLogger.cs index 91cf5b8..f4cf37d 100644 --- a/Wjybxx.BTree.Core/src/TaskLogger.cs +++ b/Wjybxx.BTree.Core/src/TaskLogger.cs @@ -17,7 +17,10 @@ #endregion using System; -using Wjybxx.Commons.Logger; + +#if UNITY_2018_4_OR_NEWER +using UnityEngine; +#endif namespace Wjybxx.BTree { @@ -26,22 +29,50 @@ namespace Wjybxx.BTree /// public static class TaskLogger { - private static readonly ILogger logger = LoggerFactory.GetLogger(typeof(TaskLogger)); - public static void Info(string format, params object?[] args) { - logger.Info(format, args); +#if UNITY_2018_4_OR_NEWER + Debug.LogFormat(format, args); +#else + Console.WriteLine(format, args); +#endif } public static void Info(Exception? ex, string format, params object?[] args) { - logger.Info(ex, format, args); +#if UNITY_2018_4_OR_NEWER + Debug.LogFormat(format, args); + if (ex != null) + { + Debug.LogException(ex); + } +#else + Console.WriteLine(format, args); + if (ex != null) { + Console.WriteLine(ex); + } +#endif } public static void Warning(string format, params object?[] args) { - logger.Warn(format, args); +#if UNITY_2018_4_OR_NEWER + Debug.LogWarningFormat(format, args); +#else + Console.WriteLine(format, args); +#endif } public static void Warning(Exception? ex, string format, params object?[] args) { - logger.Warn(ex, format, args); +#if UNITY_2018_4_OR_NEWER + Debug.LogWarningFormat(format, args); + if (ex != null) + { + Debug.LogException(ex); + } +#else + Console.WriteLine(format, args); + if (ex != null) { + Console.WriteLine(ex); + } +#endif } } } \ No newline at end of file diff --git a/Wjybxx.BTree.Core/src/TaskOverrides.cs b/Wjybxx.BTree.Core/src/TaskOverrides.cs index 2bd60ca..13307f9 100644 --- a/Wjybxx.BTree.Core/src/TaskOverrides.cs +++ b/Wjybxx.BTree.Core/src/TaskOverrides.cs @@ -58,7 +58,7 @@ internal static int MaskOfTask(Type clazz) { if (IsSkippable(clazz, "BeforeEnter")) { mask &= ~MASK_BEFORE_ENTER; } - if (IsSkippable(clazz, "Enter", TYPE_INT32)) { + if (IsSkippable(clazz, "Enter")) { mask &= ~MASK_ENTER; } if (IsSkippable(clazz, "Exit")) { diff --git a/Wjybxx.BTree.Tests/Wjybxx.BTree.Tests.csproj b/Wjybxx.BTree.Tests/Wjybxx.BTree.Tests.csproj new file mode 100644 index 0000000..4372187 --- /dev/null +++ b/Wjybxx.BTree.Tests/Wjybxx.BTree.Tests.csproj @@ -0,0 +1,29 @@ + + + + net7.0 + disable + annotations + 11 + + BTree.Tests + BTree.Tests + + false + true + BTree.Tests + + + + + + + + + + + + + + + diff --git a/Wjybxx.BTree.Tests/src/ActiveTest.cs b/Wjybxx.BTree.Tests/src/ActiveTest.cs index 15ecd1e..399eb44 100644 --- a/Wjybxx.BTree.Tests/src/ActiveTest.cs +++ b/Wjybxx.BTree.Tests/src/ActiveTest.cs @@ -17,25 +17,13 @@ #endregion using NUnit.Framework; -using NUnit.Framework.Internal; using Wjybxx.BTree; -using Wjybxx.BTree.FSM; using Wjybxx.BTree.Leaf; namespace BTree.Tests; public class ActiveTest { - private static TaskEntry newStateMachineTree() { - StateMachineTask stateMachineTask = new StateMachineTask(); - stateMachineTask.Name = "RootStateMachine"; - stateMachineTask.Handler = StateMachineHandlers.DefaultHandler(); - - TaskEntry taskEntry = BtreeTestUtil.newTaskEntry(); - taskEntry.RootTask = stateMachineTask; - return taskEntry; - } - /// /// waitframe本应该在第5帧完成,但我们暂停了其心跳,在第9帧后启用心跳,第10帧就完成 /// @@ -54,7 +42,7 @@ public void testWaitFrame() { }); Assert.AreEqual(10, taskEntry.RootTask.RunFrames); } - + /** 测试active为false的情况下在第9帧取消 */ [Test] public void testCancel() { diff --git a/Wjybxx.BTree.Tests/src/BtreeTestUtil.cs b/Wjybxx.BTree.Tests/src/BtreeTestUtil.cs index 3517671..f758d5a 100644 --- a/Wjybxx.BTree.Tests/src/BtreeTestUtil.cs +++ b/Wjybxx.BTree.Tests/src/BtreeTestUtil.cs @@ -40,12 +40,8 @@ public static TaskEntry newTaskEntry(Task root) { public static void untilCompleted(TaskEntry entry) where T : class { for (int idx = 0; idx < 200; idx++) { // 避免死循环 - if (MathCommon.IsEven(idx)) { - entry.Update(idx); - } else { - entry.UpdateInlined(idx); - } - if (entry.IsCompleted) return; + entry.Update(idx); + if (entry.IsCompleted)return; } throw new InfiniteLoopException(); } diff --git a/Wjybxx.BTree.Tests/src/DecoratorTest.cs b/Wjybxx.BTree.Tests/src/DecoratorTest.cs index d7c98d7..bcb4cc7 100644 --- a/Wjybxx.BTree.Tests/src/DecoratorTest.cs +++ b/Wjybxx.BTree.Tests/src/DecoratorTest.cs @@ -49,16 +49,16 @@ public CountRandom(bool isGuard) { this.isGuard = isGuard; } - protected override void Execute() { + protected override int Execute() { if (!isGuard && random.Next(2) == 1) { // 随机等待 - return; + return TaskStatus.RUNNING; } if (random.Next(2) == 1) { successCount++; - SetSuccess(); + return TaskStatus.SUCCESS; } else { failedCount++; - SetFailed(TaskStatus.ERROR); + return TaskStatus.ERROR; } } diff --git a/Wjybxx.BTree.Tests/src/InlineTest.cs b/Wjybxx.BTree.Tests/src/InlineTest.cs index b5393de..98da492 100644 --- a/Wjybxx.BTree.Tests/src/InlineTest.cs +++ b/Wjybxx.BTree.Tests/src/InlineTest.cs @@ -61,10 +61,11 @@ private class EventAcceptor : LeafTask { internal object? eventObj; - protected override void Execute() { + protected override int Execute() { if (RunFrames >= 10) { - SetSuccess(); + return TaskStatus.SUCCESS; } + return TaskStatus.RUNNING; } protected override void OnEventImpl(object eventObj) { diff --git a/Wjybxx.BTree.Tests/src/JoinTest.cs b/Wjybxx.BTree.Tests/src/JoinTest.cs index 71d1b98..13ee967 100644 --- a/Wjybxx.BTree.Tests/src/JoinTest.cs +++ b/Wjybxx.BTree.Tests/src/JoinTest.cs @@ -44,13 +44,12 @@ public void setUp() { } // region - private class Counter : ActionTask where T : class + private class Counter : LeafTask where T : class { - protected override int ExecuteImpl() { + protected override int Execute() { // 不能过于简单成功,否则无法覆盖所有情况 if (BtreeTestUtil.random.Next(2) == 1) { globalCount++; - SetSuccess(); return TaskStatus.SUCCESS; } return TaskStatus.RUNNING; diff --git a/Wjybxx.BTree.Tests/src/LeafTest.cs b/Wjybxx.BTree.Tests/src/LeafTest.cs index d4c3c7f..485b14e 100644 --- a/Wjybxx.BTree.Tests/src/LeafTest.cs +++ b/Wjybxx.BTree.Tests/src/LeafTest.cs @@ -64,11 +64,11 @@ public void testStillborn() { Assert.AreEqual(0, waitFrame.PrevStatus); } - private class PrevStatusTask : ActionTask where T : class + private class PrevStatusTask : LeafTask where T : class { private int next = TaskStatus.SUCCESS; - protected override int ExecuteImpl() { + protected override int Execute() { if (next == TaskStatus.GUARD_FAILED) { next++; } diff --git a/Wjybxx.BTree.Tests/src/SingleRunningTest1.cs b/Wjybxx.BTree.Tests/src/SingleRunningTest1.cs index 2549dbe..0a5fd1c 100644 --- a/Wjybxx.BTree.Tests/src/SingleRunningTest1.cs +++ b/Wjybxx.BTree.Tests/src/SingleRunningTest1.cs @@ -171,11 +171,11 @@ public FailAtFrame(int frame) { this.frame = frame; } - protected override void Execute() { + protected override int Execute() { if (taskEntry.CurFrame >= frame) { - SetFailed(TaskStatus.ERROR); + return TaskStatus.ERROR; } else { - SetSuccess(); + return TaskStatus.SUCCESS; } } diff --git a/Wjybxx.BTree.Tests/src/StateMachineTest.cs b/Wjybxx.BTree.Tests/src/StateMachineTest.cs deleted file mode 100644 index c72d859..0000000 --- a/Wjybxx.BTree.Tests/src/StateMachineTest.cs +++ /dev/null @@ -1,366 +0,0 @@ -#region LICENSE - -// Copyright 2023-2024 wjybxx(845740757@qq.com) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// 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. - -#endregion - -using NUnit.Framework; -using Wjybxx.BTree; -using Wjybxx.BTree.FSM; -using Wjybxx.BTree.Leaf; -using Wjybxx.Commons; - -namespace BTree.Tests; - -public class StateMachineTest -{ - private static int global_count = 0; - private static bool delayChange = false; - private const int queue_size = 5; - - [SetUp] - public void setUp() { - global_count = 0; - delayChange = false; - } - - private static TaskEntry newStateMachineTree() { - StackStateMachineTask stateMachineTask = new StackStateMachineTask(); - stateMachineTask.Name = ("RootStateMachine"); - stateMachineTask.SetUndoQueueCapacity(queue_size); - stateMachineTask.SetRedoQueueCapacity(queue_size); - stateMachineTask.Handler = StateMachineHandlers.DefaultHandler(); - - TaskEntry taskEntry = BtreeTestUtil.newTaskEntry(); - taskEntry.RootTask = stateMachineTask; - return taskEntry; - } - - #region reentry - - /** 不延迟的情况下,三个任务都会进入被取消状态 */ - [Test] - public void testCount() { - TaskEntry taskEntry = newStateMachineTree(); - taskEntry.GetRootStateMachine().Handler = StateMachineHandlers.OfListener( - (stateMachineTask, curState, nextState) => { - if (curState == null) return; // 首次切换 - Assert.IsTrue(curState.IsCancelled); - }); - - taskEntry.GetRootStateMachine().ChangeState(new StateA()); - BtreeTestUtil.untilCompleted(taskEntry); - Assert.AreEqual(3, global_count); - } - - /** 延迟到当前状态退出后切换,三个任务都会进入成功完成状态 */ - [Test] - public void testCountDelay() { - delayChange = true; - TaskEntry taskEntry = newStateMachineTree(); - taskEntry.GetRootStateMachine().Handler = StateMachineHandlers.OfListener( - (stateMachineTask, curState, nextState) => { - if (curState == null) return; // 首次切换 - Assert.IsTrue(curState.IsSucceeded); - }); - - taskEntry.GetRootStateMachine().ChangeState(new StateA()); - BtreeTestUtil.untilCompleted(taskEntry); - Assert.AreEqual(3, global_count); - } - - /** 测试同一个状态重入 */ - [Test] - public void testReentry() { - TaskEntry taskEntry = newStateMachineTree(); - StateA stateA = new StateA(); - StateB stateB = new StateB(); - stateA.nextState = stateB; - stateB.nextState = stateA; - taskEntry.GetRootStateMachine().ChangeState(stateA); - - BtreeTestUtil.untilCompleted(taskEntry); - Assert.AreEqual(3, global_count); - } - - private class StateA : ActionTask where T : class - { - internal Task? nextState; - - protected override int ExecuteImpl() { - if (global_count++ == 0) { - if (nextState == null) { - nextState = new StateB(); - } - ChangeStateArgs args = delayChange ? ChangeStateArgs.PLAIN_WHEN_COMPLETED : ChangeStateArgs.PLAIN; - StateMachineTask.FindStateMachine(this).ChangeState(nextState, args); - } - return TaskStatus.SUCCESS; - } - - protected override void OnEventImpl(object eventObj) { - } - } - - private class StateB : ActionTask where T : class - { - internal Task? nextState; - - protected override int ExecuteImpl() { - if (global_count++ == 1) { - if (nextState == null) { - nextState = new StateA(); - } - ChangeStateArgs args = delayChange ? ChangeStateArgs.PLAIN_WHEN_COMPLETED : ChangeStateArgs.PLAIN; - StateMachineTask.FindStateMachine(this).ChangeState(nextState, args); - } - return TaskStatus.SUCCESS; - } - - protected override void OnEventImpl(object eventObj) { - } - } - - #endregion - - #region redo/undo - - /** redo,计数从 0 加到 5 */ - [Test] - public void testRedo() { - TaskEntry taskEntry = newStateMachineTree(); - StackStateMachineTask stateMachine = (StackStateMachineTask)taskEntry.RootTask; - fillRedoQueue(stateMachine); - - stateMachine.Handler = StateMachineHandlers.RedoHandler(); - stateMachine.RedoChangeState(); // 初始化 - - BtreeTestUtil.untilCompleted(taskEntry); - Assert.AreEqual(queue_size, global_count); - } - - /** undo,计数从 5 减到 0 */ - [Test] - public void testUndo() { - global_count = queue_size; - TaskEntry taskEntry = newStateMachineTree(); - StackStateMachineTask stateMachine = (StackStateMachineTask)taskEntry.RootTask; - fillUndoQueue(stateMachine); - - stateMachine.Handler = StateMachineHandlers.UndoHandler(); - stateMachine.UndoChangeState(); // 初始化 - - BtreeTestUtil.untilCompleted(taskEntry); - Assert.AreEqual(0, global_count); - } - - /** redo再undo,计数从0加到5,再减回0 */ - [Test] - public void testRedoUndo() { - TaskEntry taskEntry = newStateMachineTree(); - StackStateMachineTask stateMachine = (StackStateMachineTask)taskEntry.RootTask; - fillRedoQueue(stateMachine); - - stateMachine.Handler = new RedoUndoHandler(); - stateMachine.RedoChangeState(); // 初始化 - - BtreeTestUtil.untilCompleted(taskEntry); - Assert.AreEqual(0, global_count); - } - - private static void fillRedoQueue(StackStateMachineTask stateMachine) where T : class { - stateMachine.AddRedoState(new RedoState(4)); // redo是栈结构 - stateMachine.AddRedoState(new RedoState(3)); - stateMachine.AddRedoState(new RedoState(2)); - stateMachine.AddRedoState(new RedoState(1)); - stateMachine.AddRedoState(new RedoState(0)); - } - - internal static void fillUndoQueue(StackStateMachineTask stateMachine) where T : class { - stateMachine.AddUndoState(new UndoState(1)); // undo也栈结构 - stateMachine.AddUndoState(new UndoState(2)); - stateMachine.AddUndoState(new UndoState(3)); - stateMachine.AddUndoState(new UndoState(4)); - stateMachine.AddUndoState(new UndoState(5)); - } - - private class RedoUndoHandler : IStateMachineHandler where T : class - { - private bool redoFinished; - - public bool OnNextStateAbsent(StateMachineTask stateMachineTask, Task preState) { - if (!redoFinished) { - if (stateMachineTask.RedoChangeState()) { - return true; - } - Assert.AreEqual(queue_size, global_count); - fillUndoQueue((StackStateMachineTask)stateMachineTask); - redoFinished = true; - } - if (stateMachineTask.UndoChangeState()) { - return true; - } - stateMachineTask.SetSuccess(); - return true; - } - } - - private class UndoState : ActionTask where T : class - { - readonly int expected; - - internal UndoState(int expected) { - this.expected = expected; - } - - protected override int ExecuteImpl() { - if (BtreeTestUtil.random.Next(2) == 1) { - return TaskStatus.RUNNING; // 随机等待 - } - if (global_count == expected) { - global_count--; - return TaskStatus.SUCCESS; - } - return TaskStatus.ERROR; - } - - protected override void OnEventImpl(object eventObj) { - } - } - - private class RedoState : ActionTask where T : class - { - readonly int expected; - - internal RedoState(int expected) { - this.expected = expected; - } - - protected override int ExecuteImpl() { - if (BtreeTestUtil.random.Next(2) == 1) { - return TaskStatus.RUNNING; // 随机等待 - } - if (global_count == expected) { - global_count++; - return TaskStatus.SUCCESS; - } - return TaskStatus.ERROR; - } - - protected override void OnEventImpl(object eventObj) { - } - } - - #endregion - - #region 传统状态机样式 - - [Test] - public void testDelayExecute() { - TaskEntry taskEntry = newStateMachineTree(); - ClassicalState nextState = new ClassicalState(); - taskEntry.GetRootStateMachine().ChangeState(nextState); - BtreeTestUtil.untilCompleted(taskEntry); - - Assert.IsTrue(nextState.IsSucceeded); - Assert.IsTrue(taskEntry.IsSucceeded); - } - - [Test] - public void testFireEvent() { - TaskEntry taskEntry = newStateMachineTree(); - ClassicalState nextState = new ClassicalState(); - taskEntry.GetRootStateMachine().ChangeState(nextState); - - string message = "message"; - taskEntry.Update(0); // 先启动 - taskEntry.OnEvent(message); - Assert.AreEqual(message, nextState.message); - } - - /** 传统状态机下的状态;期望enter和execute分开执行 */ - class ClassicalState : LeafTask where T : class - { - public object? message; - - protected override void BeforeEnter() { - base.BeforeEnter(); - IsSlowStart = true; - } - - protected override void Execute() { - if (RunFrames != 1) { - throw new IllegalStateException(); - } - SetSuccess(); - } - - protected override void OnEventImpl(object eventObj) { - message = eventObj; - } - } - - #endregion - - #region ChangeState - - /// - /// 由于未指定delayArg,因此当前状态会进入取消状态 - /// - [Test] - public void testChangeStateTask() { - TaskEntry taskEntry = newStateMachineTree(); - ChangeStateTask stateTask = new ChangeStateTask(new Success()); - taskEntry.GetRootStateMachine().ChangeState(stateTask); - - BtreeTestUtil.untilCompleted(taskEntry); - Assert.IsTrue(stateTask.IsCancelled, "ChangeState task is cancelled? code: " + stateTask.Status); - } - - /// - /// 由于我们指定了DelayArg,因此会进入成功状态 - /// - [Test] - public void testChangeStateTask2() { - TaskEntry taskEntry = newStateMachineTree(); - ChangeStateTask stateTask = new ChangeStateTask(new Success()); - stateTask.DelayArg = TaskStatus.SUCCESS; - taskEntry.GetRootStateMachine().ChangeState(stateTask); - - BtreeTestUtil.untilCompleted(taskEntry); - Assert.IsTrue(stateTask.IsSucceeded, "ChangeState task is succeeded? code: " + stateTask.Status); - } - - [Test] - public void testDelay_currentCompleted() { - int runFrames = 10; - TaskEntry taskEntry = newStateMachineTree(); - StateMachineTask rootStateMachine = taskEntry.GetRootStateMachine(); - rootStateMachine.Handler = StateMachineHandlers.OfListener( - (stateMachineTask, curState, nextState) => { - if (curState != null && nextState != null) { - Assert.AreEqual(runFrames, curState.RunFrames); - } - } - ); - rootStateMachine.ChangeState(new WaitFrame(runFrames)); - taskEntry.Update(0); // 启动任务树,使行为树处于运行状态 - - rootStateMachine.ChangeState(new WaitFrame(1), ChangeStateArgs.PLAIN_WHEN_COMPLETED); - BtreeTestUtil.untilCompleted(taskEntry); - } - - # endregion -} \ No newline at end of file