Skip to content

Commit a99ab15

Browse files
authored
Merge pull request #6637 from cloudflare/erikcorry/websocket-autoresponse
Websockets: Wrap the ongoingAutoResponse so it gets destructed on the…
2 parents e5e9cf4 + e06791f commit a99ab15

2 files changed

Lines changed: 33 additions & 6 deletions

File tree

src/workerd/api/web-socket.c++

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,14 @@ void WebSocket::serializeAttachment(jsg::Lock& js, jsg::JsValue attachment) {
782782
void WebSocket::setAutoResponseStatus(
783783
kj::Maybe<kj::Date> time, kj::Promise<void> autoResponsePromise) {
784784
autoResponseTimestamp = time;
785-
autoResponseStatus.ongoingAutoResponse = kj::mv(autoResponsePromise);
785+
KJ_IF_SOME(context, IoContext::tryCurrent()) {
786+
autoResponseStatus.ongoingAutoResponse.emplace(
787+
context.addObject(kj::heap(kj::mv(autoResponsePromise))));
788+
} else {
789+
// Called outside an IoContext (e.g. from the hibernation manager's readLoop).
790+
// Use plain kj::Own; the caller manages the promise lifecycle.
791+
autoResponseStatus.ongoingAutoResponse.emplace(kj::heap(kj::mv(autoResponsePromise)));
792+
}
786793
}
787794

788795
kj::Maybe<kj::Date> WebSocket::getAutoResponseTimestamp() {
@@ -860,9 +867,14 @@ kj::Promise<void> WebSocket::sendAutoResponse(kj::String message, kj::WebSocket&
860867
autoResponseStatus.pendingAutoResponseDeque.push(kj::mv(message));
861868
} else if (!autoResponseStatus.isClosed) {
862869
auto p = ws.send(message).fork();
863-
autoResponseStatus.ongoingAutoResponse = p.addBranch();
870+
KJ_IF_SOME(context, IoContext::tryCurrent()) {
871+
autoResponseStatus.ongoingAutoResponse.emplace(context.addObject(kj::heap(p.addBranch())));
872+
} else {
873+
// Called outside an IoContext (e.g. from the hibernation manager's readLoop).
874+
autoResponseStatus.ongoingAutoResponse.emplace(kj::heap(p.addBranch()));
875+
}
864876
co_await p;
865-
autoResponseStatus.ongoingAutoResponse = kj::READY_NOW;
877+
autoResponseStatus.ongoingAutoResponse = kj::none;
866878
}
867879
}
868880

@@ -928,8 +940,17 @@ kj::Promise<void> WebSocket::pump(IoContext& context,
928940

929941
// If we have a ongoingAutoResponse, we must co_await it here because there's a ws.send()
930942
// in progress. Otherwise there can occur ws.send() race problems.
931-
co_await autoResponse.ongoingAutoResponse;
932-
autoResponse.ongoingAutoResponse = kj::READY_NOW;
943+
KJ_IF_SOME(promiseHolder, autoResponse.ongoingAutoResponse) {
944+
KJ_SWITCH_ONEOF(promiseHolder) {
945+
KJ_CASE_ONEOF(ioOwned, IoOwn<kj::Promise<void>>) {
946+
co_await *ioOwned;
947+
}
948+
KJ_CASE_ONEOF(owned, kj::Own<kj::Promise<void>>) {
949+
co_await *owned;
950+
}
951+
}
952+
autoResponse.ongoingAutoResponse = kj::none;
953+
}
933954

934955
do {
935956
while (outgoingMessages.size() > 0) {

src/workerd/api/web-socket.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,13 @@ class WebSocket: public EventTarget {
592592
// Keep track of current hibernatable websockets auto-response status to avoid racing
593593
// between regular websocket messages, and auto-responses.
594594
struct AutoResponse {
595-
kj::Promise<void> ongoingAutoResponse = kj::READY_NOW;
595+
// The ongoing auto-response promise, used for pump() synchronization.
596+
// Wrapped in IoOwn when an IoContext is available (for GC-safe destruction that avoids
597+
// violating DISALLOW_KJ_IO_DESTRUCTORS_SCOPE), or plain kj::Own when no IoContext is
598+
// available (e.g. when called from the hibernation manager's readLoop).
599+
using OwnedAutoResponsePromise =
600+
kj::OneOf<IoOwn<kj::Promise<void>>, kj::Own<kj::Promise<void>>>;
601+
kj::Maybe<OwnedAutoResponsePromise> ongoingAutoResponse;
596602
workerd::util::Queue<kj::String> pendingAutoResponseDeque;
597603
size_t queuedAutoResponses = 0;
598604
bool isPumping = false;

0 commit comments

Comments
 (0)