Skip to content

Commit

Permalink
Add AlarmInvocationInfo jsg::Object to alarm handler.
Browse files Browse the repository at this point in the history
  • Loading branch information
jqmmes committed Feb 26, 2024
1 parent 11cb4ce commit 5ba2b7f
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 9 deletions.
5 changes: 3 additions & 2 deletions src/workerd/api/global-scope.c++
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ void ServiceWorkerGlobalScope::startScheduled(
kj::Promise<WorkerInterface::AlarmResult> ServiceWorkerGlobalScope::runAlarm(
kj::Date scheduledTime,
kj::Duration timeout,
uint32_t retryNumber,
Worker::Lock& lock, kj::Maybe<ExportedHandler&> exportedHandler) {

auto& context = IoContext::current();
Expand All @@ -388,7 +389,7 @@ kj::Promise<WorkerInterface::AlarmResult> ServiceWorkerGlobalScope::runAlarm(
auto& alarm = KJ_ASSERT_NONNULL(handler.alarm);

return context
.run([exportedHandler, &context, timeout, &alarm,
.run([exportedHandler, &context, timeout, retryNumber, &alarm,
maybeAsyncContext = jsg::AsyncContextFrame::currentRef(lock)]
(Worker::Lock& lock) mutable -> kj::Promise<WorkerInterface::AlarmResult> {
jsg::AsyncContextFrame::Scope asyncScope(lock, maybeAsyncContext);
Expand All @@ -415,7 +416,7 @@ kj::Promise<WorkerInterface::AlarmResult> ServiceWorkerGlobalScope::runAlarm(
};
});

return alarm(lock).then([]() -> kj::Promise<WorkerInterface::AlarmResult> {
return alarm(lock, jsg::alloc<AlarmInvocationInfo>(retryNumber)).then([]() -> kj::Promise<WorkerInterface::AlarmResult> {
return WorkerInterface::AlarmResult {
.retry = false,
.outcome = EventOutcome::OK
Expand Down
23 changes: 21 additions & 2 deletions src/workerd/api/global-scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,23 @@ class ExecutionContext: public jsg::Object {
}
};

// AlarmEventInfo is a jsg::Object used to pass alarm invocation info to an alarm handler.
class AlarmInvocationInfo: public jsg::Object {
public:
AlarmInvocationInfo(uint32_t number): retryNumber(number) {}

bool getIsRetry() { return retryNumber > 0; }
uint32_t getRetryNumber() { return retryNumber; }

JSG_RESOURCE_TYPE(AlarmInvocationInfo) {
JSG_READONLY_INSTANCE_PROPERTY(isRetry, getIsRetry);
JSG_READONLY_INSTANCE_PROPERTY(retryNumber, getRetryNumber);
}

private:
uint32_t retryNumber = 0;
};

// Type signature for handlers exported from the root module.
//
// We define each handler method as a LenientOptional rather than as a plain Optional in order to
Expand All @@ -202,7 +219,7 @@ struct ExportedHandler {
jsg::Ref<ScheduledController> controller, jsg::Value env, jsg::Optional<jsg::Ref<ExecutionContext>> ctx);
jsg::LenientOptional<jsg::Function<ScheduledHandler>> scheduled;

typedef kj::Promise<void> AlarmHandler();
typedef kj::Promise<void> AlarmHandler(jsg::Ref<AlarmInvocationInfo> alarmInfo);
// Alarms are only exported on DOs, which receive env bindings from the constructor
jsg::LenientOptional<jsg::Function<AlarmHandler>> alarm;

Expand Down Expand Up @@ -313,6 +330,7 @@ class ServiceWorkerGlobalScope: public WorkerGlobalScope {
kj::Promise<WorkerInterface::AlarmResult> runAlarm(
kj::Date scheduledTime,
kj::Duration timeout,
uint32_t retryNumber,
Worker::Lock& lock, kj::Maybe<ExportedHandler&> exportedHandler);

// Received test() (called from C++, not JS). See WorkerInterface::test(). This version returns
Expand Down Expand Up @@ -691,6 +709,7 @@ class ServiceWorkerGlobalScope: public WorkerGlobalScope {
api::ServiceWorkerGlobalScope::StructuredCloneOptions, \
api::PromiseRejectionEvent, \
api::Navigator, \
api::Performance
api::Performance, \
api::AlarmInvocationInfo
// The list of global-scope.h types that are added to worker.c++'s JSG_DECLARE_ISOLATE_TYPE
} // namespace workerd::api
10 changes: 5 additions & 5 deletions src/workerd/io/worker-entrypoint.c++
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ private:
kj::Promise<T> maybeAddGcPassForTest(IoContext& context, kj::Promise<T> promise);

kj::Promise<WorkerEntrypoint::AlarmResult> runAlarmImpl(
kj::Own<IoContext::IncomingRequest> incomingRequest, kj::Date scheduledTime);
kj::Own<IoContext::IncomingRequest> incomingRequest, kj::Date scheduledTime, uint32_t retryNumber);

public: // For kj::heap() only; pretend this is private.
WorkerEntrypoint(kj::Badge<WorkerEntrypoint> badge,
Expand Down Expand Up @@ -502,7 +502,7 @@ kj::Promise<WorkerInterface::ScheduledResult> WorkerEntrypoint::runScheduled(
}

kj::Promise<WorkerInterface::AlarmResult> WorkerEntrypoint::runAlarmImpl(
kj::Own<IoContext::IncomingRequest> incomingRequest, kj::Date scheduledTime) {
kj::Own<IoContext::IncomingRequest> incomingRequest, kj::Date scheduledTime, uint32_t retryNumber) {
// We want to de-duplicate alarm requests as follows:
// - An alarm must not be canceled once it is running, UNLESS the whole actor is shut down.
// - If multiple alarm invocations arrive with the same scheduled time, we only run one.
Expand Down Expand Up @@ -551,7 +551,7 @@ kj::Promise<WorkerInterface::AlarmResult> WorkerEntrypoint::runAlarmImpl(

try {
auto result = co_await context.run(
[scheduledTime, entrypointName=entrypointName, &context](Worker::Lock& lock){
[scheduledTime, retryNumber, entrypointName=entrypointName, &context](Worker::Lock& lock){
jsg::AsyncContextFrame::StorageScope traceScope = context.makeAsyncTraceScope(lock);

// If we have an invalid timeout, set it to the default value of 15 minutes.
Expand All @@ -562,7 +562,7 @@ kj::Promise<WorkerInterface::AlarmResult> WorkerEntrypoint::runAlarmImpl(
}

auto handler = lock.getExportedHandler(entrypointName, context.getActor());
return lock.getGlobalScope().runAlarm(scheduledTime, timeout, lock, handler);
return lock.getGlobalScope().runAlarm(scheduledTime, timeout, retryNumber, lock, handler);
});

// The alarm handler was successfully complete. We must guarantee this same alarm does not
Expand Down Expand Up @@ -607,7 +607,7 @@ kj::Promise<WorkerInterface::AlarmResult> WorkerEntrypoint::runAlarm(
this->incomingRequest = kj::none;

auto& context = incomingRequest->getContext();
auto promise = runAlarmImpl(kj::mv(incomingRequest), scheduledTime);
auto promise = runAlarmImpl(kj::mv(incomingRequest), scheduledTime, retryNumber);
return maybeAddGcPassForTest(context, kj::mv(promise));
}

Expand Down

0 comments on commit 5ba2b7f

Please sign in to comment.