Skip to content

M14.5: worker-thread dispatch for vendored MLX - #29

Merged
ausimian merged 3 commits into
mainfrom
m14.5-mlx-from-source
Apr 17, 2026
Merged

M14.5: worker-thread dispatch for vendored MLX#29
ausimian merged 3 commits into
mainfrom
m14.5-mlx-from-source

Conversation

@ausimian

@ausimian ausimian commented Apr 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • Worker-thread dispatch: replace stream-index NIF convention and global safe_eval mutex with per-stream dedicated OS threads. Each WorkerThread (C++) owns an MLX stream and its Metal CommandEncoder; NIFs dispatch via run_sync (promise/future). Eliminates thread-local CommandEncoder mismatch under BEAM process migration and enables concurrent Metal dispatch across multiple workers.
  • MLX from source: vendor MLX as a git submodule (vendor/mlx), build libmlx.a via cmake, statically link into the NIF, stage mlx.metallib into priv/. No prebuilt download step.
  • Emily.MlxStream GenServer: owns the default WorkerThread resource under the application supervisor. default_worker/0 caches the worker ref in the process dictionary. User-created streams (Emily.Stream.new/1) get their own worker with enif_monitor_process cleanup.
  • CI: checkout with submodules: recursive, remove stale prebuilt cache step, add .gitmodules to build cache key.

Test plan

  • mix precommit passes (compile --warnings-as-errors, format, credo --strict, test)
  • 303 default tests, 0 failures
  • Conformance suite (17 tests), grad conformance (9 tests)
  • All *_full suites: training_full, qwen3_full, qwen3_quant_full, vit_full, whisper_full, fast_kernels_full
  • Soak tests: memory, backend concurrency, stream concurrency, eval concurrency
  • CI green on macos-14 runner (submodule checkout + build from source)

🤖 Generated with Claude Code

The SIGSEGV from M14's stream-per-process isn't fixed by the
safe_eval mutex — the race is in Metal command encoder state
outside of eval. Building MLX from source (for native thread-
safety) needs to land before M15 linalg, not after.
Replace stream-index NIF convention and global eval mutex with
per-stream dedicated OS threads. Each WorkerThread owns an MLX
stream and its Metal CommandEncoder; NIFs dispatch via run_sync
(promise/future). Eliminates thread-local CommandEncoder mismatch
under BEAM process migration and enables concurrent Metal dispatch.

MLX vendored as git submodule (vendor/mlx), built from source as
libmlx.a with metallib staged into priv/.
USE_SYSTEM_FMT=ON required a system-installed fmt library,
which the CI runner doesn't have. Remove the flag so MLX's
cmake fetches fmt itself.
@ausimian
ausimian merged commit c0f14ca into main Apr 17, 2026
1 check passed
@ausimian
ausimian deleted the m14.5-mlx-from-source branch April 17, 2026 09: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.

1 participant