rust 43 lines · 7 steps

A self-timing mutex guard in Rust

Wrapping a MutexGuard in a custom type lets you log how long a lock is held using RAII.

Explained by highlit
1use std::sync::{Arc, Mutex, MutexGuard};
2use std::time::Instant;
3 
4pub struct TimedGuard<'a, T> {
5 inner: MutexGuard<'a, T>,
6 label: &'static str,
7 acquired: Instant,
8}
9 
10impl<'a, T> TimedGuard<'a, T> {
11 pub fn acquire(mutex: &'a Mutex<T>, label: &'static str) -> Self {
12 let acquired = Instant::now();
13 let inner = mutex.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
14 log::debug!("acquired lock '{}'", label);
15 Self { inner, label, acquired }
16 }
17}
18 
19impl<'a, T> std::ops::Deref for TimedGuard<'a, T> {
20 type Target = T;
21 fn deref(&self) -> &T {
22 &self.inner
23 }
24}
25 
26impl<'a, T> std::ops::DerefMut for TimedGuard<'a, T> {
27 fn deref_mut(&mut self) -> &mut T {
28 &mut self.inner
29 }
30}
31 
32impl<'a, T> Drop for TimedGuard<'a, T> {
33 fn drop(&mut self) {
34 let held = self.acquired.elapsed();
35 log::debug!("released lock '{}' after {:.2}ms", self.label, held.as_secs_f64() * 1000.0);
36 }
37}
38 
39pub fn credit_balance(account: &Arc<Mutex<u64>>, amount: u64) -> u64 {
40 let mut balance = TimedGuard::acquire(account, "account_balance");
41 *balance = balance.saturating_add(amount);
42 *balance
43}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Wrapping a guard in your own type lets you attach behavior to a lock's entire lifetime.
  2. 2Implementing Deref and DerefMut makes a wrapper transparent so callers use it like the value itself.
  3. 3Drop turns scope exit into a reliable hook for cleanup or measurement without explicit calls.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A self-timing mutex guard in Rust — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code