From af0845f3843889912544666dda59b63c8614fed3 Mon Sep 17 00:00:00 2001 From: tomatosalat0 <101592647+tomatosalat0@users.noreply.github.com> Date: Thu, 24 Oct 2024 07:43:03 +0200 Subject: [PATCH] Made test of cancellation of engine execution more robust (#1984) --- .../Runtime/ExecutionConstraintTests.cs | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/Jint.Tests/Runtime/ExecutionConstraintTests.cs b/Jint.Tests/Runtime/ExecutionConstraintTests.cs index 31b45de3f..cc3ad802f 100644 --- a/Jint.Tests/Runtime/ExecutionConstraintTests.cs +++ b/Jint.Tests/Runtime/ExecutionConstraintTests.cs @@ -35,18 +35,38 @@ public void ShouldThrowExecutionCanceled() Assert.Throws( () => { - using (var tcs = new CancellationTokenSource()) + using (var cts = new CancellationTokenSource()) using (var waitHandle = new ManualResetEvent(false)) + using (var cancellationTokenSet = new ManualResetEvent(initialState: false)) { - var engine = new Engine(cfg => cfg.CancellationToken(tcs.Token)); - - ThreadPool.QueueUserWorkItem(state => + var engine = new Engine(cfg => cfg.CancellationToken(cts.Token)); + + /* + * To ensure that the action "threadPoolAction" has actually been called by the ThreadPool + * (which can happen very late, depending on the ThreadPool usage, etc.), the + * "cancellationTokenSet" event will be an indicator for that. + * + * 1. The "cancellationTokenSet" will be set after the "cts" has been cancelled. + * 2. Within the JS, the "test" code will only start as soon as the "cancellationTokenSet" + * event will be set. + * 3. The cancellationToken and its source has not been used on purpose for this test to + * not mix "test infrastructure" and "test code". + * 4. To verify that this test still works under heavy load, you can add + * a "Thread.Sleep(10000)" call anywhere within the "threadPoolAction" action. + */ + + WaitCallback threadPoolAction = _ => { waitHandle.WaitOne(); - tcs.Cancel(); - }); + cts.Cancel(); + cancellationTokenSet.Set(); + }; + ThreadPool.QueueUserWorkItem(threadPoolAction); engine.SetValue("waitHandle", waitHandle); + engine.SetValue("waitForTokenToBeSet", new Action(() => cancellationTokenSet.WaitOne())); + engine.SetValue("mustNotBeCalled", new Action(() => + throw new InvalidOperationException("A cancellation of the script execution has been requested, but the script did continue to run."))); engine.Evaluate(@" function sleep(millisecondsTimeout) { var totalMilliseconds = new Date().getTime() + millisecondsTimeout; @@ -56,11 +76,16 @@ function sleep(millisecondsTimeout) { sleep(100); waitHandle.Set(); + waitForTokenToBeSet(); + + // Now it is ensured that the cancellationToken has been cancelled. + // The following JS code execution should get aborted by the engine. sleep(1000); sleep(1000); sleep(1000); sleep(1000); sleep(1000); + mustNotBeCalled(); "); } } @@ -376,4 +401,4 @@ public void Log(string logMessage) Console.WriteLine(logMessage); } } -} \ No newline at end of file +}