Skip to content

Commit d05e0a9

Browse files
committed
add JS promise test, fix possible bugs in event loop handling
1 parent 4532e91 commit d05e0a9

File tree

10 files changed

+73
-29
lines changed

10 files changed

+73
-29
lines changed

.github/workflows/unit_tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,4 +265,4 @@ jobs:
265265
cd build
266266
# exclude failed tests
267267
# --no-experimental-fetch config from https://github.com/emscripten-core/emscripten/issues/16915
268-
node --no-experimental-fetch UnitTests.js '--gtest_filter=-ThreadPool.*:EngineScopeTest.ExitEngine:EngineScopeTest.TwoThreads:EngineScopeTest.ThreadLocal:MessageQueue.Interrupt:MessageQueue.Shutdown:MessageQueue.ShutdownNow:MessageQueue.FullAndPostInsideLoopQueue:ReferenceTest.WeakGc:ReferenceTest.WeakGc:ReferenceTest.GlobalNotClear:ReferenceTest.GlobalOnEngineDestroy:ReferenceTest.WeakOnEngineDestroy:ReferenceTest.WeakNotClrear:ManagedObjectTest.EngineDispose:ManagedObjectTest.FunctionCallback:PressureTest.All:EngineTest.JsPromiseTest:ShowCaseTest.SetTimeout'
268+
node --no-experimental-fetch UnitTests.js '--gtest_filter=-ThreadPool.*:EngineScopeTest.ExitEngine:EngineScopeTest.TwoThreads:EngineScopeTest.ThreadLocal:MessageQueue.Interrupt:MessageQueue.Shutdown:MessageQueue.ShutdownNow:MessageQueue.FullAndPostInsideLoopQueue:ReferenceTest.WeakGc:ReferenceTest.WeakGc:ReferenceTest.GlobalNotClear:ReferenceTest.GlobalOnEngineDestroy:ReferenceTest.WeakOnEngineDestroy:ReferenceTest.WeakNotClrear:ManagedObjectTest.EngineDispose:ManagedObjectTest.FunctionCallback:PressureTest.All:EngineTest.JsPromiseTest:EngineTest.JsPromiseTest2:ShowCaseTest.SetTimeout'

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.6.0
1+
3.6.1

backend/QuickJs/QjsEngine.cc

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,10 @@ void QjsEngine::destroy() noexcept {
191191
delete this;
192192
}
193193

194-
void QjsEngine::scheduleTick() {
194+
void QjsEngine::triggerTick() {
195195
bool no = false;
196-
if (tickScheduled_.compare_exchange_strong(no, true)) {
196+
if (!isDestroying() && JS_IsJobPending(runtime_) &&
197+
tickScheduled_.compare_exchange_strong(no, true)) {
197198
utils::Message tick(
198199
[](auto& m) {
199200
auto eng = static_cast<QjsEngine*>(m.ptr0);
@@ -203,9 +204,7 @@ void QjsEngine::scheduleTick() {
203204
}
204205
eng->tickScheduled_ = false;
205206
},
206-
[](auto& m) {
207-
208-
});
207+
nullptr);
209208
tick.ptr0 = this;
210209
tick.tag = this;
211210
queue_->postMessage(tick);
@@ -263,7 +262,7 @@ Local<Value> QjsEngine::eval(const Local<String>& script, const Local<Value>& so
263262
}
264263
qjs_backend::checkException(ret);
265264

266-
scheduleTick();
265+
triggerTick();
267266

268267
return Local<Value>(ret);
269268
}

backend/QuickJs/QjsEngine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class QjsEngine : public ScriptEngine {
140140
/**
141141
* similar to js_std_loop
142142
*/
143-
void scheduleTick();
143+
void triggerTick();
144144

145145
void extendLifeTimeToNextLoop(JSValue value);
146146

backend/QuickJs/QjsLocalReference.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ Local<Value> Local<Function>::callImpl(const Local<Value>& thiz, size_t size,
332332
qjs_backend::checkException(ret);
333333
});
334334

335-
engine.scheduleTick();
335+
engine.triggerTick();
336336
return qjs_interop::makeLocal<Value>(ret);
337337
}
338338

backend/QuickJs/QjsScope.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ EngineScopeImpl::EngineScopeImpl(QjsEngine &current, QjsEngine *prev)
3030
}
3131

