From 72a93721f99734b2c4b014e5e913293d8cefecb5 Mon Sep 17 00:00:00 2001 From: PopSlime Date: Sat, 22 Apr 2023 21:16:17 +0800 Subject: [PATCH] Fix race condition for the shared evaluator when any active input handler is threaded. --- Assets/Cryville/Crtr/InputProxy.cs | 11 +++---- Assets/Cryville/Crtr/Judge.cs | 49 +++++++++++++++------------- Assets/Cryville/Crtr/NoteHandler.cs | 8 ++--- Assets/Cryville/Crtr/PdtEvaluator.cs | 43 ++++++++++++------------ 4 files changed, 57 insertions(+), 54 deletions(-) diff --git a/Assets/Cryville/Crtr/InputProxy.cs b/Assets/Cryville/Crtr/InputProxy.cs index 79cb6d9..c1aa065 100644 --- a/Assets/Cryville/Crtr/InputProxy.cs +++ b/Assets/Cryville/Crtr/InputProxy.cs @@ -18,7 +18,7 @@ namespace Cryville.Crtr { _vecsrcs[i] = vecsrc; _vecops[i] = new InputVectorOp(vecsrc); } - _etor = ChartPlayer.etor; + _etor = judge != null ? judge._etor : ChartPlayer.etor; _ruleset = ruleset; _judge = judge; foreach (var i in ruleset.inputs) { @@ -172,7 +172,6 @@ namespace Cryville.Crtr { } } - readonly object _lock = new object(); static readonly int _var_value = IdentifierManager.SharedInstance.Request("value"); const int MAX_DEPTH = 15; const int MAX_DIMENSION = 3; @@ -228,7 +227,7 @@ namespace Cryville.Crtr { readonly Dictionary _vecs = new Dictionary(); double? _lockTime = null; unsafe void OnInput(InputIdentifier id, InputVector vec) { - lock (_lock) { + lock (_etor) { InputProxyEntry proxy; if (_sproxies.TryGetValue(id.Source, out proxy)) { _etor.ContextCascadeInsert(); @@ -258,10 +257,10 @@ namespace Cryville.Crtr { _etor.ContextCascadeInsert(); bool newNullFlag = nullflag; if (!newNullFlag) { - ChartPlayer.etor.Evaluate(_vecops[depth + 1], p.Value); + _etor.Evaluate(_vecops[depth + 1], p.Value); newNullFlag = _vecsrcs[depth + 1].IsNull; - if (newNullFlag) ChartPlayer.etor.ContextCascadeUpdate(_var_value, PropSrc.Null); - else ChartPlayer.etor.ContextCascadeUpdate(_var_value, _vecsrcs[depth + 1]); + if (newNullFlag) _etor.ContextCascadeUpdate(_var_value, PropSrc.Null); + else _etor.ContextCascadeUpdate(_var_value, _vecsrcs[depth + 1]); } OnInput(id, p.Key, ft, tt, newNullFlag, depth + 1); _etor.ContextCascadeDiscard(); diff --git a/Assets/Cryville/Crtr/Judge.cs b/Assets/Cryville/Crtr/Judge.cs index 367b444..b57c2d3 100644 --- a/Assets/Cryville/Crtr/Judge.cs +++ b/Assets/Cryville/Crtr/Judge.cs @@ -13,7 +13,7 @@ namespace Cryville.Crtr { public class Judge { #region Data readonly ChartPlayer _sys; - readonly PdtEvaluator _etor; + internal readonly PdtEvaluator _etor; readonly PdtRuleset _rs; readonly Dictionary> evs = new Dictionary>(); @@ -38,7 +38,8 @@ namespace Cryville.Crtr { } public Judge(ChartPlayer sys, PdtRuleset rs) { _sys = sys; - _etor = ChartPlayer.etor; + _etor = new PdtEvaluator(); + _etor.ContextJudge = this; _rs = rs; _numsrc1 = new PropSrc.Float(() => _numbuf1); _numsrc2 = new PropSrc.Float(() => _numbuf2); @@ -67,8 +68,8 @@ namespace Cryville.Crtr { Identifier input = default(Identifier); Clip clip = default(Clip); var def = _rs.judges[tev.Id]; - ChartPlayer.etor.Evaluate(new PropOp.Identifier(v => input = new Identifier(v)), def.input); - ChartPlayer.etor.Evaluate(new PropOp.Clip(v => clip = v), def.clip); + _etor.Evaluate(new PropOp.Identifier(v => input = new Identifier(v)), def.input); + _etor.Evaluate(new PropOp.Clip(v => clip = v), def.clip); double st = sev.Time, et = st + sev.Duration; var list = evs[input]; var ev = new JudgeEvent { @@ -181,13 +182,15 @@ namespace Cryville.Crtr { return false; } public void Cleanup(Identifier target, float tt) { - Forward(target, tt); - var actlist = activeEvs[target]; - for (int i = actlist.Count - 1; i >= 0; i--) { - JudgeEvent ev = actlist[i]; - if (tt > ev.EndClip) { - actlist.RemoveAt(i); - if (ev.Definition.miss != null) Pass(ev, tt, ev.Definition.miss); + lock (_etor) { + Forward(target, tt); + var actlist = activeEvs[target]; + for (int i = actlist.Count - 1; i >= 0; i--) { + JudgeEvent ev = actlist[i]; + if (tt > ev.EndClip) { + actlist.RemoveAt(i); + if (ev.Definition.miss != null) Pass(ev, tt, ev.Definition.miss); + } } } } @@ -254,18 +257,20 @@ namespace Cryville.Crtr { return scoreSrcs.TryGetValue(key, out value); } public TargetString GetFullFormattedScoreString() { - bool flag = false; - scoreFullBuf.Clear(); - foreach (var s in scores) { - var id = s.Key; - scoreFullBuf.AppendFormat(flag ? "\n{0}: " : "{0}: ", (string)IdentifierManager.SharedInstance.Retrieve(id)); - scoreFullBuf.AppendFormat(scoreFormatCache[id], scores[id]); - flag = true; + lock (_etor) { + bool flag = false; + scoreFullBuf.Clear(); + foreach (var s in scores) { + var id = s.Key; + scoreFullBuf.AppendFormat(flag ? "\n{0}: " : "{0}: ", (string)IdentifierManager.SharedInstance.Retrieve(id)); + scoreFullBuf.AppendFormat(scoreFormatCache[id], scores[id]); + flag = true; + } + scoreFullStr.Length = scoreFullBuf.Count; + var arr = scoreFullStr.TrustedAsArray(); + scoreFullBuf.CopyTo(0, arr, 0, scoreFullBuf.Count); + return scoreFullStr; } - scoreFullStr.Length = scoreFullBuf.Count; - var arr = scoreFullStr.TrustedAsArray(); - scoreFullBuf.CopyTo(0, arr, 0, scoreFullBuf.Count); - return scoreFullStr; } class ScoreStringSrc : PropSrc { readonly Func _cb; diff --git a/Assets/Cryville/Crtr/NoteHandler.cs b/Assets/Cryville/Crtr/NoteHandler.cs index 6839a8c..bbef2b0 100644 --- a/Assets/Cryville/Crtr/NoteHandler.cs +++ b/Assets/Cryville/Crtr/NoteHandler.cs @@ -103,11 +103,11 @@ namespace Cryville.Crtr { if (ev == null) { } else if (ev.Unstamped == null) { } else if (ev.Unstamped is Chart.Judge) { - ChartPlayer.etor.ContextEvent = ev.Unstamped; - ChartPlayer.etor.ContextState = s; + judge._etor.ContextEvent = ev.Unstamped; + judge._etor.ContextState = s; judge.Prepare(ev, this); - ChartPlayer.etor.ContextState = null; - ChartPlayer.etor.ContextEvent = null; + judge._etor.ContextState = null; + judge._etor.ContextEvent = null; } } if (s.CloneType == 2 && ev != null && ev.Unstamped is Chart.Judge) { diff --git a/Assets/Cryville/Crtr/PdtEvaluator.cs b/Assets/Cryville/Crtr/PdtEvaluator.cs index 633c45f..db501ac 100644 --- a/Assets/Cryville/Crtr/PdtEvaluator.cs +++ b/Assets/Cryville/Crtr/PdtEvaluator.cs @@ -10,7 +10,7 @@ using UnityEngine; namespace Cryville.Crtr { public class PdtEvaluator : PdtEvaluatorBase { - static readonly Dictionary _shortops = new Dictionary(); + readonly Dictionary _shortops = new Dictionary(); readonly IntKeyedDictionary _ctxops = new IntKeyedDictionary(); static readonly byte[] _nullbuf = new byte[0]; @@ -151,27 +151,6 @@ namespace Cryville.Crtr { for (int i = 0; i < ContextCascade.Length; i++) ContextCascade[i] = new IntKeyedDictionary(); _vecsrc = new VectorSrc(() => _vec); - _ctxops.Add(IdentifierManager.SharedInstance.Request("screen_edge"), new func_screen_edge(() => ContextTransform)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("int"), new func_int(() => ContextSelfValue)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("clamp"), new func_clamp(() => ContextSelfValue)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("min"), new func_min(() => ContextSelfValue)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("max"), new func_max(() => ContextSelfValue)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("abs"), new func_abs(() => ContextSelfValue)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("anim"), new func_anim(() => ContextSelfValue)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("cubic_bezier"), new func_cubic_bezier(() => ContextSelfValue)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("ease"), new func_cubic_bezier_fixed(0.25f, 0.1f, 0.25f, 1f, () => ContextSelfValue)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("ease_in"), new func_cubic_bezier_fixed(0.42f, 0f, 1f, 1f, () => ContextSelfValue)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("ease_out"), new func_cubic_bezier_fixed(0f, 0f, 0.58f, 1f, () => ContextSelfValue)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("ease_in_out"), new func_cubic_bezier_fixed(0.42f, 0f, 0.58f, 1f, () => ContextSelfValue)); - - Func cccb = k => ContextCascadeLookup(k); - _ctxops.Add(IdentifierManager.SharedInstance.Request("attack_timing"), new func_attack_timing(cccb)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("enter_timing"), new func_enter_timing(cccb)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("release_timing"), new func_release_timing(cccb)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("leave_timing"), new func_leave_timing(cccb)); - _ctxops.Add(IdentifierManager.SharedInstance.Request("contact_timing"), new func_contact_timing(cccb)); - } - static PdtEvaluator() { _shortops.Add(new PdtOperatorSignature("@", 2), new op_at_2()); _shortops.Add(new PdtOperatorSignature("*", 2), new op_mul_2()); @@ -193,6 +172,26 @@ namespace Cryville.Crtr { _shortops.Add(new PdtOperatorSignature("in_area", 1), new func_in_area()); _shortops.Add(new PdtOperatorSignature("interval", 3), new func_interval()); _shortops.Add(new PdtOperatorSignature("is", 2), new func_is()); + + _ctxops.Add(IdentifierManager.SharedInstance.Request("screen_edge"), new func_screen_edge(() => ContextTransform)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("int"), new func_int(() => ContextSelfValue)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("clamp"), new func_clamp(() => ContextSelfValue)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("min"), new func_min(() => ContextSelfValue)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("max"), new func_max(() => ContextSelfValue)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("abs"), new func_abs(() => ContextSelfValue)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("anim"), new func_anim(() => ContextSelfValue)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("cubic_bezier"), new func_cubic_bezier(() => ContextSelfValue)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("ease"), new func_cubic_bezier_fixed(0.25f, 0.1f, 0.25f, 1f, () => ContextSelfValue)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("ease_in"), new func_cubic_bezier_fixed(0.42f, 0f, 1f, 1f, () => ContextSelfValue)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("ease_out"), new func_cubic_bezier_fixed(0f, 0f, 0.58f, 1f, () => ContextSelfValue)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("ease_in_out"), new func_cubic_bezier_fixed(0.42f, 0f, 0.58f, 1f, () => ContextSelfValue)); + + Func cccb = k => ContextCascadeLookup(k); + _ctxops.Add(IdentifierManager.SharedInstance.Request("attack_timing"), new func_attack_timing(cccb)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("enter_timing"), new func_enter_timing(cccb)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("release_timing"), new func_release_timing(cccb)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("leave_timing"), new func_leave_timing(cccb)); + _ctxops.Add(IdentifierManager.SharedInstance.Request("contact_timing"), new func_contact_timing(cccb)); } #region Operators #pragma warning disable IDE1006