Skip to content

fix lock inversion between evalLock and dispatch_once on globalStreams - #499

Merged
davidkoski merged 1 commit into
mainfrom
fix-lock-inversion
Oct 9, 2026
Merged

davidkoski merged 1 commit into
mainfrom
fix-lock-inversion

Conversation

@davidkoski

Copy link
Copy Markdown
Member

Proposed changes

  • globalStreams could take the evalLock
  • evalLock could take the globalStreams lock (dispatch_once)
  • break the cycle by making the no-args init lock free

Note: this is potentially a problem on main / latest tag. The triggering condition is narrow, so not likely, but still reachable.

Checklist

Put an x in the boxes that apply.

  • I have read the CONTRIBUTING document
  • I have run pre-commit run --all-files to format my code / installed pre-commit prior to committing changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the necessary documentation (if needed)

- globalStreams could take the evalLock
- evalLock could take the globalStreams lock (dispatch_once)
- break the cycle by making the no-args init lock free
Comment thread Source/MLX/Stream.swift
Comment on lines -191 to +196
self.cpu = RealizedStreamPromise(defaultDevice)
self.cpu = LazyStreamPromise(defaultDevice)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem occurred because RealizedStreamPromise would synchronously obtain a stream, which could require the evalLock to create a new stream. No problem normally, but this is the initializer that is used for:

let globalStreams = StreamPairPromise()

This in turn takes a dispatch_once lock.

Lock inversion: EvalLock -> dispatch_once and dispatch_once -> EvalLock.

Switching to the LazyStreamPromise does not acquire any resources at creation time so this does not use EvalLock under the dispatch_once (at the cost of a lock every time it is accessed ).

@nwh nwh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@davidkoski
davidkoski merged commit 1e61d9c into main Oct 9, 2026
7 checks passed
@davidkoski
davidkoski deleted the fix-lock-inversion branch October 9, 2026 22:51
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.

2 participants