2 # sync.rb - 2 phase lock with counter
3 # by Keiju ISHITSUKA(keiju@ishitsuka.com)
4 # reworked by MenTaLguY
7 # Sync_m, Synchronizer_m
17 # Sync_m#sync_locked?, locked?
18 # Sync_m#sync_shared?, shared?
19 # Sync_m#sync_exclusive?, sync_exclusive?
20 # Sync_m#sync_try_lock, try_lock
21 # Sync_m#sync_lock, lock
22 # Sync_m#sync_unlock, unlock
33 # Sync#try_lock(mode) -- mode = :EX, :SH, :UN
34 # Sync#lock(mode) -- mode = :EX, :SH, :UN
36 # Sync#synchronize(mode) {...}
40 unless defined? Thread
41 fail "Thread not available for this ruby interpreter"
55 class Err < StandardError
57 fail self, sprintf(self::Message, *opt)
60 class UnknownLocker < Err
61 Message = "Thread(%s) not locked."
62 def UnknownLocker.Fail(th)
67 class LockModeFailer < Err
68 Message = "Unknown lock mode(%s)"
69 def LockModeFailer.Fail(mode)
78 def Sync_m.define_aliases(cl)
80 alias locked? sync_locked?
81 alias shared? sync_shared?
82 alias exclusive? sync_exclusive?
84 alias unlock sync_unlock
85 alias try_lock sync_try_lock
86 alias synchronize sync_synchronize
90 def Sync_m.append_features(cl)
92 unless cl.instance_of?(Module)
93 # do nothing for Modules
94 # make aliases and include the proper module.
99 def Sync_m.extend_object(obj)
105 unless (defined? locked? and
107 defined? exclusive? and
110 defined? try_lock and
111 defined? synchronize)
112 Sync_m.define_aliases(class<<self;self;end)
119 @sync_mutex.synchronize { @sync_mode != UN }
123 @sync_mutex.synchronize { @sync_mode == SH }
127 @sync_mutex.synchronize { @sync_mode == EX }
131 def sync_try_lock(m = EX)
132 return sync_unlock if m == UN
133 @sync_mutex.synchronize do
138 def sync_lock(m = EX)
139 return sync_unlock if m == UN
140 @sync_mutex.synchronize do
141 until sync_try_lock_sub(m)
142 if @sync_sh_lockers.include? Thread.current
143 @sync_upgrade_n_waiting += 1
145 @sync_upgrade_cond.wait(@sync_mutex)
147 @sync_upgrade_n_waiting -= 1
150 @sync_cond.wait(@sync_mutex)
157 def sync_unlock(m = EX)
158 @sync_mutex.synchronize do
161 Err::UnknownLocker.Fail(Thread.current)
163 # downgrade EX unlock requests in SH mode
166 # upgrade SH unlock requests in EX mode
167 # (necessary to balance lock request upgrades)
174 Err::UnknownLocker.Fail(Thread.current)
177 if @sync_ex_locker == Thread.current
179 if @sync_ex_count.zero?
180 @sync_ex_locker = nil
181 if @sync_sh_lockers.include? Thread.current
189 Err::UnknownLocker.Fail(Thread.current)
193 count = @sync_sh_lockers[Thread.current]
194 Err::UnknownLocker.Fail(Thread.current) if count.nil?
197 @sync_sh_lockers.delete Thread.current
198 if @sync_sh_lockers.empty? and @sync_mode == SH
203 @sync_sh_lockers[Thread.current] = count
208 if @sync_upgrade_n_waiting.nonzero?
209 @sync_upgrade_cond.signal
218 def sync_synchronize(mode = EX)
228 @sync_mutex.synchronize { @sync_mode }
234 @sync_mutex = Mutex.new
236 @sync_cond = ConditionVariable.new
237 @sync_upgrade_cond = ConditionVariable.new
238 @sync_upgrade_n_waiting = 0
239 @sync_sh_lockers = Hash.new
240 @sync_ex_locker = nil
244 def initialize(*args)
249 def sync_try_lock_sub(m)
255 count = @sync_sh_lockers[Thread.current] || 0
256 @sync_sh_lockers[Thread.current] = count + 1
259 # in EX mode, lock will upgrade to EX lock
260 if @sync_ex_locker == Thread.current
268 if @sync_mode == UN or
269 @sync_mode == SH && @sync_sh_lockers.size == 1 &&
270 @sync_sh_lockers.include?(Thread.current) or
271 @sync_mode == EX && @sync_ex_locker == Thread.current
274 @sync_ex_locker = Thread.current
281 Err::LockModeFailer.Fail mode
285 Synchronizer_m = Sync_m
288 #Sync_m.extend_class self