-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: optional async ops #3715
feat: optional async ops #3715
Conversation
1d2fb20
to
2828ec4
Compare
// We're idle if pending_ops is empty. | ||
if inner.pending_ops.is_empty() { | ||
// We're idle if all pending_ops have blocks_exit flag false. | ||
if inner.pending_ops.iter().all(|op| !op.1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not so sure if this would have some impact under large amount of concurrent async op (iterating potentially thousands of pending ops for each poll).
When I did my impl in #2735 , I used a simple counter: whenever optional op added, increment; when done, decrement; when length of pending ops is same as optional ops counter amount, we know all the remaining are optional ones and can safely exit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a todo comment for it.
I agree that this could cause perf issue in future, but I think it's not a huge problem for a moment because there is only a few optional async ops (0 at this moment, and 1 if #3610 landed).
Do you think that we need to address it before landing this?
@kt3k I'm not sure I understand why we need "optional" ops. Could you show some example? |
@bartlomieju It is mostly for signals. We want to allow promises for signal handlers to be resolved when a signal is triggered, but that this promise should not block the process from exit if everything else is done, thus "optional". |
@kevinkassimo so say I have this script: const sig = Deno.signal(Deno.Signal.SIGTERM);
for await (const _ for sig) {
console.log("SIGTERM received!");
} Should this op be optional? That means program would just quit after registering for signal. |
@bartlomieju Treating signal ops as non-optional blocks exit, which I believe is not what people would expect with signal handlers. But I do feel using async iterator pattern and have an optional async op is definitely strange, which was one of the reason when I originally did my old PR I implemented |
@bartlomieju @kevinkassimo And actually if we block the program to exit with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that this PR does not make any changes to the JS interface.
Although sensible defaults nice, I would rather give the end user control over what ops are "optional" vs non-optional. |
I think @kevinkassimo's implementation with 3-variant
Combined with above op we can simply add new field to JSON dispatch to handle such scenarios 👍 |
Op::Async(fut) => { | ||
Op::Async(fut, blocks_exit) => { | ||
let fut2 = fut.map_ok(move |buf| (op_id, buf)); | ||
self.pending_ops.push(fut2.boxed()); | ||
self | ||
.pending_ops | ||
.push(Pin::new(Box::new(PendingOp(fut2.boxed(), blocks_exit)))); | ||
self.have_unpolled_ops = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend using Op::Async
and Op::AsyncOptional
.
Op::AsyncOptional
should be kept in Isolate.pending_optional_ops
- which is the same type as Isolate.pending_ops
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created another version of this at #3721, which uses AsyncOptional
instead of adding flag to Async
. I'd like to avoid having Isolate.pending_optional_ops
because if we have 2 FuturesUnordered
list in isolate, it seems to me that it's difficult to poll these 2 futures list correctly.
closed in favor of #3721 |
Motivation
As described in #3694, it's necessary to end the program even when certain types of async ops are remaining in
pending_ops
list. The example of such op isop_poll_signal
in #3610 (which not yet landed)Solution
This PR enhances
deno_core::Op::Async
type. NowOp::Async
has the flagblocks_exit
along with its mainfuture
. The flag is used when checking the result ofpoll
method of deno_core::Isolate and if the flag is false (= the op is optional), those ops are filtered out in calculation ofPoll
result, which means the program can exit with those optional ops still remaining.closes #3694