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

Implement newCrossThreadPromiseFulfiller(). #1174

Merged
merged 2 commits into from
Feb 8, 2021
Merged

Conversation

kentonv
Copy link
Member

@kentonv kentonv commented Feb 5, 2021

For use cases that fit, this should be significantly easier to use, and also perform better, than Executor has been.

@kentonv
Copy link
Member Author

kentonv commented Feb 5, 2021

(No changes since last night, just squashed some commits now that they're verified to work.)

Copy link
Member

@harrishancock harrishancock left a comment

Choose a reason for hiding this comment

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

I left some suggestions, but looks good.

c++/src/kj/async-inl.h Outdated Show resolved Hide resolved
c++/src/kj/async-inl.h Outdated Show resolved Hide resolved
c++/src/kj/async-inl.h Show resolved Hide resolved
bool shouldFulfill() { return obj != nullptr; }

template <typename T>
XThreadPafImpl<T>* getTarget() { return static_cast<XThreadPafImpl<T>*>(obj); }
Copy link
Member

Choose a reason for hiding this comment

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

Would this be a use case for kj::downcast<T>()? (Fine with me either way.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe. In this case, you can easily verify from surrounding code that the downcast is safe, so I'm comfortable skipping checking entirely. I like to use downcast more when it's part of an API's contract that you're supposed to pass in a specific type, so it's checking that the calling code didn't screw up.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, like a require vs an assert.

template <typename T>
PromiseFulfillerPair<T> newCrossThreadPromiseAndFulfiller();
// Like `newPromiseAndFulfiller()`, but the fulfiller is allowed to be invoked from any thread,
// not just the one that called this method.
Copy link
Member

Choose a reason for hiding this comment

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

It might be worth mentioning that both the promise's and the fulfiller's destructors will lock the construction thread's executor. I wrote a deadlock due to not considering the similar behavior in executeAsync()'s returned promise at one point.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, I don't understand how this could lead to deadlock. For deadlock, you have to have two mutexes locked in opposite orders. We shouldn't be locking any other mutexes while holding an executor lock -- if we are, then that's a bug that needs fixing.

Copy link
Member

Choose a reason for hiding this comment

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

As discussed in chat, I was remember something slightly different:

  • Thread A locks Mutex A.
  • Thread B blocks waiting for Mutex A.
  • Thread A waits on Thread B's executor state to change, but it never does.

It looks like this doesn't suffer from the same problem -- the XThreadPaf's disposer does perform a wait on its own thread's executor, but this wait will always succeed because it's just waiting for FULFILLING to become FULFILLED.

So, never mind me.

Copy link
Member

@harrishancock harrishancock left a comment

Choose a reason for hiding this comment

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

Actually reviewed the later commits this time.

// MSVC's atomic intrinsics are weird and different, whereas the C++ standard atomics match the GCC
// builtins -- except for requiring the obnoxious std::atomic<T> wrapper. So, on MSVC let's just
// #define the builtins based on the C++ library, reinterpret-casting native types to
// std::atomic... this is cheating but ugh, whatever.
Copy link
Member

Choose a reason for hiding this comment

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

Your sin has been recorded in the ledger.

For use cases that fit, this should be significantly easier to use, and also perform better, than `Executor` has been.
@kentonv
Copy link
Member Author

kentonv commented Feb 8, 2021

Rebased on master, no other changes. Previous MacOS failure was a timer test flake. Gonna merge.

@kentonv kentonv merged commit 267bfea into master Feb 8, 2021
@kentonv kentonv deleted the kenton/xthread-fulfiller branch February 8, 2021 18:20
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.

2 participants