fix: only marshal promise resolution when created on the runtime loop#396
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughPromise proxy installation now passes a runtime-loop callback into the embedded bootstrap, and promise resolve/reject/.then scheduling now checks the runtime loop before marshaling to another runloop. A new test covers background creation with main-queue settlement. ChangesPromise proxy runtime-loop scheduling
Sequence Diagram(s)sequenceDiagram
participant PromiseProxy as "PromiseProxy.cpp"
participant RuntimeLoop as "runtime->RuntimeLoop()"
participant PerformBlock as "CFRunLoopPerformBlock"
participant WakeUp as "CFRunLoopWakeUp"
PromiseProxy->>RuntimeLoop: compare current CFRunLoop
alt current thread matches runtime loop
PromiseProxy->>PromiseProxy: invoke resolve/reject/.then synchronously
else current thread differs
PromiseProxy->>PerformBlock: queue callback on the captured runloop
PromiseProxy->>WakeUp: wake the target runloop
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
TestRunner/app/tests/Promises.js (1)
142-147: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winFail explicitly on unexpected rejection.
If this promise rejects, the test currently hangs until timeout, which obscures the failure. Add a
catchthat fails and completes the async spec.Proposed test hardening
new Promise(resolve => { NSOperationQueue.mainQueue.addOperationWithBlock(() => resolve("settled")); }).then(value => { expect(value).toBe("settled"); done(); + }).catch(error => { + expect(true).toBe(false, `Promise rejected unexpectedly: ${error}`); + done(); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@TestRunner/app/tests/Promises.js` around lines 142 - 147, The Promise test in the async spec currently only handles the success path, so unexpected rejection can hang the test until timeout. Update the Promise chain in the `Promises.js` test case to add a `catch` alongside the existing `then` so any rejection explicitly fails the spec and calls `done()`. Keep the change focused on the affected promise block near the `NSOperationQueue.mainQueue.addOperationWithBlock` usage and the `expect(value).toBe("settled")` assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@TestRunner/app/tests/Promises.js`:
- Around line 142-147: The Promise test in the async spec currently only handles
the success path, so unexpected rejection can hang the test until timeout.
Update the Promise chain in the `Promises.js` test case to add a `catch`
alongside the existing `then` so any rejection explicitly fails the spec and
calls `done()`. Keep the change focused on the affected promise block near the
`NSOperationQueue.mainQueue.addOperationWithBlock` usage and the
`expect(value).toBe("settled")` assertion.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 95e3cfae-e63f-4054-b4fc-cd4897841294
📒 Files selected for processing (2)
NativeScript/runtime/PromiseProxy.cppTestRunner/app/tests/Promises.js
The promise proxy captures the run loop at construction and, when a promise resolves on a different thread, marshals the resolution back to that captured run loop. When a promise is constructed while native code runs JS on a background thread, the captured run loop belongs to that background thread, which is dormant once the call returns. A resolution that arrives later on the runtime loop (for example from a setTimeout) is scheduled onto the dormant run loop and never runs, so the promise hangs forever. Only marshal the resolution when the promise was created on the runtime loop, which is always being pumped. A promise created on any other thread resolves inline on whichever thread settles it. The same gate is applied to the get trap on the returned promise. Fixes NativeScript#330
ebe32ac to
79a74fc
Compare
@coderabbitai, fixed now. |
|
Tip For best results, initiate chat on the files or code changes.
(ᵔᴥᵔ) If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! |
A promise constructed while native code runs JS on a background thread never resolves. The proxy captures the current run loop at construction, and when the promise later resolves on a different thread it marshals the resolution back to that captured run loop. For a promise created on a background thread, that run loop is dormant once the call returns, so a resolution arriving on the runtime loop (for example from a
setTimeout) is scheduled onto a loop nobody pumps and never runs.This gates the marshaling on origin: a resolution is only marshaled back when the promise was created on the runtime loop, which is always being pumped. A promise created on any other thread resolves inline on whichever thread settles it. The same gate is applied to the get trap on the returned promise. This matches the approach discussed in the issue.
Test:
TestRunner/app/tests/Promises.jsgets a case that constructs a promise on a background dispatch queue and resolves it on the runtime loop. It times out before the change and passes after; the rest of the suite stays green.Fixes #330
Summary by CodeRabbit
.thenhandlers run immediately when appropriate, or dispatch correctly when crossing the thread boundary.NSOperationQueue.