Skip to content

Commit 9c9e021

Browse files
committed
✨ feat: Enhance V8 await mode
* Add `V8AwaitMode.RunNoWait` * Fix unexpected behavior of `V8AwaitMode.RunOnce`
1 parent b10c185 commit 9c9e021

File tree

5 files changed

+115
-9
lines changed

5 files changed

+115
-9
lines changed

cpp/jni/javet_enums.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ namespace Javet {
2828

2929
namespace V8AwaitMode {
3030
enum V8AwaitMode {
31-
RunOnce = 0,
32-
RunTillNoMoreTasks = 1,
31+
RunNoWait = 2,
32+
RunOnce = 1,
33+
RunTillNoMoreTasks = 0,
3334
};
3435
};
3536

cpp/jni/javet_v8_runtime.cpp

+16-3
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,19 @@ namespace Javet {
9797
bool V8Runtime::Await(const Javet::Enums::V8AwaitMode::V8AwaitMode awaitMode) noexcept {
9898
bool hasMoreTasks = false;
9999
#ifdef ENABLE_NODE
100+
uv_run_mode uvRunMode;
101+
switch (awaitMode)
102+
{
103+
case Javet::Enums::V8AwaitMode::V8AwaitMode::RunOnce:
104+
uvRunMode = UV_RUN_ONCE;
105+
break;
106+
case Javet::Enums::V8AwaitMode::V8AwaitMode::RunNoWait:
107+
uvRunMode = UV_RUN_NOWAIT;
108+
break;
109+
default:
110+
uvRunMode = UV_RUN_DEFAULT;
111+
break;
112+
}
100113
do {
101114
{
102115
// Reduce the locking granularity so that Node.js can respond to requests from other threads.
@@ -105,12 +118,12 @@ namespace Javet {
105118
V8HandleScope v8HandleScope(v8Isolate);
106119
auto v8Context = GetV8LocalContext();
107120
auto v8ContextScope = GetV8ContextScope(v8Context);
108-
uv_run(&uvLoop, UV_RUN_NOWAIT);
121+
uv_run(&uvLoop, uvRunMode);
109122
// DrainTasks is thread-safe.
110123
v8PlatformPointer->DrainTasks(v8Isolate);
111124
}
112125
hasMoreTasks = uv_loop_alive(&uvLoop);
113-
if (hasMoreTasks) {
126+
if (uvRunMode == UV_RUN_DEFAULT && hasMoreTasks) {
114127
// Sleep a while to give CPU cycles to other threads.
115128
std::this_thread::sleep_for(oneMillisecond);
116129
}
@@ -124,7 +137,7 @@ namespace Javet {
124137
node::EmitProcessBeforeExit(nodeEnvironment.get());
125138
hasMoreTasks = uv_loop_alive(&uvLoop);
126139
}
127-
} while (awaitMode == Javet::Enums::V8AwaitMode::RunTillNoMoreTasks && hasMoreTasks);
140+
} while (uvRunMode == UV_RUN_DEFAULT && hasMoreTasks);
128141
#else
129142
// It has to be v8::platform::MessageLoopBehavior::kDoNotWait, otherwise it blockes;
130143
v8::platform::PumpMessageLoop(v8PlatformPointer, v8Isolate);

docs/release_notes/release_notes_3_1.rst

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
Release Notes 3.1.x
33
===================
44

5+
3.1.2 V8 v12.5
6+
--------------
7+
8+
* Added ``V8AwaitMode.RunNoWait``
9+
* Fixed unexpected behavior of ``V8AwaitMode.RunOnce``
10+
511
3.1.1 V8 v12.4
612
--------------
713

src/main/java/com/caoccao/javet/enums/V8AwaitMode.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,27 @@
2323
*/
2424
public enum V8AwaitMode {
2525
/**
26-
* Run once tells Javet to drain the tasks once and return.
26+
* RunNoWait tells Javet to trigger the task queue execution but do not wait.
27+
* It only works in Node.js mode.
28+
*
29+
* @since 3.1.2
30+
*/
31+
RunNoWait(2),
32+
/**
33+
* RunOnce tells Javet to drain the tasks once and return.
2734
* It only works in Node.js mode.
2835
*
2936
* @since 2.0.4
3037
*/
31-
RunOnce(0),
38+
RunOnce(1),
3239
/**
33-
* Run till no more tasks tells Javet to keep waiting till there are no more tasks.
40+
* RunTillNoMoreTasks tells Javet to keep waiting till there are no more tasks.
41+
* This is the default mode.
3442
* It only works in Node.js mode.
3543
*
3644
* @since 2.0.4
3745
*/
38-
RunTillNoMoreTasks(1);
46+
RunTillNoMoreTasks(0);
3947

4048
private final int id;
4149

src/test/java/com/caoccao/javet/interop/TestNodeRuntime.java

+78
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818

1919
import com.caoccao.javet.BaseTestJavet;
2020
import com.caoccao.javet.enums.JSRuntimeType;
21+
import com.caoccao.javet.enums.V8AwaitMode;
2122
import com.caoccao.javet.exceptions.JavetError;
2223
import com.caoccao.javet.exceptions.JavetException;
24+
import com.caoccao.javet.interop.converters.JavetProxyConverter;
2325
import com.caoccao.javet.interop.options.NodeRuntimeOptions;
2426
import com.caoccao.javet.node.modules.NodeModuleAny;
2527
import com.caoccao.javet.node.modules.NodeModuleProcess;
@@ -35,6 +37,7 @@
3537
import java.nio.file.Path;
3638
import java.nio.file.StandardOpenOption;
3739
import java.text.MessageFormat;
40+
import java.util.concurrent.atomic.AtomicInteger;
3841

3942
import static org.junit.jupiter.api.Assertions.*;
4043

@@ -82,6 +85,81 @@ protected void internalTest(String fileName, String expectedJsonString) throws J
8285
}
8386
}
8487

88+
@Test
89+
public void testAwaitRunNoWait() {
90+
try {
91+
nodeRuntime.setConverter(new JavetProxyConverter());
92+
AtomicInteger counter = new AtomicInteger();
93+
nodeRuntime.getGlobalObject().set("counter", counter);
94+
nodeRuntime.getExecutor("let timeoutIDs = [];" +
95+
"function a() {\n" +
96+
" counter.incrementAndGet();\n" +
97+
" if (counter.get() < 10) {\n" +
98+
" timeoutIDs.push(setTimeout(a, 1000));\n" +
99+
" }\n" +
100+
"};\n" +
101+
"a();").executeVoid();
102+
assertTrue(nodeRuntime.await(V8AwaitMode.RunNoWait));
103+
nodeRuntime.getExecutor("timeoutIDs.forEach(id => clearTimeout(id));").executeVoid();
104+
assertEquals(1, counter.get());
105+
nodeRuntime.getGlobalObject().delete("counter");
106+
} catch (Throwable t) {
107+
fail(t);
108+
} finally {
109+
nodeRuntime.lowMemoryNotification();
110+
}
111+
}
112+
113+
@Test
114+
public void testAwaitRunOnce() {
115+
try {
116+
nodeRuntime.setConverter(new JavetProxyConverter());
117+
AtomicInteger counter = new AtomicInteger();
118+
nodeRuntime.getGlobalObject().set("counter", counter);
119+
nodeRuntime.getExecutor("let timeoutIDs = [];" +
120+
"function a() {\n" +
121+
" counter.incrementAndGet();\n" +
122+
" if (counter.get() < 10) {\n" +
123+
" timeoutIDs.push(setTimeout(a, 0));\n" +
124+
" }\n" +
125+
"};\n" +
126+
"a();").executeVoid();
127+
while (counter.get() < 2) {
128+
assertTrue(nodeRuntime.await(V8AwaitMode.RunOnce));
129+
}
130+
nodeRuntime.getExecutor("timeoutIDs.forEach(id => clearTimeout(id));").executeVoid();
131+
assertEquals(2, counter.get());
132+
nodeRuntime.getGlobalObject().delete("counter");
133+
} catch (Throwable t) {
134+
fail(t);
135+
} finally {
136+
nodeRuntime.lowMemoryNotification();
137+
}
138+
}
139+
140+
@Test
141+
public void testAwaitRunTillNoMoreTasks() {
142+
try {
143+
nodeRuntime.setConverter(new JavetProxyConverter());
144+
AtomicInteger counter = new AtomicInteger();
145+
nodeRuntime.getGlobalObject().set("counter", counter);
146+
nodeRuntime.getExecutor("function a() {\n" +
147+
" counter.incrementAndGet();\n" +
148+
" if (counter.get() < 10) {\n" +
149+
" setTimeout(a, 0);\n" +
150+
" }\n" +
151+
"};\n" +
152+
"a();").executeVoid();
153+
assertFalse(nodeRuntime.await(V8AwaitMode.RunTillNoMoreTasks));
154+
assertEquals(10, counter.get());
155+
nodeRuntime.getGlobalObject().delete("counter");
156+
} catch (Throwable t) {
157+
fail(t);
158+
} finally {
159+
nodeRuntime.lowMemoryNotification();
160+
}
161+
}
162+
85163
@Test
86164
public void testConsoleArguments() throws JavetException {
87165
NodeRuntimeOptions runtimeOptions = new NodeRuntimeOptions();

0 commit comments

Comments
 (0)