Maybe this has changed, but last time I looked at futexes there was no syscall for locking (assuming no contention), but unlocking always made a syscall. This was many years ago so it could be different now.
https://github.com/lattera/glibc/blob/master/nptl/pthread_mu...
https://doc.rust-lang.org/src/std/sys/unix/locks/futex_mutex...
Unlock is just:: self.futex.swap(0, Release) -- if the value we get back is 2 then we know at least one thread is asleep waiting on this futex, so we need a system call to wake them, but in the uncontended case we're done immediately.