1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// RLX — versatile ML compiler + runtime.
// Copyright (C) 2026 Eugene Hauptmann, Nataliya Kosmyna.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Serialize access to the MLX C++ runtime.
//!
//! MLX's device context, allocator/refcount tables, and `mlx::compile`
//! trace builder are not safe under concurrent use from multiple Rust
//! threads. Integration tests run in parallel by default; without this
//! lock, compiled-mode conv repro tests can exit with SIGTRAP and result
//! arrays freed on one thread (`Array::drop` → `rlx_mlx_array_free`) can
//! SIGSEGV against another thread's in-flight `eval()`.
//!
//! The lock is **reentrant** on purpose: `Array::drop` takes it, and
//! intermediate arrays are dropped constantly *inside* an already-guarded
//! `run_*` call on the same thread — a non-reentrant `Mutex` would
//! self-deadlock there. `ReentrantLock` lets the owning thread re-enter
//! for free while still excluding all other threads. Single-threaded
//! inference (the hot path) only ever sees the uncontended-or-reentrant
//! case, so the overhead is a thread-id check, not cross-core contention.
use Cell;
use ;
static MLX_RUNTIME_LOCK: = new;
thread_local!
/// Reentrant guard over [`MLX_RUNTIME_LOCK`]. The outermost acquisition on a
/// thread owns the real `MutexGuard`; nested ones hold `None` and only manage
/// the depth counter. Dropping decrements the depth and, at zero, releases the
/// mutex (the `_outer` field drops after this `Drop` body runs).
pub
/// Hold for the duration of any MLX FFI that builds, executes, or frees
/// graphs/arrays. Reentrant: safe to take while already held on this thread
/// (e.g. `Array::drop` firing inside a guarded `run_*`).
pub