Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

JavaScriptCore execution-time-limit watchdog only polls once per VM entry (WebKit Watchdog bug; worked around by re-arming from the callback)

Đang mở
#241 3 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
42/100
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
cpp, javascript
Lĩnh vực
backend

Hướng nghiên cứu

Read Core/AppRuntime/Source/AppRuntime_JavaScriptCore.cpp, especially the Worker branch's ExecutionWatchdog::Rearm, then compare it with WebKit's Source/JavaScriptCore/runtime/Watchdog.cpp and JSContextRefPrivate.h. Run the standalone JavaScriptCore reproduction to confirm the single-callback behavior. Done means the engine behavior is tracked with WebKit and the required re-arm workaround remains documented and intact.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Summary

JavaScriptCore's execution-time-limit watchdog (JSContextGroupSetExecutionTimeLimit, JSContextRefPrivate.h) fires its callback once per VM entry when the callback returns false: Watchdog::shouldTerminate() never restarts the timer in its "callback did nothing" case. Any embedder that polls from that callback — which is how JsRuntimeHost's Worker polyfill lets Worker.terminate() stop a while (true) {} — gets exactly one poll and then a script that can no longer be interrupted.

This is a latent WebKit bug, not something in this repository. JsRuntimeHost works around it by re-setting the limit from inside the callback (the WebKit comment's "case 2"), which restarts the timer through setTimeLimit(). The workaround lives in Core/AppRuntime/Source/AppRuntime_JavaScriptCore.cpp on the Worker branch (ExecutionWatchdog::Rearm, fork commit rebeckerspecialties/JsRuntimeHost@c6f8afd) and will arrive here with the Worker polyfill PR. This issue records the engine behaviour so the re-arm is not "simplified" away later, and so the WebKit report can be tracked.

Where it goes wrong (WebKit Source/JavaScriptCore/runtime/Watchdog.cpp)

//   1. cleared the time limit (i.e. watchdog is disabled),
//   2. set a new time limit via Watchdog::setTimeLimit(), or
//   3. did nothing (i.e. allow another cycle of the current time limit).
...
bool callbackAlreadyStartedTimer = (m_cpuDeadline != noTimeLimit);
if (hasTimeLimit() && !callbackAlreadyStartedTimer)
    startTimer(m_timeLimit);

m_cpuDeadline is only reset to noTimeLimit by stopTimer() (called from exitedVM()). When shouldTerminate() runs after the CPU deadline expired, m_cpuDeadline still holds that expired deadline, so callbackAlreadyStartedTimer is true and case 3 schedules nothing. JSContextRefPrivate.h documents the callback as deciding whether to terminate, not as being responsible for re-arming.

Reproduction (standalone, against the system JavaScriptCore on macOS 27 / Darwin 27.0)

static int calls = 0;
static bool cb(JSContextRef ctx, void* data) { fprintf(stderr, "callback %d\n", ++calls); return calls >= 3; }
...
JSGlobalContextRef ctx = JSGlobalContextCreateInGroup(NULL, NULL);
JSContextGroupSetExecutionTimeLimit(JSContextGetGroup(ctx), 0.05, cb, NULL);
JSEvaluateScript(ctx, JSStringCreateWithUTF8CString("while (true) {}"), NULL, NULL, 1, &exception);
  • Expected: callback 1, callback 2, callback 3, then JSEvaluateScript returns with the termination exception after ~150 ms.
  • Actual: callback 1 only; the script runs forever.
  • With callback = NULL the script is terminated after 50 ms as documented, and re-setting the limit from inside the callback also works — which is the workaround.

Bun's Linux/Android JavaScriptCore archives (#206) carry the same code, so the behaviour is engine-wide, not Apple-specific. (Under ThreadSanitizer on Linux the trap that carries the termination is additionally undeliverable — signal-based VM traps — which is why the fork's TSan job sets JSC_usePollingTraps=1; that is a separate, TSan-only effect.)

Suggested WebKit fix

Clear m_cpuDeadline before invoking the callback so "callback started a timer" is only true when the callback's own setTimeLimit() → startTimer() ran:

    m_cpuDeadline = noTimeLimit;
    bool needsTermination = !m_callback || m_callback(globalObject, m_callbackData1, m_callbackData2);

A bugs.webkit.org report with this content is the next step (draft kept alongside the fork's integration notes); this issue tracks it from the JsRuntimeHost side.

Also worth knowing when touching this path

The termination exception the watchdog raises is a bare string ("JavaScript execution terminated.", VM::ensureTerminationException) since 2021, not an Error object; handing it to napi_create_reference used to trip a RELEASE_ASSERT inside JavaScriptCore on Apple platforms (fixed by #239).

Ngôn ngữ chính
C++
Star
22
Fork
23
Merge trung bình
4 ngày 8 giờ
Pull request đã merge (30 ngày)
3

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của BabylonJS/JsRuntimeHost

Tất cả issue của BabylonJS/JsRuntimeHost

Issue tương tự

Thêm issue về C++

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.