Skip to content

[Bug]: Expo Device Hub retains simulator capture sessions without clients and uses ~200% CPU #16614

Description

@PrinceJunkie

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

The original trigger has not been reproduced from a fresh launch. This sequence follows the affected session history:

  1. Run T3 Code Alpha 0.0.45 on Apple Silicon with its pinned Expo Device Hub 0.12.0.
  2. Have an agent open iOS simulator previews. The affected hub retained two simulator capture sessions.
  3. Leave the simulators booted, close or leave the previews, and continue using T3 Code.
  4. After extended use, inspect the Expo Device Hub child process rather than only the desktop renderer.
  5. Check whether capture counters continue advancing and CPU stays high after all preview clients disconnect.

An agent used a device preview earlier in my session. Overall application activity when I first noticed the issue is uncertain. During inspection, the selected chat was paused and displayed a file panel instead of a device preview. The hub had been running for about 12 hours; this is process uptime, not the measured duration of high CPU usage.

Expected behavior

Native device capture and its timers should stop or suspend when the last preview consumer leaves, and restart when needed. The hub should consume little CPU when no preview clients are connected.

Actual behavior

The CPU consumer is T3 Code's bundled Expo Device Hub, running under the Electron executable in Node mode. Seven observations over approximately 31 seconds (00:32:48–00:33:19 CEST) measured 184.7–198.9% CPU, averaging 192.8%. On macOS, 100% is one CPU core. The hub accumulated about 60 seconds of CPU time in this window.

Process PID CPU range Mean CPU
Expo Device Hub 5673 184.7–198.9% 192.8%
T3 desktop main 18553 0.0–0.1% 0.03%
T3 server 18637 0.1–0.4% 0.20%
Main renderer 18636 0.0–0.3% 0.17%
GPU helper 18556 0.4–0.7% 0.54%
Agent Device daemon 5790 0.0% 0.0%

Repeated lsof observations showed only a listening TCP socket on 127.0.0.1:53853, with no established connections. The hub had two existing simulator state records; both capture statistics reported zero WebRTC sessions. A later snapshot at 00:36:40 showed 120.8% CPU, still with no connections, so the load fluctuates.

Reads of the existing sessions' /webrtc/stats endpoints at 00:34:42 and 00:34:48 showed continued work without new frames:

Capture session Additional poll ticks and surface selections Surface selection time Mean per selection New screen or idle frames
Simulator 1 14 4,220.62 ms 301.47 ms 0
Simulator 2 13 4,387.80 ms 337.52 ms 0

This stats endpoint uses peekDeviceSession, so these reads do not create capture sessions. Resident memory was 568–586 MiB; macOS stack samples reported a physical footprint of 1.3 GB (a different memory measure).

Two native stack samples (10 s and 5 s) show the same busy ROCKit remote-proxy paths, map enumeration, weak-reference handling, and two frame-capture queues releasing blocks/autorelease pools. Two worker threads were observed at approximately 95% CPU each. The JavaScript main thread was predominantly waiting in uv__io_poll → kevent.

Likely lifecycle problem: the installed bundle's getDeviceSession starts and retains the primary capture session until closeDeviceSession. MJPEG/AVCC disconnect handlers unsubscribe their stream consumers but do not close that session. The associated native code starts a strict 60 Hz framebuffer polling timer; stop() cancels it and unregisters callbacks. T3's LocalDeviceHost retains and supervises the hub process.

Relevant source revision from expo-device-hub 0.12.0's published gitHead:

Continued capture with no preview clients is confirmed. Accumulated proxies, stale descriptors, or native cleanup failure could explain the expensive surface lookups; a specific memory leak is not established. Please investigate capture shutdown when the last consumer leaves and why surface lookups retry indefinitely without producing frames. The attached archive includes the detailed report, sanitized full native samples, raw CPU observations, capture counters, and the confirmed shutdown check.

Impact

Major degradation or frequent failure

Version or commit

T3 Code Alpha 0.0.45; pinned expo-device-hub 0.12.0; agent-device 0.21.12

Environment

macOS 26.7.1 (25G241), MacBook Pro Mac14,9, Apple M2 Pro (10 cores), 32 GB RAM, Electron 44.4.2, Xcode 27.0 (27A266a), iOS 27.0 simulator runtime. Observed 2026-10-07 around 00:29–00:40 CEST.

Logs or stack traces

Process tree:
T3 Code desktop (PID 18553)
  T3 Code server, apps/server/dist/bin.mjs (PID 18637)
    Expo Device Hub (PID 5673)

Hub command:
/Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha)
  /Users/USER/.t3/tools/expo-device-hub/0.12.0/node_modules/expo-device-hub/dist/server/cli.mjs
  --port 53853 --host 127.0.0.1 --hide-sidebar --hide-boot-device

Busy native stack:
serve-sim-native.node
  ROCKRemoteProxy._forwardStackInvocation
  rock_ROCKInvocationInterfaceInvokeWithRemoteProxy
  rock_NSBlockToXPCObject
  ROCKForwardingBlockProxy.forwardingProxyWithSessionManager
  rock_ROCKSessionManagerForwardingProxyForInstance
  NSEnumerateMapTable
  NSConcreteMapTable.getKeys:values:
  objc_loadWeakRetained / weak_entry_for_referent

Both native samples also show two frame-capture queues spending substantial time
releasing Objective-C blocks and draining autorelease pools. The full sanitized
samples retain native addon binary offsets for symbolication.

Screenshots, recordings, or supporting files

t3-code-cpu-diagnostics.zip

Workaround

Fully quitting T3 Code stopped the CPU-heavy hub. At 00:40:19 CEST, the desktop, server, hub PID 5673, renderer/helpers, resource monitor, and Agent Device daemon had exited, and nothing was listening on the hub's former port 53853. Recurrence after relaunch remains unverified. Killing only the hub may cause T3's supervisor to restart it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions