Commit f4ab3d2d authored by Kenton Varda's avatar Kenton Varda

Refactor how promise imports are represented in the RPC implementation.

parent 2aa37a8a
This diff is collapsed.
......@@ -428,7 +428,7 @@ struct Resolve {
# When a promise ID is first sent over the wire (e.g. in a `CapDescriptor`), the sender (exporter)
# guarantees that it will follow up at some point with exactly one `Resolve` message. If the
# same `promiseId` is sent again before `Resolve`, still only one `Resolve` is sent. If the
# same ID is reused again later _after_ a `Resolve`, it can only be because the export's
# same ID is sent again later _after_ a `Resolve`, it can only be because the export's
# reference count hit zero in the meantime and the ID was re-assigned to a new export, therefore
# this later promise does _not_ correspond to the earlier `Resolve`.
#
......@@ -816,10 +816,10 @@ struct CapDescriptor {
senderPromise @1 :ExportId;
# A promise which the sender will resolve later. The sender will send exactly one Resolve
# message at a future point in time to replace this promise.
#
# TODO(soon): Can we merge this with senderHosted? Change `Resolve` to be allowed on any
# export (i.e. it can be delivered zero or one times). Maybe rename it to `Replace`.
# message at a future point in time to replace this promise. Note that even if the same
# `senderPromise` is received multiple times, only one `Resolve` is sent to cover all of
# them. The `Resolve` is delivered even if `senderPromise` is not retained, or is retained
# but then released before the `Resolve` is sent.
receiverHosted @2 :ExportId;
# A capability (or promise) previously exported by the receiver.
......
......@@ -259,6 +259,7 @@ inline Array<T> heapArray(size_t size) {
}
template <typename T> Array<T> heapArray(const T* content, size_t size);
template <typename T> Array<T> heapArray(ArrayPtr<T> content);
template <typename T> Array<T> heapArray(ArrayPtr<const T> content);
template <typename T, typename Iterator> Array<T> heapArray(Iterator begin, Iterator end);
template <typename T> Array<T> heapArray(std::initializer_list<T> init);
......@@ -654,6 +655,13 @@ Array<T> heapArray(const T* content, size_t size) {
return builder.finish();
}
template <typename T>
Array<T> heapArray(ArrayPtr<T> content) {
ArrayBuilder<T> builder = heapArrayBuilder<T>(content.size());
builder.addAll(content);
return builder.finish();
}
template <typename T>
Array<T> heapArray(ArrayPtr<const T> content) {
ArrayBuilder<T> builder = heapArrayBuilder<T>(content.size());
......
......@@ -536,5 +536,59 @@ TEST(Async, EventLoopGuarded) {
}
}
class DestructorDetector {
public:
DestructorDetector(bool& setTrue): setTrue(setTrue) {}
~DestructorDetector() { setTrue = true; }
private:
bool& setTrue;
};
TEST(Async, Attach) {
bool destroyed = false;
SimpleEventLoop loop;
Promise<int> promise = loop.evalLater([&]() {
EXPECT_FALSE(destroyed);
return 123;
});
promise.attach(kj::heap<DestructorDetector>(destroyed));
promise = loop.there(kj::mv(promise), [&](int i) {
EXPECT_TRUE(destroyed);
return i + 321;
});
EXPECT_FALSE(destroyed);
EXPECT_EQ(444, loop.wait(kj::mv(promise)));
EXPECT_TRUE(destroyed);
}
TEST(Async, EagerlyEvaluate) {
bool called = false;
SimpleEventLoop loop;
Promise<void> promise = nullptr;
loop.wait(loop.evalLater([&]() {
promise = Promise<void>(READY_NOW).then([&]() {
called = true;
});
}));
loop.wait(loop.evalLater([]() {}));
EXPECT_FALSE(called);
promise.eagerlyEvaluate(loop);
loop.wait(loop.evalLater([]() {}));
EXPECT_TRUE(called);
}
} // namespace
} // namespace kj
......@@ -350,6 +350,27 @@ void ImmediateBrokenPromiseNode::get(ExceptionOrValue& output) noexcept {
// -------------------------------------------------------------------
AttachmentPromiseNodeBase::AttachmentPromiseNodeBase(Own<PromiseNode>&& dependency)
: dependency(kj::mv(dependency)) {}
bool AttachmentPromiseNodeBase::onReady(EventLoop::Event& event) noexcept {
return dependency->onReady(event);
}
void AttachmentPromiseNodeBase::get(ExceptionOrValue& output) noexcept {
dependency->get(output);
}
Maybe<const EventLoop&> AttachmentPromiseNodeBase::getSafeEventLoop() noexcept {
return dependency->getSafeEventLoop();
}
void AttachmentPromiseNodeBase::dropDependency() {
dependency = nullptr;
}
// -------------------------------------------------------------------
TransformPromiseNodeBase::TransformPromiseNodeBase(
Maybe<const EventLoop&> loop, Own<PromiseNode>&& dependency)
: loop(loop), dependency(kj::mv(dependency)) {}
......@@ -375,6 +396,15 @@ void TransformPromiseNodeBase::dropDependency() {
dependency = nullptr;
}
void TransformPromiseNodeBase::getDepResult(ExceptionOrValue& output) {
dependency->get(output);
KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]() {
dependency = nullptr;
})) {
output.addException(kj::mv(*exception));
}
}
// -------------------------------------------------------------------
ForkBranchBase::ForkBranchBase(Own<const ForkHubBase>&& hubParam): hub(kj::mv(hubParam)) {
......
......@@ -28,6 +28,7 @@
#include "mutex.h"
#include "refcount.h"
#include "work-queue.h"
#include "tuple.h"
namespace kj {
......@@ -631,6 +632,18 @@ public:
// `Own<U>`, `U` must have a method `Own<const U> addRef() const` which returns a new reference
// to the same (or an equivalent) object (probably implemented via reference counting).
template <typename... Attachments>
void attach(Attachments&&... attachments);
// "Attaches" one or more movable objects (often, Own<T>s) to the promise, such that they will
// be destroyed when the promise resolves. This is useful when a promise's callback contains
// pointers into some object and you want to make sure the object still exists when the callback
// runs -- after calling then(), use attach() to add necessary objects to the result.
void eagerlyEvaluate(const EventLoop& eventLoop = EventLoop::current());
// Force eager evaluation of this promise. Use this if you are going to hold on to the promise
// for awhile without consuming the result, but you want to make sure that the system actually
// processes it.
private:
Promise(bool, Own<_::PromiseNode>&& node): PromiseBase(kj::mv(node)) {}
// Second parameter prevent ambiguity with immediate-value constructor.
......@@ -996,6 +1009,45 @@ private:
// -------------------------------------------------------------------
class AttachmentPromiseNodeBase: public PromiseNode {
public:
AttachmentPromiseNodeBase(Own<PromiseNode>&& dependency);
bool onReady(EventLoop::Event& event) noexcept override;
void get(ExceptionOrValue& output) noexcept override;
Maybe<const EventLoop&> getSafeEventLoop() noexcept override;
private:
Own<PromiseNode> dependency;
void dropDependency();
template <typename>
friend class AttachmentPromiseNode;
};
template <typename Attachment>
class AttachmentPromiseNode final: public AttachmentPromiseNodeBase {
// A PromiseNode that holds on to some object (usually, an Own<T>, but could be any movable
// object) until the promise resolves.
public:
AttachmentPromiseNode(Own<PromiseNode>&& dependency, Attachment&& attachment)
: AttachmentPromiseNodeBase(kj::mv(dependency)),
attachment(kj::mv<Attachment>(attachment)) {}
~AttachmentPromiseNode() noexcept(false) {
// We need to make sure the dependency is deleted before we delete the attachment because the
// dependency may be using the attachment.
dropDependency();
}
private:
Attachment attachment;
};
// -------------------------------------------------------------------
class TransformPromiseNodeBase: public PromiseNode {
public:
TransformPromiseNodeBase(Maybe<const EventLoop&> loop, Own<PromiseNode>&& dependency);
......@@ -1009,6 +1061,7 @@ private:
Own<PromiseNode> dependency;
void dropDependency();
void getDepResult(ExceptionOrValue& output);
virtual void getImpl(ExceptionOrValue& output) = 0;
......@@ -1040,7 +1093,7 @@ private:
void getImpl(ExceptionOrValue& output) override {
ExceptionOr<DepT> depResult;
dependency->get(depResult);
getDepResult(depResult);
KJ_IF_MAYBE(depException, depResult.exception) {
output.as<T>() = handle(
MaybeVoidCaller<Exception, FixVoid<ReturnType<ErrorFunc, Exception>>>::apply(
......@@ -1452,6 +1505,18 @@ Promise<_::Forked<T>> ForkedPromise<T>::addBranch() const {
return hub->addBranch();
}
template <typename T>
template <typename... Attachments>
void Promise<T>::attach(Attachments&&... attachments) {
node = kj::heap<_::AttachmentPromiseNode<Tuple<Attachments...>>>(
kj::mv(node), kj::tuple(kj::fwd<Attachments>(attachments)...));
}
template <typename T>
void Promise<T>::eagerlyEvaluate(const EventLoop& eventLoop) {
node = _::spark<_::FixVoid<T>>(kj::mv(node), eventLoop);
}
// =======================================================================================
namespace _ { // private
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment