perf(runtime): split trigger evals from the blocking pool; bounded, deadline-aware execution
This commit is contained in:
@@ -403,13 +403,15 @@ public class ScriptActorTests : TestKit, IDisposable
|
||||
"ExprFault", "Expression", "{\"expression\":\"true\",\"mode\":\"OnTrue\"}", null, expr);
|
||||
try
|
||||
{
|
||||
actor.Tell(Change("A", "1")); // eval starts on the scheduler and BLOCKS → _evalInFlight = true
|
||||
actor.Tell(Change("A", "1")); // eval starts off-thread and BLOCKS → _evalInFlight = true
|
||||
AwaitAssert(() => Assert.Equal(1, EvalGate.Entries), TimeSpan.FromSeconds(10));
|
||||
|
||||
// #18 seam: the blocked evaluation is running on THIS class's injected
|
||||
// scheduler — not the process-wide singleton — so a worker it strands can
|
||||
// never starve another test class.
|
||||
Assert.Equal(1, _scheduler.BusyThreadCount);
|
||||
// WP3.1: the blocked evaluation no longer occupies a script-execution worker at
|
||||
// all — it runs on the shared thread pool behind TriggerEvalGate, which is the
|
||||
// whole point of finding #4. The blocking pool must be completely idle here; the
|
||||
// pre-WP3.1 assertion was the opposite (BusyThreadCount == 1).
|
||||
Assert.Equal(0, _scheduler.BusyThreadCount);
|
||||
Assert.Equal(0, _scheduler.QueueDepth);
|
||||
|
||||
actor.Tell(Change("A", "2")); // coalesces → _evalPending = true
|
||||
|
||||
@@ -432,15 +434,24 @@ public class ScriptActorTests : TestKit, IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExpressionTrigger_EvaluatesOnScriptSchedulerThread_AndStillFires()
|
||||
public void ExpressionTrigger_EvaluatesOffTheBlockingScriptPool_AndStillFires()
|
||||
{
|
||||
// The expression is TRUE only when evaluated on a script-execution thread.
|
||||
// Before P1 it ran synchronously on the actor's dispatcher thread (name is
|
||||
// NOT "script-execution-*") → false → no fire. After P1 it runs on the
|
||||
// script scheduler → true → fire.
|
||||
// WP3.1 retarget of the former P1 assertion, whose sense is deliberately INVERTED.
|
||||
//
|
||||
// P1 moved trigger-expression evaluation off the actor's dispatcher thread and onto
|
||||
// the dedicated script-execution scheduler, and this test asserted exactly that
|
||||
// ("evaluated on a script-execution-* thread"). WP3.1 (finding #4) proved that
|
||||
// destination wrong: sharing the blocking pool meant N blocked script bodies stalled
|
||||
// every Expression trigger on the node indefinitely. Evaluations are non-blocking by
|
||||
// construction, so they now run as plain async work on the shared .NET thread pool
|
||||
// behind TriggerEvalGate.
|
||||
//
|
||||
// The expression is therefore TRUE only when evaluated OFF a script-execution thread —
|
||||
// still off the dispatcher (the P1 property, covered by the coalescing/PipeTo tests),
|
||||
// and now provably off the blocking pool too.
|
||||
var expr = CompileRawTriggerExpression(
|
||||
"System.Threading.Thread.CurrentThread.Name != null && " +
|
||||
"System.Threading.Thread.CurrentThread.Name.StartsWith(\"script-execution-\")");
|
||||
"System.Threading.Thread.CurrentThread.Name == null || " +
|
||||
"!System.Threading.Thread.CurrentThread.Name.StartsWith(\"script-execution-\")");
|
||||
var (actor, instance) = CreateTriggeredActor(
|
||||
"ExprThread",
|
||||
"Expression",
|
||||
@@ -449,7 +460,7 @@ public class ScriptActorTests : TestKit, IDisposable
|
||||
expr);
|
||||
|
||||
actor.Tell(Change("Any", "1"));
|
||||
instance.ExpectMsg<SetStaticAttributeCommand>(TimeSpan.FromSeconds(10)); // fired ⇒ evaluated off-dispatcher
|
||||
instance.ExpectMsg<SetStaticAttributeCommand>(TimeSpan.FromSeconds(10)); // fired ⇒ evaluated off the blocking pool
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user