Fix up Rubinius specific library specs.
[rbx.git] / lib / sync.rb
blobbf57c840bd7117eda85d0dbce5e4ab077268588e
2 #   sync.rb - 2 phase lock with counter
3 #           by Keiju ISHITSUKA(keiju@ishitsuka.com)
4 #           reworked by MenTaLguY
6 # --
7 #  Sync_m, Synchronizer_m
8 #  Usage:
9 #   obj.extend(Sync_m)
10 #   or
11 #   class Foo
12 #        include Sync_m
13 #        :
14 #   end
16 #   Sync_m#sync_mode
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
24 #   Sync, Synchronicer:
25 #        include Sync_m
26 #   Usage:
27 #   sync = Sync.new
29 #   Sync#mode
30 #   Sync#locked?
31 #   Sync#shared?
32 #   Sync#exclusive?
33 #   Sync#try_lock(mode) -- mode = :EX, :SH, :UN
34 #   Sync#lock(mode)     -- mode = :EX, :SH, :UN
35 #   Sync#unlock
36 #   Sync#synchronize(mode) {...}
37 #   
40 unless defined? Thread
41   fail "Thread not available for this ruby interpreter"
42 end
44 require 'thread'
46 module Sync_m
47   RCS_ID='-$Header$-'
48   
49   # lock mode
50   UN = :UN
51   SH = :SH
52   EX = :EX
53   
54   # exceptions
55   class Err < StandardError
56     def Err.Fail(*opt)
57       fail self, sprintf(self::Message, *opt)
58     end
59     
60     class UnknownLocker < Err
61       Message = "Thread(%s) not locked."
62       def UnknownLocker.Fail(th)
63         super(th.inspect)
64       end
65     end
66     
67     class LockModeFailer < Err
68       Message = "Unknown lock mode(%s)"
69       def LockModeFailer.Fail(mode)
70         if mode.id2name
71           mode = id2name
72         end
73         super(mode)
74       end
75     end
76   end
77   
78   def Sync_m.define_aliases(cl)
79     cl.module_eval %q{
80       alias locked? sync_locked?
81       alias shared? sync_shared?
82       alias exclusive? sync_exclusive?
83       alias lock sync_lock
84       alias unlock sync_unlock
85       alias try_lock sync_try_lock
86       alias synchronize sync_synchronize
87     }
88   end
89   
90   def Sync_m.append_features(cl)
91     super
92     unless cl.instance_of?(Module)
93       # do nothing for Modules
94       # make aliases and include the proper module.
95       define_aliases(cl)
96     end
97   end
98   
99   def Sync_m.extend_object(obj)
100     super
101     obj.sync_extended
102   end
104   def sync_extended
105     unless (defined? locked? and
106             defined? shared? and
107             defined? exclusive? and
108             defined? lock and
109             defined? unlock and
110             defined? try_lock and
111             defined? synchronize)
112       Sync_m.define_aliases(class<<self;self;end)
113     end
114     sync_initialize
115   end
117   # accessing
118   def sync_locked?
119     @sync_mutex.synchronize { @sync_mode != UN }
120   end
121   
122   def sync_shared?
123     @sync_mutex.synchronize { @sync_mode == SH }
124   end
125   
126   def sync_exclusive?
127     @sync_mutex.synchronize { @sync_mode == EX }
128   end
129   
130   # locking methods.
131   def sync_try_lock(m = EX)
132     return sync_unlock if m == UN
133     @sync_mutex.synchronize do
134       sync_try_lock_sub(m)
135     end
136   end
137   
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
144           begin
145             @sync_upgrade_cond.wait(@sync_mutex)
146           ensure
147             @sync_upgrade_n_waiting -= 1
148           end
149         else
150           @sync_cond.wait(@sync_mutex)
151         end
152       end
153     end
154     self
155   end
156   
157   def sync_unlock(m = EX)
158     @sync_mutex.synchronize do
159       case @sync_mode
160       when UN
161         Err::UnknownLocker.Fail(Thread.current)
162       when SH
163         # downgrade EX unlock requests in SH mode
164         m = SH if m == EX
165       when EX
166         # upgrade SH unlock requests in EX mode
167         # (necessary to balance lock request upgrades)
168         m = EX if m == SH
169       end
170     
171       runnable = false
172       case m
173       when UN
174         Err::UnknownLocker.Fail(Thread.current)
175       
176       when EX
177         if @sync_ex_locker == Thread.current
178           @sync_ex_count -= 1
179           if @sync_ex_count.zero?
180             @sync_ex_locker = nil
181             if @sync_sh_lockers.include? Thread.current
182               @sync_mode = SH
183             else
184               @sync_mode = UN
185             end
186             runnable = true
187           end
188         else
189           Err::UnknownLocker.Fail(Thread.current)
190         end
191       
192       when SH
193         count = @sync_sh_lockers[Thread.current]
194         Err::UnknownLocker.Fail(Thread.current) if count.nil?
195         count -= 1
196         if count.zero?
197           @sync_sh_lockers.delete Thread.current
198           if @sync_sh_lockers.empty? and @sync_mode == SH
199             @sync_mode = UN
200             runnable = true
201           end
202         else
203           @sync_sh_lockers[Thread.current] = count
204         end
205       end
207       if runnable
208         if @sync_upgrade_n_waiting.nonzero?
209           @sync_upgrade_cond.signal
210         else
211           @sync_cond.signal
212         end
213       end
214     end
215     self
216   end
217   
218   def sync_synchronize(mode = EX)
219     begin
220       sync_lock(mode)
221       yield
222     ensure
223       sync_unlock
224     end
225   end
227   def sync_mode
228     @sync_mutex.synchronize { @sync_mode }
229   end
231   private
233   def sync_initialize
234     @sync_mutex = Mutex.new
235     @sync_mode = UN
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
241     @sync_ex_count = 0
242   end
244   def initialize(*args)
245     sync_initialize
246     super
247   end
248     
249   def sync_try_lock_sub(m)
250     case m
251     when SH
252       case @sync_mode
253       when UN, SH
254         @sync_mode = SH
255         count = @sync_sh_lockers[Thread.current] || 0
256         @sync_sh_lockers[Thread.current] = count + 1
257         true
258       when EX
259         # in EX mode, lock will upgrade to EX lock
260         if @sync_ex_locker == Thread.current
261           @sync_ex_count += 1
262           true
263         else
264           false
265         end
266       end
267     when EX
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
273         @sync_mode = EX
274         @sync_ex_locker = Thread.current
275         @sync_ex_count += 1
276         true
277       else
278         false
279       end
280     else
281       Err::LockModeFailer.Fail mode
282     end
283   end
285 Synchronizer_m = Sync_m
287 class Sync
288   #Sync_m.extend_class self
289   include Sync_m
290     
291   def initialize
292     super
293   end
294     
296 Synchronizer = Sync