From 736c8a6a516c0178ecd98cabe2c0296940d9ac55 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 21 Aug 2026 09:11:33 -0700 Subject: [PATCH] Add ioContext.createObject Minor quality of life improvement... since `ioContext.addObject(kj::heap(...))` is rather common, save ourselves some extra steps with a `ioContext.createObject(...)` shortcut. --- src/workerd/api/basics.c++ | 2 +- src/workerd/api/container.c++ | 4 ++-- src/workerd/api/sockets.c++ | 2 +- src/workerd/api/sockets.h | 4 ++-- src/workerd/api/sql.c++ | 2 +- src/workerd/api/streams/internal.h | 2 +- src/workerd/api/sync-kv.c++ | 2 +- src/workerd/api/web-socket.c++ | 13 +++++++------ src/workerd/io/io-context.h | 6 ++++++ 9 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/workerd/api/basics.c++ b/src/workerd/api/basics.c++ index de888876359..d076b42babd 100644 --- a/src/workerd/api/basics.c++ +++ b/src/workerd/api/basics.c++ @@ -1148,7 +1148,7 @@ void AbortSignal::serialize(jsg::Lock& js, jsg::Serializer& serializer) { // Keep track of every AbortSignal cloned from this one. // If this->triggerAbort(...) is called, each clone will be informed. rpcRegistrations.add(kj::arc(ioContext.getCrossContextExecutor(), - ioContext.addObject(kj::heap(kj::mv(triggerCap))))); + ioContext.createObject(kj::mv(triggerCap)))); } jsg::Ref AbortSignal::deserialize( diff --git a/src/workerd/api/container.c++ b/src/workerd/api/container.c++ index 3f5c5302c09..e6b5a9b56e5 100644 --- a/src/workerd/api/container.c++ +++ b/src/workerd/api/container.c++ @@ -460,7 +460,7 @@ void Container::startMonitor() { }).fork(); currentMonitor = - IoContext::current().addObject(kj::heap(kj::mv(monitor), ++nextMonitorGeneration)); + IoContext::current().createObject(kj::mv(monitor), ++nextMonitorGeneration); } jsg::Promise Container::setLabels(jsg::Lock& js, jsg::Dict labels) { @@ -1481,7 +1481,7 @@ jsg::Ref Container::getTcpPort(jsg::Lock& js, int port) { auto portState = [&]() -> kj::Rc { if (util::Autogate::isEnabled(util::AutogateKey::CONTAINER_TUNNEL_REUSE)) { if (tcpPortStates == kj::none) { - tcpPortStates = ioctx.addObject(kj::heap>>()); + tcpPortStates = ioctx.createObject>>(); } auto& states = *KJ_ASSERT_NONNULL(tcpPortStates); diff --git a/src/workerd/api/sockets.c++ b/src/workerd/api/sockets.c++ index d9defde8ed9..522591e5291 100644 --- a/src/workerd/api/sockets.c++ +++ b/src/workerd/api/sockets.c++ @@ -1009,7 +1009,7 @@ jsg::Ref hydrateRpcSocket(jsg::Lock& js, // socket is being torn down, so there is nothing to wire up). The wiring runs JS that can throw // with no user code on the stack, so each body is wrapped in JSG_TRY/JSG_CATCH and reports via // js.reportError() (matching the queueMicrotask() pattern in global-scope.c++). - auto disconnectedOwn = ioContext.addObject(kj::heap>(kj::mv(disconnected))); + auto disconnectedOwn = ioContext.createObject>(kj::mv(disconnected)); js.v8Context()->GetMicrotaskQueue()->EnqueueMicrotask(js.v8Isolate, js.wrapSimpleFunction(js.v8Context(), JSG_VISITABLE_LAMBDA((self = socket.addRef(), disconnected = kj::mv(disconnectedOwn)), diff --git a/src/workerd/api/sockets.h b/src/workerd/api/sockets.h index 88c22d668e3..ef232c8e477 100644 --- a/src/workerd/api/sockets.h +++ b/src/workerd/api/sockets.h @@ -75,8 +75,8 @@ class Socket: public jsg::Object { kj::Maybe domain, bool isDefaultFetchPort, jsg::PromiseResolverPair openedPrPair) - : connectionData(context.addObject(kj::heap( - kj::mv(tlsStarter), kj::mv(connectionStream), kj::mv(watchForDisconnectTask)))), + : connectionData(context.createObject( + kj::mv(tlsStarter), kj::mv(connectionStream), kj::mv(watchForDisconnectTask))), readable(kj::mv(readableParam)), writable(kj::mv(writable)), closedResolver(kj::mv(closedPrPair.resolver)), diff --git a/src/workerd/api/sql.c++ b/src/workerd/api/sql.c++ index f5acd309608..e0ee68b5d5f 100644 --- a/src/workerd/api/sql.c++ +++ b/src/workerd/api/sql.c++ @@ -25,7 +25,7 @@ static constexpr uint SQL_STATEMENT_CACHE_MAX_SIZE = 1024 * 1024; SqlStorage::SqlStorage(jsg::Ref storage) : storage(kj::mv(storage)), - statementCache(IoContext::current().addObject(kj::heap())) {} + statementCache(IoContext::current().createObject()) {} SqlStorage::~SqlStorage() {} diff --git a/src/workerd/api/streams/internal.h b/src/workerd/api/streams/internal.h index bc27a53838f..2cdd9affc6d 100644 --- a/src/workerd/api/streams/internal.h +++ b/src/workerd/api/streams/internal.h @@ -210,7 +210,7 @@ class WritableStreamInternalController: public WritableStreamController { kj::Maybe maybeHighWaterMark = kj::none, kj::Maybe> maybeClosureWaitable = kj::none) : state(State::create>( - IoContext::current().addObject(kj::heap(kj::mv(writable))))), + IoContext::current().createObject(kj::mv(writable)))), observer(kj::mv(observer)), maybeHighWaterMark(maybeHighWaterMark), maybeClosureWaitable(kj::mv(maybeClosureWaitable)) {} diff --git a/src/workerd/api/sync-kv.c++ b/src/workerd/api/sync-kv.c++ index 816d2f4618c..22a394ede15 100644 --- a/src/workerd/api/sync-kv.c++ +++ b/src/workerd/api/sync-kv.c++ @@ -77,7 +77,7 @@ jsg::Ref SyncKvStorage::list( KJ_UNWRAP_OR(DurableObjectStorageOperations::compileListOptions(asyncOptions), { // Key range is empty. Return empty map. return js.alloc( - IoContext::current().addObject(kj::heap(nullptr))); + IoContext::current().createObject(nullptr)); }); auto cursor = sqliteKv.list(start, end, limit, reverse ? SqliteKv::REVERSE : SqliteKv::FORWARD) diff --git a/src/workerd/api/web-socket.c++ b/src/workerd/api/web-socket.c++ index e06784d7c4e..8bf81d2fab7 100644 --- a/src/workerd/api/web-socket.c++ +++ b/src/workerd/api/web-socket.c++ @@ -307,11 +307,12 @@ LegacyWebSocketAdapter::LegacyWebSocketAdapter(jsg::Lock& js, ws, kj::mv(KJ_REQUIRE_NONNULL(package.maybeTags)), package.closedOutgoingConnection)), - outgoingMessages(IoContext::current().addObject(kj::heap())), - autoResponseStatusOwner(ioContext.addObject(kj::heap())), + outgoingMessages(ioContext.createObject()), + autoResponseStatusOwner(ioContext.createObject()), autoResponseStatus(*autoResponseStatusOwner) { autoResponseStatus.isClosed = farNative->closedOutgoing; } + // This constructor is used when reinstantiating a websocket that had been hibernating, which is // why we can go straight to the Accepted state. However, note that we are actually in the // `Hibernatable` "sub-state"! @@ -324,8 +325,8 @@ LegacyWebSocketAdapter::LegacyWebSocketAdapter( : BinaryType::ARRAYBUFFER), allowHalfOpen(!FeatureFlags::get(js).getWebSocketAutoReplyToClose()), farNative(nullptr), - outgoingMessages(IoContext::current().addObject(kj::heap())), - autoResponseStatusOwner(IoContext::current().addObject(kj::heap())), + outgoingMessages(IoContext::current().createObject()), + autoResponseStatusOwner(IoContext::current().createObject()), autoResponseStatus(*autoResponseStatusOwner) { auto nativeObj = kj::heap(); nativeObj->state.init(kj::mv(native)); @@ -339,8 +340,8 @@ LegacyWebSocketAdapter::LegacyWebSocketAdapter(jsg::Lock& js, WebSocket& shell, : BinaryType::ARRAYBUFFER), allowHalfOpen(!FeatureFlags::get(js).getWebSocketAutoReplyToClose()), farNative(nullptr), - outgoingMessages(IoContext::current().addObject(kj::heap())), - autoResponseStatusOwner(IoContext::current().addObject(kj::heap())), + outgoingMessages(IoContext::current().createObject()), + autoResponseStatusOwner(IoContext::current().createObject()), autoResponseStatus(*autoResponseStatusOwner) { auto nativeObj = kj::heap(); nativeObj->state.init(); diff --git a/src/workerd/io/io-context.h b/src/workerd/io/io-context.h index 532229c7e0b..5099aa48677 100644 --- a/src/workerd/io/io-context.h +++ b/src/workerd/io/io-context.h @@ -764,6 +764,12 @@ class IoContext final: public kj::Refcounted, private kj::TaskSet::ErrorHandler template IoOwn addObject(kj::Rc obj); + // Shortcut for addObject(kj::heap(...)) to avoid having to write kj::heap() in every call site. + template + IoOwn createObject(Params&&... params) { + return addObject(kj::heap(kj::fwd(params)...)); + } + // Like addObject() but takes a functor, returning a functor which holds the original functor // under an `IoOwn`, and so will stop working if the IoContext is no longer valid. This is // particularly useful for passing to `jsg::Promise::then()` when you need the continuation to