Commit e2905da7 authored by Kenton Varda's avatar Kenton Varda

Test and fix embargoes.

parent 6b8b8c71
...@@ -207,7 +207,13 @@ class QueuedPipeline final: public PipelineHook, public kj::Refcounted { ...@@ -207,7 +207,13 @@ class QueuedPipeline final: public PipelineHook, public kj::Refcounted {
public: public:
QueuedPipeline(const kj::EventLoop& loop, kj::Promise<kj::Own<const PipelineHook>>&& promise) QueuedPipeline(const kj::EventLoop& loop, kj::Promise<kj::Own<const PipelineHook>>&& promise)
: loop(loop), : loop(loop),
promise(loop.fork(kj::mv(promise))) {} promise(loop.fork(kj::mv(promise))),
selfResolutionOp(loop.there(this->promise.addBranch(),
[this](kj::Own<const PipelineHook>&& inner) {
*redirect.lockExclusive() = kj::mv(inner);
})) {
selfResolutionOp.eagerlyEvaluate(loop);
}
kj::Own<const PipelineHook> addRef() const override { kj::Own<const PipelineHook> addRef() const override {
return kj::addRef(*this); return kj::addRef(*this);
...@@ -226,6 +232,12 @@ public: ...@@ -226,6 +232,12 @@ public:
private: private:
const kj::EventLoop& loop; const kj::EventLoop& loop;
kj::ForkedPromise<kj::Own<const PipelineHook>> promise; kj::ForkedPromise<kj::Own<const PipelineHook>> promise;
kj::MutexGuarded<kj::Maybe<kj::Own<const PipelineHook>>> redirect;
// Once the promise resolves, this will become non-null and point to the underlying object.
kj::Promise<void> selfResolutionOp;
// Represents the operation which will set `redirect` when possible.
}; };
class QueuedClient final: public ClientHook, public kj::Refcounted { class QueuedClient final: public ClientHook, public kj::Refcounted {
...@@ -371,12 +383,18 @@ private: ...@@ -371,12 +383,18 @@ private:
}; };
kj::Own<const ClientHook> QueuedPipeline::getPipelinedCap(kj::Array<PipelineOp>&& ops) const { kj::Own<const ClientHook> QueuedPipeline::getPipelinedCap(kj::Array<PipelineOp>&& ops) const {
auto clientPromise = loop.there(promise.addBranch(), kj::mvCapture(ops, auto lock = redirect.lockShared();
[](kj::Array<PipelineOp>&& ops, kj::Own<const PipelineHook> pipeline) {
return pipeline->getPipelinedCap(kj::mv(ops));
}));
return kj::refcounted<QueuedClient>(loop, kj::mv(clientPromise)); KJ_IF_MAYBE(redirect, *lock) {
return redirect->get()->getPipelinedCap(kj::mv(ops));
} else {
auto clientPromise = loop.there(promise.addBranch(), kj::mvCapture(ops,
[](kj::Array<PipelineOp>&& ops, kj::Own<const PipelineHook> pipeline) {
return pipeline->getPipelinedCap(kj::mv(ops));
}));
return kj::refcounted<QueuedClient>(loop, kj::mv(clientPromise));
}
} }
// ======================================================================================= // =======================================================================================
......
This diff is collapsed.
This diff is collapsed.
...@@ -1052,5 +1052,11 @@ kj::Promise<void> TestMoreStuffImpl::getHeld( ...@@ -1052,5 +1052,11 @@ kj::Promise<void> TestMoreStuffImpl::getHeld(
return kj::READY_NOW; return kj::READY_NOW;
} }
kj::Promise<void> TestMoreStuffImpl::echo(EchoParams::Reader params, EchoResults::Builder result) {
++callCount;
result.setCap(params.getCap());
return kj::READY_NOW;
}
} // namespace _ (private) } // namespace _ (private)
} // namespace capnp } // namespace capnp
...@@ -237,6 +237,8 @@ public: ...@@ -237,6 +237,8 @@ public:
kj::Promise<void> getHeld(GetHeldParams::Reader params, kj::Promise<void> getHeld(GetHeldParams::Reader params,
GetHeldResults::Builder result) override; GetHeldResults::Builder result) override;
kj::Promise<void> echo(EchoParams::Reader params, EchoResults::Builder result) override;
private: private:
int& callCount; int& callCount;
kj::Own<kj::PromiseFulfiller<void>> neverFulfill; kj::Own<kj::PromiseFulfiller<void>> neverFulfill;
......
...@@ -611,8 +611,10 @@ interface TestPipeline { ...@@ -611,8 +611,10 @@ interface TestPipeline {
} }
interface TestCallOrder { interface TestCallOrder {
getCallSequence @0 () -> (n: UInt32); getCallSequence @0 (expected: UInt32) -> (n: UInt32);
# First call returns 0, next returns 1, ... # First call returns 0, next returns 1, ...
#
# The input `expected` is ignored but useful for disambiguating debug logs.
} }
interface TestTailCallee { interface TestTailCallee {
...@@ -649,6 +651,9 @@ interface TestMoreStuff extends(TestCallOrder) { ...@@ -649,6 +651,9 @@ interface TestMoreStuff extends(TestCallOrder) {
getHeld @5 () -> (cap :TestInterface); getHeld @5 () -> (cap :TestInterface);
# Returns the capability previously held using `hold` (and keeps holding it). # Returns the capability previously held using `hold` (and keeps holding it).
echo @6 (cap :TestCallOrder) -> (cap :TestCallOrder);
# Just returns the input cap.
} }
struct TestSturdyRefHostId { struct TestSturdyRefHostId {
......
...@@ -69,6 +69,91 @@ public: ...@@ -69,6 +69,91 @@ public:
} // namespace } // namespace
namespace _ { // private
class TaskSetImpl {
public:
inline TaskSetImpl(const EventLoop& loop, TaskSet::ErrorHandler& errorHandler)
: loop(loop), errorHandler(errorHandler) {}
~TaskSetImpl() noexcept(false) {
// std::map doesn't like it when elements' destructors throw, so carefully disassemble it.
auto& taskMap = tasks.getWithoutLock();
if (!taskMap.empty()) {
Vector<Own<Task>> deleteMe(taskMap.size());
for (auto& entry: taskMap) {
deleteMe.add(kj::mv(entry.second));
}
}
}
class Task final: public EventLoop::Event {
public:
Task(const TaskSetImpl& taskSet, Own<_::PromiseNode>&& nodeParam)
: EventLoop::Event(taskSet.loop), taskSet(taskSet), node(kj::mv(nodeParam)) {
if (node->onReady(*this)) {
arm();
}
}
~Task() {
disarm();
}
protected:
void fire() override {
// Get the result.
_::ExceptionOr<_::Void> result;
node->get(result);
// Delete the node, catching any exceptions.
KJ_IF_MAYBE(exception, kj::runCatchingExceptions([this]() {
node = nullptr;
})) {
result.addException(kj::mv(*exception));
}
// Call the error handler if there was an exception.
KJ_IF_MAYBE(e, result.exception) {
taskSet.errorHandler.taskFailed(kj::mv(*e));
}
}
private:
const TaskSetImpl& taskSet;
kj::Own<_::PromiseNode> node;
};
void add(Promise<void>&& promise) const {
auto task = heap<Task>(*this, _::makeSafeForLoop<_::Void>(kj::mv(promise.node), loop));
Task* ptr = task;
tasks.lockExclusive()->insert(std::make_pair(ptr, kj::mv(task)));
}
private:
const EventLoop& loop;
TaskSet::ErrorHandler& errorHandler;
// TODO(soon): Use a linked list instead. We should factor out the intrusive linked list code
// that appears in EventLoop and ForkHub.
MutexGuarded<std::map<Task*, Own<Task>>> tasks;
};
class LoggingErrorHandler: public TaskSet::ErrorHandler {
public:
static LoggingErrorHandler instance;
void taskFailed(kj::Exception&& exception) override {
KJ_LOG(ERROR, "Uncaught exception in daemonized task.", exception);
}
};
LoggingErrorHandler LoggingErrorHandler::instance = LoggingErrorHandler();
} // namespace _ (private)
// =======================================================================================
EventLoop& EventLoop::current() { EventLoop& EventLoop::current() {
EventLoop* result = threadLocalEventLoop; EventLoop* result = threadLocalEventLoop;
KJ_REQUIRE(result != nullptr, "No event loop is running on this thread."); KJ_REQUIRE(result != nullptr, "No event loop is running on this thread.");
...@@ -79,7 +164,10 @@ bool EventLoop::isCurrent() const { ...@@ -79,7 +164,10 @@ bool EventLoop::isCurrent() const {
return threadLocalEventLoop == this; return threadLocalEventLoop == this;
} }
EventLoop::EventLoop() {} EventLoop::EventLoop()
: daemons(kj::heap<_::TaskSetImpl>(*this, _::LoggingErrorHandler::instance)) {}
EventLoop::~EventLoop() noexcept(false) {}
void EventLoop::waitImpl(Own<_::PromiseNode> node, _::ExceptionOrValue& result) { void EventLoop::waitImpl(Own<_::PromiseNode> node, _::ExceptionOrValue& result) {
EventLoop* oldEventLoop = threadLocalEventLoop; EventLoop* oldEventLoop = threadLocalEventLoop;
...@@ -119,6 +207,10 @@ void EventLoop::receivedNewJob() const { ...@@ -119,6 +207,10 @@ void EventLoop::receivedNewJob() const {
wake(); wake();
} }
void EventLoop::daemonize(kj::Promise<void>&& promise) const {
daemons->add(kj::mv(promise));
}
EventLoop::Event::Event(const EventLoop& loop) EventLoop::Event::Event(const EventLoop& loop)
: loop(loop), : loop(loop),
jobs { loop.queue.createJob(*this), loop.queue.createJob(*this) } {} jobs { loop.queue.createJob(*this), loop.queue.createJob(*this) } {}
...@@ -227,76 +319,8 @@ void PromiseBase::absolve() { ...@@ -227,76 +319,8 @@ void PromiseBase::absolve() {
runCatchingExceptions([this]() { node = nullptr; }); runCatchingExceptions([this]() { node = nullptr; });
} }
class TaskSet::Impl {
public:
inline Impl(const EventLoop& loop, ErrorHandler& errorHandler)
: loop(loop), errorHandler(errorHandler) {}
~Impl() noexcept(false) {
// std::map doesn't like it when elements' destructors throw, so carefully disassemble it.
auto& taskMap = tasks.getWithoutLock();
if (!taskMap.empty()) {
Vector<Own<Task>> deleteMe(taskMap.size());
for (auto& entry: taskMap) {
deleteMe.add(kj::mv(entry.second));
}
}
}
class Task final: public EventLoop::Event {
public:
Task(const Impl& taskSet, Own<_::PromiseNode>&& nodeParam)
: EventLoop::Event(taskSet.loop), taskSet(taskSet), node(kj::mv(nodeParam)) {
if (node->onReady(*this)) {
arm();
}
}
~Task() {
disarm();
}
protected:
void fire() override {
// Get the result.
_::ExceptionOr<_::Void> result;
node->get(result);
// Delete the node, catching any exceptions.
KJ_IF_MAYBE(exception, runCatchingExceptions([this]() {
node = nullptr;
})) {
result.addException(kj::mv(*exception));
}
// Call the error handler if there was an exception.
KJ_IF_MAYBE(e, result.exception) {
taskSet.errorHandler.taskFailed(kj::mv(*e));
}
}
private:
const Impl& taskSet;
kj::Own<_::PromiseNode> node;
};
void add(Promise<void>&& promise) const {
auto task = heap<Task>(*this, _::makeSafeForLoop<_::Void>(kj::mv(promise.node), loop));
Task* ptr = task;
tasks.lockExclusive()->insert(std::make_pair(ptr, kj::mv(task)));
}
private:
const EventLoop& loop;
ErrorHandler& errorHandler;
// TODO(soon): Use a linked list instead. We should factor out the intrusive linked list code
// that appears in EventLoop and ForkHub.
MutexGuarded<std::map<Task*, Own<Task>>> tasks;
};
TaskSet::TaskSet(const EventLoop& loop, ErrorHandler& errorHandler) TaskSet::TaskSet(const EventLoop& loop, ErrorHandler& errorHandler)
: impl(heap<Impl>(loop, errorHandler)) {} : impl(heap<_::TaskSetImpl>(loop, errorHandler)) {}
TaskSet::~TaskSet() noexcept(false) {} TaskSet::~TaskSet() noexcept(false) {}
......
...@@ -182,6 +182,8 @@ class ChainPromiseNode; ...@@ -182,6 +182,8 @@ class ChainPromiseNode;
template <typename T> template <typename T>
class ForkHub; class ForkHub;
class TaskSetImpl;
} // namespace _ (private) } // namespace _ (private)
// ======================================================================================= // =======================================================================================
...@@ -228,6 +230,7 @@ class EventLoop: private _::NewJobCallback { ...@@ -228,6 +230,7 @@ class EventLoop: private _::NewJobCallback {
public: public:
EventLoop(); EventLoop();
~EventLoop() noexcept(false);
static EventLoop& current(); static EventLoop& current();
// Get the event loop for the current thread. Throws an exception if no event loop is active. // Get the event loop for the current thread. Throws an exception if no event loop is active.
...@@ -298,6 +301,15 @@ public: ...@@ -298,6 +301,15 @@ public:
Promise<T> exclusiveJoin(Promise<T>&& promise1, Promise<T>&& promise2) const; Promise<T> exclusiveJoin(Promise<T>&& promise1, Promise<T>&& promise2) const;
// Like `promise1.exclusiveJoin(promise2)`, returning the joined promise. // Like `promise1.exclusiveJoin(promise2)`, returning the joined promise.
void daemonize(kj::Promise<void>&& promise) const;
// Allows the given promise to continue running in the background until it completes or the
// `EventLoop` is destroyed. Be careful when using this: you need to make sure that the promise
// owns all the objects it touches or make sure those objects outlive the EventLoop. Also, be
// careful about error handling: exceptions will merely be logged with KJ_LOG(ERROR, ...).
//
// This method exists mainly to implement the Cap'n Proto requirement that RPC calls cannot be
// canceled unless the callee explicitly permits it.
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// Low-level interface. // Low-level interface.
...@@ -387,6 +399,8 @@ private: ...@@ -387,6 +399,8 @@ private:
Maybe<_::WorkQueue<EventJob>::JobWrapper&> insertionPoint; Maybe<_::WorkQueue<EventJob>::JobWrapper&> insertionPoint;
// Where to insert preemptively-scheduled events into the queue. // Where to insert preemptively-scheduled events into the queue.
Own<_::TaskSetImpl> daemons;
template <typename T, typename Func, typename ErrorFunc> template <typename T, typename Func, typename ErrorFunc>
Own<_::PromiseNode> thereImpl(Promise<T>&& promise, Func&& func, ErrorFunc&& errorHandler) const; Own<_::PromiseNode> thereImpl(Promise<T>&& promise, Func&& func, ErrorFunc&& errorHandler) const;
// Shared implementation of there() and Promise::then(). // Shared implementation of there() and Promise::then().
...@@ -456,7 +470,7 @@ private: ...@@ -456,7 +470,7 @@ private:
friend class _::ChainPromiseNode; friend class _::ChainPromiseNode;
template <typename> template <typename>
friend class Promise; friend class Promise;
friend class TaskSet; friend class _::TaskSetImpl;
}; };
template <typename T> template <typename T>
...@@ -763,8 +777,7 @@ public: ...@@ -763,8 +777,7 @@ public:
void add(Promise<void>&& promise) const; void add(Promise<void>&& promise) const;
private: private:
class Impl; Own<_::TaskSetImpl> impl;
Own<Impl> impl;
}; };
constexpr _::Void READY_NOW = _::Void(); constexpr _::Void READY_NOW = _::Void();
......
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