Skip to content

Commit

Permalink
Update multiple workerd/io files to idiomatic comment style (#1067)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasnell committed Aug 25, 2023
1 parent 4654ae8 commit ab8e251
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 226 deletions.
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

0 comments on commit ab8e251

Please sign in to comment.