From 7ea5cedd2470532ef92131dc9853f7642952249f 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++ | 6 +++--- src/workerd/io/io-context.h | 6 ++++++ 9 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/workerd/api/basics.c++ b/src/workerd/api/basics.c++ index b592e3b3ed5..fcda87280f4 100644 --- a/src/workerd/api/basics.c++ +++ b/src/workerd/api/basics.c++ @@ -824,7 +824,7 @@ void AbortSignal::serialize(jsg::Lock& js, jsg::Serializer& serializer) { auto& ioContext = IoContext::current(); // Keep track of every AbortSignal cloned from this one. // If this->triggerAbort(...) is called, each rpcClient will be informed. - rpcClients.add(ioContext.addObject(kj::heap(kj::mv(triggerCap)))); + rpcClients.add(ioContext.createObject(kj::mv(triggerCap))); } jsg::Ref AbortSignal::deserialize( diff --git a/src/workerd/api/container.c++ b/src/workerd/api/container.c++ index 51a5c906b07..8735a82bbb7 100644 --- a/src/workerd/api/container.c++ +++ b/src/workerd/api/container.c++ @@ -466,7 +466,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) { @@ -1487,7 +1487,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 5d3bcc3a915..e282aef43af 100644 --- a/src/workerd/api/sockets.c++ +++ b/src/workerd/api/sockets.c++ @@ -983,7 +983,7 @@ jsg::Ref Socket::deserialize( // 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 3745d91dfe8..bd3c6c748b1 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 098fce96948..93d1fd14b71 100644 --- a/src/workerd/api/web-socket.c++ +++ b/src/workerd/api/web-socket.c++ @@ -279,7 +279,7 @@ LegacyWebSocketAdapter::LegacyWebSocketAdapter(jsg::Lock& js, ws, kj::mv(KJ_REQUIRE_NONNULL(package.maybeTags)), package.closedOutgoingConnection)), - outgoingMessages(IoContext::current().addObject(kj::heap())) {} + outgoingMessages(IoContext::current().createObject()) {} // 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"! @@ -292,7 +292,7 @@ LegacyWebSocketAdapter::LegacyWebSocketAdapter( : BinaryType::ARRAYBUFFER), allowHalfOpen(!FeatureFlags::get(js).getWebSocketAutoReplyToClose()), farNative(nullptr), - outgoingMessages(IoContext::current().addObject(kj::heap())) { + outgoingMessages(IoContext::current().createObject()) { auto nativeObj = kj::heap(); nativeObj->state.init(kj::mv(native)); farNative = IoContext::current().addObject(kj::mv(nativeObj)); @@ -305,7 +305,7 @@ LegacyWebSocketAdapter::LegacyWebSocketAdapter(jsg::Lock& js, WebSocket& shell, : BinaryType::ARRAYBUFFER), allowHalfOpen(!FeatureFlags::get(js).getWebSocketAutoReplyToClose()), farNative(nullptr), - outgoingMessages(IoContext::current().addObject(kj::heap())) { + outgoingMessages(IoContext::current().createObject()) { auto nativeObj = kj::heap(); nativeObj->state.init(); farNative = IoContext::current().addObject(kj::mv(nativeObj)); diff --git a/src/workerd/io/io-context.h b/src/workerd/io/io-context.h index d1f41acf866..e8e6212ee1e 100644 --- a/src/workerd/io/io-context.h +++ b/src/workerd/io/io-context.h @@ -757,6 +757,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