Skip to content

CudaContext lifecycle memory fixes - #1395

Merged
alan-george-lk merged 5 commits into
mainfrom
alan/bugfix-cuda-context-lifecycle
Sep 8, 2026
Merged

CudaContext lifecycle memory fixes#1395
alan-george-lk merged 5 commits into
mainfrom
alan/bugfix-cuda-context-lifecycle

Conversation

@alan-george-lk

@alan-george-lk alan-george-lk commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

This PR addresses two issues related to memory lifecycle/safety: CudaContext and FfiServer cleanup on dispose.

CudaContext:

  • Prior to this PR, users could inadvertently re-initialize memory via CudaContext multiple times
  • Similarly, the decoders that initialized the context never destroyed it during their destructors
  • This addresses both issues and adds thread safety and debug logs

FfiServer:

  • Adds proper cleanup to dispose()
  • Adds tests around this

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Changeset ✓

This PR includes a changeset covering all affected packages:

Package Bump
libwebrtc patch
livekit patch
livekit-ffi patch
webrtc-sys patch

Comment thread livekit-ffi/src/server/mod.rs
// impls can call drop_handle without re-entering DashMap::clear().
*self.config.lock() = None;
self.handle_dropped_txs.clear();
let leftover: Vec<_> = self.ffi_handles.iter().map(|entry| *entry.key()).collect();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust novice question: is this the cleanest way to grab remaining handles before iterating? Feels a bit complex

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works, but you should be able to avoid the call to collect (which allocates) and just consume the iterator directly using the for loop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appreciate the suggestion, although I changed it and the test that covers this now deadlocks. I'll keep the original code since it seems to be stable.

Comment thread webrtc-sys/src/nvidia/cuda_context.cpp
void CudaContext::Shutdown() {
// Shutdown CUDA context
std::lock_guard<std::mutex> lock(cudaMutex());
if (ref_count_ == 0) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refcount kind of sucks, but is needed because both the encoder/decoder factories reference this.

// impls can call drop_handle without re-entering DashMap::clear().
*self.config.lock() = None;
self.handle_dropped_txs.clear();
let leftover: Vec<_> = self.ffi_handles.iter().map(|entry| *entry.key()).collect();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works, but you should be able to avoid the call to collect (which allocates) and just consume the iterator directly using the for loop.

@alan-george-lk alan-george-lk changed the title WIP: Memory leak fix for cuda_context CudaContext lifecycle memory fixes Sep 4, 2026
@alan-george-lk
alan-george-lk marked this pull request as ready for review September 4, 2026 16:06
devin-ai-integration[bot]

This comment was marked as resolved.

@xianshijing-lk xianshijing-lk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, one question

let existed = self.ffi_handles.remove(&id).is_some();
self.handle_dropped_txs.remove(&id);
if !existed {
if !existed && self.is_setup() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curiously, why self.is_setup() is needed here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a misleading warning log that emits during shutdown sometimes “Attempted to drop unknown FFI handle”, where this code can be re-entered (valid code path). This was just an easy fix to no-op / prevent that log in that case

@alan-george-lk
alan-george-lk force-pushed the alan/bugfix-cuda-context-lifecycle branch from e5fe5da to 19dd157 Compare September 4, 2026 18:16

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

🐛 1 issue in files not directly in the diff

🐛 Closed rooms retain WebRTC resources

inner.clone() creates a reference cycle for every connected room. The shared manager stores this callback in on_state_changed, while RoomSession owns that manager. Dropping a closed room retains its RTC engine and WebRTC factories indefinitely.

Devin Review

@alan-george-lk
alan-george-lk force-pushed the alan/bugfix-cuda-context-lifecycle branch from 8b7f66c to 92e7203 Compare September 8, 2026 13:58
@alan-george-lk
alan-george-lk merged commit d6a3a34 into main Sep 8, 2026
23 checks passed
@alan-george-lk
alan-george-lk deleted the alan/bugfix-cuda-context-lifecycle branch September 8, 2026 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants