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 {
public:
QueuedPipeline(const kj::EventLoop& loop, kj::Promise<kj::Own<const PipelineHook>>&& promise)
: 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 {
return kj::addRef(*this);
......@@ -226,6 +232,12 @@ public:
private:
const kj::EventLoop& loop;
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 {
......@@ -371,12 +383,18 @@ private:
};
kj::Own<const ClientHook> QueuedPipeline::getPipelinedCap(kj::Array<PipelineOp>&& ops) const {
auto clientPromise = loop.there(promise.addBranch(), kj::mvCapture(ops,
[](kj::Array<PipelineOp>&& ops, kj::Own<const PipelineHook> pipeline) {
return pipeline->getPipelinedCap(kj::mv(ops));
}));
auto lock = redirect.lockShared();
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(
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 capnp
......@@ -237,6 +237,8 @@ public:
kj::Promise<void> getHeld(GetHeldParams::Reader params,
GetHeldResults::Builder result) override;
kj::Promise<void> echo(EchoParams::Reader params, EchoResults::Builder result) override;
private:
int& callCount;
kj::Own<kj::PromiseFulfiller<void>> neverFulfill;
......
......@@ -611,8 +611,10 @@ interface TestPipeline {
}
interface TestCallOrder {
getCallSequence @0 () -> (n: UInt32);
getCallSequence @0 (expected: UInt32) -> (n: UInt32);
# First call returns 0, next returns 1, ...
#
# The input `expected` is ignored but useful for disambiguating debug logs.
}
interface TestTailCallee {
......@@ -649,6 +651,9 @@ interface TestMoreStuff extends(TestCallOrder) {
getHeld @5 () -> (cap :TestInterface);
# Returns the capability previously held using `hold` (and keeps holding it).
echo @6 (cap :TestCallOrder) -> (cap :TestCallOrder);
# Just returns the input cap.
}
struct TestSturdyRefHostId {
......
......@@ -69,6 +69,91 @@ public:
} // 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* result = threadLocalEventLoop;
KJ_REQUIRE(result != nullptr, "No event loop is running on this thread.");
......@@ -79,7 +164,10 @@ bool EventLoop::isCurrent() const {
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) {
EventLoop* oldEventLoop = threadLocalEventLoop;
......@@ -119,6 +207,10 @@ void EventLoop::receivedNewJob() const {
wake();
}
void EventLoop::daemonize(kj::Promise<void>&& promise) const {
daemons->add(kj::mv(promise));
}
EventLoop::Event::Event(const EventLoop& loop)
: loop(loop),
jobs { loop.queue.createJob(*this), loop.queue.createJob(*this) } {}
......@@ -227,76 +319,8 @@ void PromiseBase::absolve() {
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)
: impl(heap<Impl>(loop, errorHandler)) {}
: impl(heap<_::TaskSetImpl>(loop, errorHandler)) {}
TaskSet::~TaskSet() noexcept(false) {}
......
......@@ -182,6 +182,8 @@ class ChainPromiseNode;
template <typename T>
class ForkHub;
class TaskSetImpl;
} // namespace _ (private)
// =======================================================================================
......@@ -228,6 +230,7 @@ class EventLoop: private _::NewJobCallback {
public:
EventLoop();
~EventLoop() noexcept(false);
static EventLoop& current();
// Get the event loop for the current thread. Throws an exception if no event loop is active.
......@@ -298,6 +301,15 @@ public:
Promise<T> exclusiveJoin(Promise<T>&& promise1, Promise<T>&& promise2) const;
// 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.
......@@ -387,6 +399,8 @@ private:
Maybe<_::WorkQueue<EventJob>::JobWrapper&> insertionPoint;
// Where to insert preemptively-scheduled events into the queue.
Own<_::TaskSetImpl> daemons;
template <typename T, typename Func, typename ErrorFunc>
Own<_::PromiseNode> thereImpl(Promise<T>&& promise, Func&& func, ErrorFunc&& errorHandler) const;
// Shared implementation of there() and Promise::then().
......@@ -456,7 +470,7 @@ private:
friend class _::ChainPromiseNode;
template <typename>
friend class Promise;
friend class TaskSet;
friend class _::TaskSetImpl;
};
template <typename T>
......@@ -763,8 +777,7 @@ public:
void add(Promise<void>&& promise) const;
private:
class Impl;
Own<Impl> impl;
Own<_::TaskSetImpl> impl;
};
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