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

Update multiple workerd/io files to idiomatic comment style #1067

Merged
merged 1 commit into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 64 additions & 67 deletions src/workerd/io/io-gate.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,26 @@ namespace workerd {

using kj::uint;

// An InputGate blocks incoming events from being delivered to an actor while the lock is held.
class InputGate {
// An InputGate blocks incoming events from being delivered to an actor while the lock is held.

public:
// Hooks that can be used to customize InputGate behavior.
//
// Technically, everything implemented here could be accomplished by a class that wraps
// InputGate, but the part of the code that wants to implement these hooks (Worker::Actor)
// is far away from the part of the code that calls into the InputGate (ActorCache), and so
// it was more convenient to give Worker::Actor a way to inject behavior into InputGate which
// would kick in when ActorCache tried to use it.
class Hooks {
// Hooks that can be used to customize InputGate behavior.
//
// Technically, everything implemented here could be accomplished by a class that wraps
// InputGate, but the part of the code that wants to implement these hooks (Worker::Actor)
// is far away from the part of the code that calls into the InputGate (ActorCache), and so
// it was more convenient to give Worker::Actor a way to inject behavior into InputGate which
// would kick in when ActorCache tried to use it.

public:
// Optionally track metrics. In practice these are implemented by MetricsCollector::Actor, but
// we don't want to depend on that class from here.
virtual void inputGateLocked() {}
virtual void inputGateReleased() {}
virtual void inputGateWaiterAdded() {}
virtual void inputGateWaiterRemoved() {}
// Optionally track metrics. In practice these are implemented by MetricsCollector::Actor, but
// we don't want to depend on that class from here.

static Hooks DEFAULT;
};
Expand All @@ -60,61 +60,60 @@ class InputGate {

class CriticalSection;

// A lock that blocks all new events from being delivered while it exists.
class Lock {
// A lock that blocks all new events from being delivered while it exists.

public:
KJ_DISALLOW_COPY(Lock);
Lock(Lock&& other): gate(other.gate), cs(kj::mv(other.cs)) { other.gate = nullptr; }
~Lock() noexcept(false) { if (gate != nullptr) gate->releaseLock(); }

Lock addRef() { return Lock(*gate); }
// Increments the lock's refcount, returning a duplicate `Lock`. All `Lock`s must be dropped
// before the gate is unlocked.
Lock addRef() { return Lock(*gate); }

kj::Own<CriticalSection> startCriticalSection();
// Start a new critical section from this lock. After `wait()` has been called on the returned
// critical section for the first time, no further Locks will be handed out by
// InputGate::wait() until the CriticalSection has been dropped.
//
// CriticalSections can be nested. If this Lock is itself part of a CriticalSection, the new
// CriticalSection will be nested within it and the outer CriticalSection's wait() won't
// produce a Lock again until the inner CriticalSection is dropped.
kj::Own<CriticalSection> startCriticalSection();

kj::Maybe<CriticalSection&> getCriticalSection();
// If this lock was taken in a CriticalSection, return it.
kj::Maybe<CriticalSection&> getCriticalSection();

bool isFor(const InputGate& gate) const;

inline bool operator==(const Lock& other) const { return gate == other.gate; }

private:
InputGate* gate;
// Becomes null on move.
InputGate* gate;

kj::Maybe<kj::Own<CriticalSection>> cs;

Lock(InputGate& gate);
friend class InputGate;
};

kj::Promise<Lock> wait();
// Wait until there are no `Lock`s, then create a new one and return it.
kj::Promise<Lock> wait();

kj::Promise<void> onBroken();
// Rejects if and when calls to `wait()` become broken due to a failed critical section. The
// actor should be shut down in this case. This promise never resolves, only rejects.
kj::Promise<void> onBroken();

private:
Hooks& hooks;

uint lockCount = 0;
// How many instances of `Lock` currently exist? When this reaches zero, we'll release some
// waiters.
uint lockCount = 0;

bool isCriticalSection = false;
// CriticalSection inherits InputGate for implementation convenience (since much implementation
// is shared).
bool isCriticalSection = false;

struct Waiter {
Waiter(kj::PromiseFulfiller<Lock>& fulfiller, InputGate& gate, bool isChildWaiter);
Expand All @@ -128,138 +127,136 @@ class InputGate {

kj::List<Waiter, &Waiter::link> waiters;

kj::List<Waiter, &Waiter::link> waitingChildren;
// Waiters representing CriticalSections that are ready to start. These take priority over other
// waiters.
kj::List<Waiter, &Waiter::link> waitingChildren;

// A fulfiller for onBroken(), or an exception if already broken.
kj::ForkedPromise<void> brokenPromise;
kj::OneOf<kj::Own<kj::PromiseFulfiller<void>>, kj::Exception> brokenState;
// A fulfiller for onBroken(), or an exception if already broken.

void releaseLock();

void setBroken(const kj::Exception& e);
// Called when a critical section fails. All future waiters will throw this exception.
void setBroken(const kj::Exception& e);

InputGate(Hooks& hooks, kj::PromiseFulfillerPair<void> paf);
};

// A CriticalSection is a procedure that must not be interrupted by anything "external".
// While a CriticalSection is running, all events that were not initiated by the
// CriticalSection itself will be blocked from being delivered.
//
// The difference between a Lock and a CriticalSection is that a critical section may succeed
// or fail. A failed critical section permanently breaks the input gate. Locks, on the other
// hand, are simply released when dropped.
//
// A CriticalSection itself holds a Lock, which blocks the "parent scope" from continuing
// execution until the critical section is done. Meanwhile, the code running inside the critical
// section obtains nested Locks. These nested locks control concurrency of the operations
// initiated within the critical section in the same way that input locks normally do at the
// top-level scope. E.g., if a critical section initiates a storage read and a fetch() at the
// same time, the fetch() is prevented from returning until after the storage read has returned.
class InputGate::CriticalSection: private InputGate, public kj::Refcounted {
// A CriticalSection is a procedure that must not be interrupted by anything "external".
// While a CriticalSection is running, all events that were not initiated by the
// CriticalSection itself will be blocked from being delivered.
//
// The difference between a Lock and a CriticalSection is that a critical section may succeed
// or fail. A failed critical section permanently breaks the input gate. Locks, on the other
// hand, are simply released when dropped.
//
// A CriticalSection itself holds a Lock, which blocks the "parent scope" from continuing
// execution until the critical section is done. Meanwhile, the code running inside the critical
// section obtains nested Locks. These nested locks control concurrency of the operations
// initiated within the critical section in the same way that input locks normally do at the
// top-level scope. E.g., if a critical section initiates a storage read and a fetch() at the
// same time, the fetch() is prevented from returning until after the storage read has returned.

public:
CriticalSection(InputGate& parent);
~CriticalSection() noexcept(false);

kj::Promise<Lock> wait();
// Wait for a nested lock in order to continue this CriticalSection.
//
// The first call to wait() begins the CriticalSection. After that wait completes, until the
// CriticalSection is done and dropped, no other locks will be allowed on this InputGate, except
// locks requested by calling wait() on this CriticalSection -- or one of its children.
kj::Promise<Lock> wait();

Lock succeeded();
// Call when the critical section has completed successfully. If this is not called before the
// CriticalSection is dropped, then failed() is called implicitly.
//
// Returns the input lock that was held on the parent critical section. This can be used to
// continue execution in the parent before any other input arrives.
Lock succeeded();

void failed(const kj::Exception& e);
// Call to indicate the CriticalSection has failed with the given exception. This immediately
// breaks the InputGate.
void failed(const kj::Exception& e);

private:
enum State {
NOT_STARTED,
// wait() hasn't been called.
NOT_STARTED,

INITIAL_WAIT,
// wait() has been called once, and that wait hasn't finished yet.
INITIAL_WAIT,

RUNNING,
// First lock has been obtained, waiting for success() or failed().
RUNNING,

REPARENTED
// success() or failed() has been called.
REPARENTED
};

State state = NOT_STARTED;

kj::OneOf<InputGate*, kj::Own<CriticalSection>> parent;
// Points to the parent scope, which may be another CriticalSection in the case of nesting.
kj::OneOf<InputGate*, kj::Own<CriticalSection>> parent;

kj::Maybe<Lock> parentLock;
// A lock in the parent scope. `parentLock` becomes non-null after the first lock is obtained,
// and becomes null again when succeeded() is called.
kj::Maybe<Lock> parentLock;

friend class InputGate;

InputGate& parentAsInputGate();
// Return a reference for the parent scope, skipping any reparented CriticalSections
InputGate& parentAsInputGate();
};

// An OutputGate blocks outgoing messages from an Actor until writes which they might depend on
// are confirmed.
class OutputGate {
// An OutputGate blocks outgoing messages from an Actor until writes which they might depend on
// are confirmed.

public:
// Hooks that can be used to customize OutputGate behavior.
//
// Technically, everything implemented here could be accomplished by a class that wraps
// OutputGate, but the part of the code that wants to implement these hooks (Worker::Actor)
// is far away from the part of the code that calls into the OutputGate (ActorCache), and so
// it was more convenient to give Worker::Actor a way to inject behavior into OutputGate which
// would kick in when ActorCache tried to use it.
class Hooks {
// Hooks that can be used to customize OutputGate behavior.
//
// Technically, everything implemented here could be accomplished by a class that wraps
// OutputGate, but the part of the code that wants to implement these hooks (Worker::Actor)
// is far away from the part of the code that calls into the OutputGate (ActorCache), and so
// it was more convenient to give Worker::Actor a way to inject behavior into OutputGate which
// would kick in when ActorCache tried to use it.

public:
virtual kj::Promise<void> makeTimeoutPromise() { return kj::NEVER_DONE; }
// Optionally make a promise which should be exclusiveJoin()ed with the lock promise to
// implement a timeout. The returned promise should be something that throws an exception
// after some timeout has expired.
virtual kj::Promise<void> makeTimeoutPromise() { return kj::NEVER_DONE; }

// Optionally track metrics. In practice these are implemented by MetricsCollector::Actor, but
// we don't want to depend on that class from here.

virtual void outputGateLocked() {}
virtual void outputGateReleased() {}
virtual void outputGateWaiterAdded() {}
virtual void outputGateWaiterRemoved() {}
// Optionally track metrics. In practice these are implemented by MetricsCollector::Actor, but
// we don't want to depend on that class from here.

static Hooks DEFAULT;
};

OutputGate(Hooks& hooks = Hooks::DEFAULT);
~OutputGate() noexcept(false);

template <typename T>
kj::Promise<T> lockWhile(kj::Promise<T> promise);
// Block all future `wait()` calls until `promise` completes. Returns a wrapper around `promise`.
// If `promise` rejects, the exception will propagate to all future `wait()`s. If the returned
// promise is canceled before completion, all future `wait()`s will also throw.
template <typename T>
kj::Promise<T> lockWhile(kj::Promise<T> promise);

kj::Promise<void> wait();
// Wait until all preceding locks are released. The wait will not be affected by any future
// call to `lockWhile()`.
kj::Promise<void> wait();

kj::Promise<void> onBroken();
// Rejects if and when calls to `wait()` become broken due to a failed lockWhile(). The actor
// should be shut down in this case. This promise never resolves, only rejects.
//
// This method can only be called once.
kj::Promise<void> onBroken();

bool isBroken();

Expand All @@ -268,8 +265,8 @@ class OutputGate {

kj::ForkedPromise<void> pastLocksPromise;

kj::OneOf<kj::Own<kj::PromiseFulfiller<void>>, kj::Exception> brokenState;
// A fulfiller for onBroken(), or an exception if already broken.
kj::OneOf<kj::Own<kj::PromiseFulfiller<void>>, kj::Exception> brokenState;

void setBroken(const kj::Exception& e);

Expand Down
Loading
Loading