Skip to content

Commit 5fdf66e

Browse files
committed
rtc: fail fast instead of hanging when FFI is used across fork()
The native runtime (tokio worker/IO threads in liblivekit_ffi) does not survive fork() and cannot be rebuilt by re-initializing, so a child that inherited an initialized FfiClient would hang on every request. Guard request() with the owning pid and raise a clear error instead. The atexit dispose handler is likewise skipped in fork children (it needs the runtime and would hang at exit). Also move the drop_handle side effect out of an assert so it is not stripped under python -O.
1 parent 3f92f3c commit 5fdf66e

1 file changed

Lines changed: 15 additions & 1 deletion

File tree

livekit-rtc/livekit/rtc/_ffi_client.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ def disposed(self) -> bool:
8383
def dispose(self) -> None:
8484
if self.handle != INVALID_HANDLE and not self._disposed:
8585
self._disposed = True
86-
assert FfiClient.instance._ffi_lib.livekit_ffi_drop_handle(ctypes.c_uint64(self.handle))
86+
ffi = FfiClient._instance
87+
if ffi is None or ffi._pid != os.getpid():
88+
return
89+
dropped = ffi._ffi_lib.livekit_ffi_drop_handle(ctypes.c_uint64(self.handle))
90+
assert dropped
8791

8892
def __repr__(self) -> str:
8993
return f"FfiHandle({self.handle})"
@@ -218,6 +222,7 @@ def instance(cls) -> "FfiClient":
218222
return cls._instance
219223

220224
def __init__(self) -> None:
225+
self._pid = os.getpid()
221226
self._lock = threading.RLock()
222227
self._queue = FfiQueue[proto_ffi.FfiEvent]()
223228

@@ -253,16 +258,25 @@ def __init__(self) -> None:
253258
)
254259

255260
ffi_lib = self._ffi_lib
261+
init_pid = self._pid
256262

257263
@atexit.register
258264
def _dispose_lk_ffi() -> None:
265+
if os.getpid() != init_pid:
266+
return
259267
ffi_lib.livekit_ffi_dispose()
260268

261269
@property
262270
def queue(self) -> FfiQueue[proto_ffi.FfiEvent]:
263271
return self._queue
264272

265273
def request(self, req: proto_ffi.FfiRequest) -> proto_ffi.FfiResponse:
274+
if self._pid != os.getpid():
275+
raise RuntimeError(
276+
"livekit.rtc was used in a parent process before fork(); the native "
277+
"runtime cannot be used across fork(). Do not create or connect a Room "
278+
"(or use any livekit.rtc object) before forking child processes."
279+
)
266280
proto_data = req.SerializeToString()
267281
proto_len = len(proto_data)
268282
data = (ctypes.c_ubyte * proto_len)(*proto_data)

0 commit comments

Comments
 (0)