I'm intrigued! I was fighting deadlocks in some Java code this week, and I'm working on a Rust project to maybe replace some of that.

One thing I didn't see in the post or the repo: does this work with async code?

I couldn't find the "search" button on Codeberg, and tests/integration.rs didn't have any async.

For embedded, I have had my eye on https://github.com/embassy-rs/embassy (which has an async runtime for embedded) and would love a nice locking crate to go with it.

IIUC, this crate has similar restrictions to the std Mutex. So it depends on what you mean by "work with async code."

First, lock acquisition seems to be a blocking method. And I don't see a `try_lock` method, so the naive pattern of spinning on `try_lock` and yielding on failure won't work. It'll still work in an async function, you'll just block the executor if the lock is contested and be sad.

Second, the key and guard types are not Send, otherwise it would be possible to send a key of a lower level to a thread that has already acquired a lock of a higher level, allowing deadlocks. (Or to pass a mutex guard of a higher level to a thread that has a key of a lower level.)

Therefore, holding a lock or a key across an await point makes your Future not Send.

Technically, this is fine. Nothing about Rust async in general requires that your Futures are Send. But in practice, most of the popular async runtimes require this. So if you want to use this with Tokio, for example, then you have to design your system to not hold locks or keys across await points.

This first restriction seems like it could be improved with the addition of an `AsyncLockable` trait. But the second restriction seems to me to be fundamental to the design.

Just wanted to add to your great summary a link to tokio’s docs on which kind of mutex to use, which seem applicable to the mutex in TFA as well: https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html#wh...

Also to note, regarding “future not send,” that, in tokio codebases where the general expectation is that futures will be Send, enabling the clippy lint “future_not_send” is extremely helpful in avoiding these kinds of issues and also in keeping the error localized to the offending function, rather than it being miles away somewhere it happens to be getting indirectly spawned or whatever: https://rust-lang.github.io/rust-clippy/stable/index.html?se...