Class: Familia::ThreadSafety::InstrumentedMutex
- Inherits:
-
Object
- Object
- Familia::ThreadSafety::InstrumentedMutex
- Defined in:
- lib/familia/thread_safety/instrumented_mutex.rb
Overview
A Mutex wrapper that automatically reports contention metrics
This class wraps Ruby's standard Mutex to provide automatic instrumentation of lock contention and wait times.
Instance Attribute Summary collapse
-
#mutex ⇒ Object
readonly
Returns the value of attribute mutex.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
Instance Method Summary collapse
-
#contention_rate ⇒ Object
Calculate contention rate (0.0 to 1.0).
-
#double_checked_locking(check, init) ⇒ Object
Create a double-checked locking helper.
-
#initialize(name, monitor = nil) ⇒ InstrumentedMutex
constructor
Create a new instrumented mutex.
-
#lock ⇒ Object
Acquire the lock (with monitoring).
-
#locked? ⇒ Boolean
Check if locked by current thread.
-
#owned? ⇒ Boolean
Check if owned by current thread.
-
#sleep(timeout = nil) ⇒ Object
Sleep and release the lock temporarily.
-
#stats ⇒ Object
Get statistics for this mutex.
-
#synchronize { ... } ⇒ Object
Synchronize with automatic monitoring.
-
#try_lock ⇒ Object
Try to acquire the lock without blocking.
-
#unlock ⇒ Object
Release the lock.
Constructor Details
#initialize(name, monitor = nil) ⇒ InstrumentedMutex
Create a new instrumented mutex
29 30 31 32 33 34 35 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 29 def initialize(name, monitor = nil) @name = name.to_s @mutex = ::Mutex.new @monitor = monitor || Monitor.instance @lock_count = Concurrent::AtomicFixnum.new(0) @contention_count = Concurrent::AtomicFixnum.new(0) end |
Instance Attribute Details
#mutex ⇒ Object (readonly)
Returns the value of attribute mutex.
23 24 25 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 23 def mutex @mutex end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
23 24 25 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 23 def name @name end |
Instance Method Details
#contention_rate ⇒ Object
Calculate contention rate (0.0 to 1.0)
139 140 141 142 143 144 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 139 def contention_rate total = @lock_count.value return 0.0 if total == 0 @contention_count.value.to_f / total end |
#double_checked_locking(check, init) ⇒ Object
Create a double-checked locking helper
151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 151 def double_checked_locking(check, init) # Fast path - check without lock value = check.call return value if value # Slow path - check again with lock synchronize do value = check.call return value if value init.call end end |
#lock ⇒ Object
Acquire the lock (with monitoring)
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 77 def lock return @mutex.lock unless @monitor.enabled wait_start = Familia.now_in_μs if @mutex.try_lock @lock_count.increment return true end # Contention detected @contention_count.increment @monitor.record_contention(@name) result = @mutex.lock wait_end = Familia.now_in_μs wait_time_μs = wait_end - wait_start @lock_count.increment @monitor.record_wait_time(@name, wait_time_μs) result end |
#locked? ⇒ Boolean
Check if locked by current thread
114 115 116 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 114 def locked? @mutex.locked? end |
#owned? ⇒ Boolean
Check if owned by current thread
119 120 121 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 119 def owned? @mutex.owned? end |
#sleep(timeout = nil) ⇒ Object
Sleep and release the lock temporarily
124 125 126 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 124 def sleep(timeout = nil) @mutex.sleep(timeout) end |
#stats ⇒ Object
Get statistics for this mutex
129 130 131 132 133 134 135 136 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 129 def stats { name: @name, lock_count: @lock_count.value, contention_count: @contention_count.value, contention_rate: contention_rate } end |
#synchronize { ... } ⇒ Object
Synchronize with automatic monitoring
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 41 def synchronize return yield unless @monitor.enabled acquired = false wait_start = Familia.now_in_μs # Try non-blocking acquisition first to detect contention if @mutex.try_lock acquired = true wait_time = 0 @lock_count.increment else # Contention detected @contention_count.increment @monitor.record_contention(@name) # Now do blocking acquisition @mutex.lock acquired = true wait_end = Familia.now_in_μs wait_time_μs = wait_end - wait_start @lock_count.increment @monitor.record_wait_time(@name, wait_time_μs) if wait_time_μs > 10_000 # Log if waited more than 10ms (10,000μs) Familia.trace(:MUTEX_WAIT, nil, "Waited #{(wait_time_μs / 1000.0).round(2)}ms for #{@name}") end end yield ensure @mutex.unlock if acquired end |
#try_lock ⇒ Object
Try to acquire the lock without blocking
102 103 104 105 106 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 102 def try_lock result = @mutex.try_lock @lock_count.increment if result result end |
#unlock ⇒ Object
Release the lock
109 110 111 |
# File 'lib/familia/thread_safety/instrumented_mutex.rb', line 109 def unlock @mutex.unlock end |