Refcounted types own their teardown: impl Drop instead of destroy hooks, RefPtr instead of raw holders, rc classes
What does this PR do?
Follow-up to #40478 (RefPtr<T> owns its ref). Now that an intrusive ref can be held as a value, the places that still kept one as a raw *mut T + hand-written ref_()/deref(), and the refcounted types that still tore themselves down through a destroy = fn that ended in heap::take(this), move to plain ownership. No behaviour change intended; per path the work done is the same as on main (a RefPtr is a NonNull whose Drop is the deref() that was there before).
impl Dropinstead of#[ref_count(destroy = …)]/ hand-written destructors for StatWatcher/Scheduler, NativeZlib/Brotli, JSMySQLConnection, PostgresSQLConnection, FileSink, FetchTasklet, LinuxMemFdAllocator, Element, Terminal, PipeReader, JSBundleCompletionTask, JSValkeyClient, dns Resolver, NewSocket, Timeout/Immediate, NodeHTTPResponse, H2FrameParser; CronJob, AttributeIterator, RefData, JSMySQLQuery just lose a redundant override. The derive's default (drop the Box) frees the allocation, so error paths and by-value owners get the same teardown.WindowsNamedPipeContext(async free),H2FrameParser(pooled slot) andResponse(weak-ref aware) keep an override.NewSocket, timers,Resolver,JSValkeyClient,Response,ServePlugins,S3UploadStreamWrapper,NodeHTTPResponseget a derive instead of a hand-rolled count..classes.tsrefCounted: truefor the 26 class definitions (31 generated classes) whose payload is refcounted: the generated finalize thunk runs an optionalfn finalize(&self)hook and then drops the wrapper's ref, instead of handing the type aBox<Self>it does not own. 11 finalize bodies disappear and the rest lose theirheap::release/finalize_js_boxpreamble; 10 more non-refcountedfinalizebodies that only dropped the Box (some afterJsRef::finalize, which drop does) fall back toJsFinalize's default;SecureContext's moves to owning itsSSL_CTX.- Raw refcounted holders →
RefPtr/ProcessHandle/AbortSignalRef/HeadersRef/OwnedSslCtx: SQL request queues (VecDeque<RefPtr<Query>>) and prepared-statement maps; HTTP-thread SSL context cache / proxy-deref queue / h2 session registry; FetchTasklet's stream buffer, nativeResponseand abort signal;FileSink::init/create(returnsRefPtr; ~15 manual derefs inBlobgo); every*mut Processholder (shell, lifecycle scripts, security scanner,bun test --parallel,--filter/multi-run, webview,Subprocess;to_process()returnsRefPtr<Process>,ProcessHandlederefs toProcess); IPCSendQueue; S3MultiPartUploadholders andStore::S3.credentials(shared instead of deep-duped); HTMLRewriter suspended wrappers / attribute iterators /Responserefs; valkey deferred-close task and timer keep-alive; h2 cork slot;WebWorkerconstruction + thread refs (RefPtr::new,into_raw()to C++);ServePlugins;AbortSignal::ref_()returnsAbortSignalRef. The h2 parser's socket attachment (install as native callback, or hold a socket ref when that slot is taken) is one owner type,NativeSocket, undone on detach/Drop. Every ownedSSL_CTXreference (create_ssl_context,SSLContextCache::get_or_create*,SecureContext.ctx(callers clone it;borrow()is gone),SSLWrapper,Listener,HTTPContext,DuplexUpgradeContext, named-pipe TLS, valkey, quic, SQL connections,RareData.default_client_ssl_ctx) is anOwnedSslCtx. - Scope keep-alives (
x.ref_(); …re-entrant…; x.deref()) become aRefPtrlocal in NodeHTTPResponse, zlib, valkey, dns, Postgres, Terminal, memfd, S3 upload stream.bun_ptr::finalize_js_box{,_noop},destroy_box_withandCellRefCounted::ref_nngo away with their last callers.
Left as-is (refs owned by FFI/uws ext slots, lock-free queues, deliberately Copy snapshots, or cross-callback ownership tokens): RequestContext (pool-allocated), Blob / ReadableStream::Source (their own count types), WindowsNamedPipeContext::SocketType, StatWatcherScheduler's intrusive queue, ProcessAutoKiller's pointer-keyed map, Process poll/waiter-thread refs, SyncWindowsProcess.process, BlockList structured-clone refs, the auto-flusher/promise/pending-write flags in NodeHTTPResponse/FileSink/StaticPipeWriter, and the timer internals' ref_()/deref() pins (their container is found by container_of).
Bugs fixed (error paths only, because the ref is now a value that drops): a Postgres query reusing a named statement whose prepare had failed leaked a statement ref and a query ref if converting that error to JS threw; a bun test --parallel worker whose spawn/watch failed leaked its Process; an h2 parser re-corked by re-entrant JS during a forced uncork no longer strands the cork slot's ref.
How did you verify your code works?
cargo check/clippy on linux + windows targets, source lints. Debug build + the suites touching each area: serve/serve-file, node-http/http2/tls, net/socket, fetch + fetch.stream/tls, spawn/child_process/shell, sql/sqlite/mysql, zlib, fs.watch/watchFile, timers, dns, valkey, S3, HTMLRewriter, image, cron, terminal, workers — passing (two pre-existing local debug-build failures, worker.test.ts message-flood and fs.watch inotify-overflow, fail identically on an unmodified build of the base commit).