3232
EngineScopeImpl::~EngineScopeImpl() {
33+
current_->triggerTick();
3334
current_->runtimeLock_.unlock();
3435
if (previous_) {
3536
previous_->runtimeLock_.lock();

backend/QuickJs/QjsValue.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Local<Object> Object::newObjectImpl(const Local<Value>& type, size_t size,
4747
qjs_backend::checkException(ret);
4848
});
4949

50-
engine.scheduleTick();
50+
engine.triggerTick();
5151
return qjs_interop::makeLocal<Object>(ret);
5252
}
5353

backend/V8/V8Platform.cc

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,25 @@ class MessageQueueTaskRunner : public v8::TaskRunner {
102102

103103
private:
104104
void scheduleTask(std::unique_ptr<v8::Task> task, double delay_in_seconds = 0) {
105-
script::utils::Message s([](auto& msg) { static_cast<v8::Task*>(msg.ptr0)->Run(); },
106-
[](auto& msg) {
107-
using deleter = std::unique_ptr<v8::Task>::deleter_type;
108-
deleter{}(static_cast<v8::Task*>(msg.ptr0));
109-
});
105+
if (engine_->isDestroying()) {
106+
return;
107+
}
108+
109+
script::utils::Message s(
110+
[](auto& msg) {
111+
auto engine = static_cast<V8Engine*>(msg.tag);
112+
EngineScope scope(engine);
113+
try {
114+
static_cast<v8::Task*>(msg.ptr0)->Run();
115+
} catch (const Exception& e) {
116+
// this should not happen, all JS exceptions should be handled by V8
117+
abort();
118+
}
119+
},
120+
[](auto& msg) {
121+
using deleter = std::unique_ptr<v8::Task>::deleter_type;
122+
deleter{}(static_cast<v8::Task*>(msg.ptr0));
123+
});
110124
s.name = "SchedulePump";
111125
s.ptr0 = task.release();
112126
s.tag = engine_;

src/version.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919

2020
// ScriptX version config files
2121
// auto generated from the file VERSION
22-
#define SCRIPTX_VERSION_STRING "3.6.0"
22+
#define SCRIPTX_VERSION_STRING "3.6.1"
2323
#define SCRIPTX_VERSION_MAJOR 3
2424
#define SCRIPTX_VERSION_MINOR 6
25-
#define SCRIPTX_VERSION_PATCH 0
25+
#define SCRIPTX_VERSION_PATCH 1
2626

2727
namespace script {
2828

test/src/EngineTest.cc

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -135,26 +135,56 @@ TEST_F(EngineTest, LuaBuiltIns) {
135135

136136
#ifdef SCRIPTX_LANG_JAVASCRIPT
137137
TEST_F(EngineTest, JsPromiseTest) {
138-
EngineScope scope(engine);
139-
140-
int value = 0;
141-
auto setValue = Function::newFunction([&value](int val) { value = val; });
142-
engine->set("setValue", setValue);
143-
engine->eval(
144-
u8R"(
145-
const promise = new Promise((resolve, reject) => {
138+
int value = -1;
139+
{
140+
EngineScope scope(engine);
141+
auto setValue = Function::newFunction([&value](int val) {
142+
// force new line
143+
value = val;
144+
});
145+
engine->set("setValue", setValue);
146+
engine->eval(
147+
u8R"(
148+
new Promise((resolve, reject) => {
149+
resolve(0);
150+
}).then(() => {
151+
setValue(0);
152+
new Promise((resolve, reject) => {
146153
resolve(1);
147-
});
148-
149-
promise.then(num => {
154+
}).then(num => {
150155
setValue(num);
156+
});
151157
});
152158
)");
159+
}
153160

154161
engine->messageQueue()->shutdown(true);
155162
engine->messageQueue()->loopQueue(utils::MessageQueue::LoopType::kLoopAndWait);
156163
EXPECT_EQ(value, 1);
157164
}
165+
166+
TEST_F(EngineTest, JsPromiseTest2) {
167+
int value = -1;
168+
{
169+
EngineScope scope(engine);
170+
auto setValue = Function::newFunction([&value](int val) { value = val; });
171+
engine->set("setValue", setValue);
172+
engine->eval(
173+
u8R"(
174+
new Promise((resolve, reject) => {
175+
resolve(0);
176+
}).then(() => {
177+
setValue(0);
178+
throw error(); // throws in promise callback
179+
setValue(1);
180+
});
181+
)");
182+
}
183+
184+
engine->messageQueue()->shutdown(true);
185+
engine->messageQueue()->loopQueue(utils::MessageQueue::LoopType::kLoopAndWait);
186+
EXPECT_EQ(value, 0);
187+
}
158188
#endif
159189

160190
} // namespace script::test

0 commit comments

Comments
 (0)