Environment
|
|
| mlx-swift |
0.31.6 |
| Device |
iPhone 17 Pro Max (iPhone18,2), iOS 27.0 (24A437) |
| Xcode |
27.0 (27A266a), Swift 6.4 |
| Host |
macOS 27.0, Apple silicon |
| Config |
Debug, automatic signing, free developer profile |
What happens
A SwiftUI app that links MLX never reaches main() on the device. The process
is created and stays alive, but no application code runs, and iOS's launch
watchdog eventually kills it. devicectl device process launch blocks
indefinitely rather than returning.
The same binary launches normally on the iOS Simulator, and the app launched
normally before MLX was added.
Evidence that app code never runs
The first statement in App.init() appends a line to a file in the app's
Documents directory. (A file rather than print, because an iOS app's stdout
goes nowhere without a debugger attached, and the trace has to survive a launch
that never completes.)
After repeated launch attempts:
Documents/ was never written — so App.init() never ran.
Library/Caches/<bundle-id>/com.apple.metal/{libraries,functions}.data was
written during each attempt, so Metal initialisation did run.
Since Cmlx's C++ global constructors reference Metal (they are what makes
_MTLIOErrorDomain / _MTLTensorDomain undefined when linking for the
simulator — #341), those initialisers run at dyld load time, before main().
That is consistent with what the filesystem shows.
Relaunching with an already-warm Metal shader cache hung identically, so this is
not first-run shader compilation being slow.
Minimal reproduction
A SwiftUI app whose only dependency is MLX, which touches MLX only inside
.task — long after launch — so a hang before that point cannot be attributed
to application code calling into MLX.
import SwiftUI
import MLX
@main
struct ReproApp: App {
init() { Self.trace("app init reached") }
var body: some Scene {
WindowGroup { ContentView() }
}
static func trace(_ message: String) {
let line = "\(Date().timeIntervalSince1970) \(message)\n"
guard let documents = FileManager.default.urls(
for: .documentDirectory, in: .userDomainMask
).first else { return }
let url = documents.appendingPathComponent("trace.log")
if let handle = try? FileHandle(forWritingTo: url) {
handle.seekToEndOfFile()
handle.write(Data(line.utf8))
try? handle.close()
} else {
try? Data(line.utf8).write(to: url)
}
}
}
struct ContentView: View {
@State private var result = "body not evaluated yet"
var body: some View {
VStack(spacing: 16) {
Text("MLX launch repro").font(.headline)
Text(result).font(.footnote).monospaced()
}
.task {
ReproApp.trace("view appeared")
let sum = (MLXArray([1, 2, 3]) + MLXArray([4, 5, 6])).sum().item(Int.self)
result = "MLX computed \(sum)"
ReproApp.trace("mlx computed \(sum)")
}
}
}
Expected: trace.log contains app init reached, then view appeared.
What I have not been able to isolate
I want to be straight about the limits of this report.
The app where I observed the hang links MLX through
mlalma/kokoro-ios and
mlalma/MisakiSwift, so I have not
proved MLX alone is sufficient to reproduce it — the trigger could be another
initialiser in that chain that reaches into MLX. The repro above is written to
answer exactly that question, but I could not install it: a free developer
profile cannot mint a provisioning profile for a new bundle id from the command
line, and the device is already at the three-app limit.
If someone can run the repro on a device, that should settle it quickly. I am
happy to gather anything else useful.
Notes
- Also reproduced with the app's own
Documents write as the very first line of
init(), ruling out SwiftData, audio session setup and other app-level work.
- Memory is not a plausible cause: the hang happens before any allocation, and
running-on-ios.md discusses jetsam, which terminates rather than hangs.
Environment
What happens
A SwiftUI app that links MLX never reaches
main()on the device. The processis created and stays alive, but no application code runs, and iOS's launch
watchdog eventually kills it.
devicectl device process launchblocksindefinitely rather than returning.
The same binary launches normally on the iOS Simulator, and the app launched
normally before MLX was added.
Evidence that app code never runs
The first statement in
App.init()appends a line to a file in the app'sDocumentsdirectory. (A file rather thanprint, because an iOS app's stdoutgoes nowhere without a debugger attached, and the trace has to survive a launch
that never completes.)
After repeated launch attempts:
Documents/was never written — soApp.init()never ran.Library/Caches/<bundle-id>/com.apple.metal/{libraries,functions}.datawaswritten during each attempt, so Metal initialisation did run.
Since
Cmlx's C++ global constructors reference Metal (they are what makes_MTLIOErrorDomain/_MTLTensorDomainundefined when linking for thesimulator — #341), those initialisers run at dyld load time, before
main().That is consistent with what the filesystem shows.
Relaunching with an already-warm Metal shader cache hung identically, so this is
not first-run shader compilation being slow.
Minimal reproduction
A SwiftUI app whose only dependency is
MLX, which touches MLX only inside.task— long after launch — so a hang before that point cannot be attributedto application code calling into MLX.
Expected:
trace.logcontainsapp init reached, thenview appeared.What I have not been able to isolate
I want to be straight about the limits of this report.
The app where I observed the hang links MLX through
mlalma/kokoro-ios and
mlalma/MisakiSwift, so I have not
proved MLX alone is sufficient to reproduce it — the trigger could be another
initialiser in that chain that reaches into MLX. The repro above is written to
answer exactly that question, but I could not install it: a free developer
profile cannot mint a provisioning profile for a new bundle id from the command
line, and the device is already at the three-app limit.
If someone can run the repro on a device, that should settle it quickly. I am
happy to gather anything else useful.
Notes
Documentswrite as the very first line ofinit(), ruling out SwiftData, audio session setup and other app-level work.running-on-ios.mddiscusses jetsam, which terminates rather than hangs.