Skip to content
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 2 #3721

Merged
merged 7 commits into from
Jan 21, 2020
Merged

Conversation

kt3k
Copy link
Member

@kt3k kt3k commented Jan 20, 2020

This PR is the variation of #3715. This uses AsyncOptional enum variant instead of adding the flags.

@kt3k kt3k mentioned this pull request Jan 20, 2020
@@ -14,6 +14,9 @@ pub type AsyncJsonOp =
pub enum JsonOp {
Sync(Value),
Async(AsyncJsonOp),
// AsyncOptional is the variation of Async, which
// doesn't block the program exiting.
AsyncOptional(AsyncJsonOp),
Copy link
Member

@ry ry Jan 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Optional" isn't very descriptive. Maybe we could call it something like AsyncNoKeepAlive ? AsyncIgnoredAtExit ? AsyncUnref ?

core/ops.rs Outdated
pub type OpResult<E> = Result<Op<E>, E>;

pub enum Op<E> {
Sync(Buf),
Async(OpAsyncFuture<E>),
// AsyncOptional is the variation of Async, which
// doesn't block the program exiting.
AsyncOptional(OpAsyncFuture<E>),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use triple slash comments here

core/ops.rs Outdated
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
self.as_mut().0.as_mut().poll(cx)
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really necessary? Seems odd.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, I'm still advocating for Isolate.pending_optional_ops field.

Then in Isolate::poll:

...
loop {
  inner.have_unpolled_ops = false;
  #[allow(clippy::match_wild_err_arm)]
  match inner.pending_ops.poll_next_unpin(cx) {
    Poll::Ready(Some(Err(_))) => panic!("unexpected op error"),
    Poll::Ready(None) => break,
    Poll::Pending => break,
    Poll::Ready(Some(Ok((op_id, buf)))) => {
      let successful_push = inner.shared.push(op_id, &buf);
      if !successful_push {
        // If we couldn't push the response to the shared queue, because
        // there wasn't enough size, we will return the buffer via the
        // legacy route, using the argument of deno_respond.
        overflow_response = Some((op_id, buf));
        break;
      }
    }
  }
  #[allow(clippy::match_wild_err_arm)]
  match inner.pending_ops_optional.poll_next_unpin(cx) {
    Poll::Ready(Some(Err(_))) => panic!("unexpected op error"),
    Poll::Ready(None) => break,
    Poll::Pending => break,
    Poll::Ready(Some(Ok((op_id, buf)))) => {
      let successful_push = inner.shared.push(op_id, &buf);
      if !successful_push {
        overflow_response = Some((op_id, buf));
        break;
      }
    }
  }
}
...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bartlomieju But isn't that having the chance of starving?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinkassimo you might be right, then I guess using select would be in order.

Then final check returning Poll will still check if pending_ops is not empty ignoring pending_ops_optional

@kevinkassimo
Copy link
Contributor

kevinkassimo commented Jan 21, 2020

Actually I think given our current design it might be possible to do explicit Unref: somehow wrapping the op futures in a struct that also saves promiseId (or some other unique ID) and a bool on whether it is ref-ed (since with this PR we are scanning through all pending_ops anyways on each poll). Maybe we can also create a general Deno.unref(promiseId) that is a binding reaching into pending_ops and toggle that bool value.

@ry
Copy link
Member

ry commented Jan 21, 2020

Maybe we can also create a general Deno.unref(promiseId) that is a binding reaching into pending_ops and toggle that bool value.

I think that would be ideal

@bartlomieju
Copy link
Member

Maybe we can also create a general Deno.unref(promiseId) that is a binding reaching into pending_ops and toggle that bool value.

I think that would be ideal

I don't really like that solution - it requires deep changes to ops infra. At the moment when you dispatch op it's "black boxed" and you have no way to interact with it directly.

Doing Deno.unref(promiseId) means that you'll need some kind of lookup mechanism - I proposed moving promiseId to be argument of Deno.core.send() but it was rejected as promise_id is an implementation detail of concrete dispatch mechanism.

Although I see why it might be useful, I suggest to go with simpler approach for now (just optional ops, no way to control them after they're dispatched) to solve problem at hand (signal handlers). If valid use case arises for Deno.unref we could revisit

kt3k and others added 3 commits January 22, 2020 00:26
- rename AsyncOptional -> AsyncUnref
- create Isolate.pending_unref_ops and use select
* AsyncUnref is the variation of Async, which
* doesn't block the program exiting.
*/
/// AsyncUnref is the variation of Async, which doesn't block the program
Copy link
Member Author

@kt3k kt3k Jan 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry. I misread your comment!

Copy link
Member

@ry ry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - nice solution to a difficult core problem @kt3k!

Comment on lines +723 to +725
match select(&mut inner.pending_ops, &mut inner.pending_unref_ops)
.poll_next_unpin(cx)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very elegant

@ry ry merged commit 9de8178 into denoland:master Jan 21, 2020
@kt3k kt3k deleted the feature/optional-async-ops-2 branch January 21, 2020 17:05
@kt3k kt3k mentioned this pull request Jan 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants