/
load_interlock_aware_monitor.rb
72 lines (61 loc) · 1.78 KB
/
load_interlock_aware_monitor.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
# frozen_string_literal: true
require "monitor"
module ActiveSupport
module Concurrency
module LoadInterlockAwareMonitorMixin # :nodoc:
EXCEPTION_NEVER = { Exception => :never }.freeze
EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze
private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
# Enters an exclusive section, but allows dependency loading while blocked
def mon_enter
mon_try_enter ||
ActiveSupport::Dependencies.interlock.permit_concurrent_loads { super }
end
def synchronize(&block)
Thread.handle_interrupt(EXCEPTION_NEVER) do
mon_enter
begin
Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
ensure
mon_exit
end
end
end
end
# A monitor that will permit dependency loading while blocked waiting for
# the lock.
class LoadInterlockAwareMonitor < Monitor
include LoadInterlockAwareMonitorMixin
end
class ThreadLoadInterlockAwareMonitor # :nodoc:
prepend LoadInterlockAwareMonitorMixin
def initialize
@owner = nil
@count = 0
@mutex = Mutex.new
end
private
def mon_try_enter
if @owner != Thread.current
return false unless @mutex.try_lock
@owner = Thread.current
end
@count += 1
end
def mon_enter
@mutex.lock if @owner != Thread.current
@owner = Thread.current
@count += 1
end
def mon_exit
unless @owner == Thread.current
raise ThreadError, "current thread not owner"
end
@count -= 1
return unless @count == 0
@owner = nil
@mutex.unlock
end
end
end
end