@@ -206,11 +206,17 @@ and direct MLX round-trips, but most users should go through Nx.
206206
207207MLX dispatches GPU work through Metal command queues. Emily owns one
208208worker thread per command queue; each worker is a dedicated OS thread
209- that runs the MLX ops on behalf of BEAM schedulers. NIFs hand their
210- work to a worker via a ` run_sync ` promise (blocks the caller for
211- ~ 1–10 µs) rather than executing on the scheduler thread directly,
212- which keeps MLX's per-thread ` CommandEncoder ` consistent and lets
213- the BEAM migrate Elixir processes freely.
209+ that runs the MLX ops on behalf of BEAM processes. NIFs return
210+ immediately after enqueueing their work on a worker: the worker runs
211+ the op, then posts ` {ref, {:ok, result}} ` back to the caller via
212+ ` enif_send ` , and the caller's public wrapper awaits that message with
213+ a plain ` receive ` . No BEAM scheduler (regular or dirty) blocks on MLX
214+ work — callers see the same synchronous semantics as before, but the
215+ scheduler is free to run other processes while the GPU is busy.
216+
217+ Because the MLX stream is pinned to its worker thread, MLX's
218+ per-thread ` CommandEncoder ` state stays consistent regardless of how
219+ the BEAM migrates Elixir processes between schedulers.
214220
215221By default, every op uses the ** default worker** owned by the
216222` Emily.MlxStream.Default ` GenServer under the application supervisor.
0 commit comments