* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / lib / sync.rb
blob22f160f29021290c0dbca7740dcfde4436f04906
2 #   sync.rb - 2 phase lock with counter
3 #       $Release Version: 1.0$
4 #       $Revision$
5 #       by Keiju ISHITSUKA(keiju@ishitsuka.com)
7 # --
8 #  Sync_m, Synchronizer_m
9 #  Usage:
10 #   obj.extend(Sync_m)
11 #   or
12 #   class Foo
13 #       include Sync_m
14 #       :
15 #   end
17 #   Sync_m#sync_mode
18 #   Sync_m#sync_locked?, locked?
19 #   Sync_m#sync_shared?, shared?
20 #   Sync_m#sync_exclusive?, sync_exclusive?
21 #   Sync_m#sync_try_lock, try_lock
22 #   Sync_m#sync_lock, lock
23 #   Sync_m#sync_unlock, unlock
25 #  Sync, Synchronizer:
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   raise "Thread not available for this ruby interpreter"
42 end
44 module Sync_m
45   RCS_ID='-$Header$-'
46   
47   # lock mode
48   UN = :UN
49   SH = :SH
50   EX = :EX
51   
52   # exceptions
53   class Err < StandardError
54     def Err.Fail(*opt)
55       fail self, sprintf(self::Message, *opt)
56     end
57     
58     class UnknownLocker < Err
59       Message = "Thread(%s) not locked."
60       def UnknownLocker.Fail(th)
61         super(th.inspect)
62       end
63     end
64     
65     class LockModeFailer < Err
66       Message = "Unknown lock mode(%s)"
67       def LockModeFailer.Fail(mode)
68         if mode.id2name
69           mode = id2name
70         end
71         super(mode)
72       end
73     end
74   end
75   
76   def Sync_m.define_aliases(cl)
77     cl.module_eval %q{
78       alias locked? sync_locked?
79       alias shared? sync_shared?
80       alias exclusive? sync_exclusive?
81       alias lock sync_lock
82       alias unlock sync_unlock
83       alias try_lock sync_try_lock
84       alias synchronize sync_synchronize
85     }
86   end
87   
88   def Sync_m.append_features(cl)
89     super
90     # do nothing for Modules
91     # make aliases for Classes.
92     define_aliases(cl) unless cl.instance_of?(Module)
93     self
94   end
95   
96   def Sync_m.extend_object(obj)
97     super
98     obj.sync_extend
99   end
100   
101   def sync_extend
102     unless (defined? locked? and
103             defined? shared? and
104             defined? exclusive? and
105             defined? lock and
106             defined? unlock and
107             defined? try_lock and
108             defined? synchronize)
109       Sync_m.define_aliases(class<<self;self;end)
110     end
111     sync_initialize
112   end
114   # accessing
115   def sync_locked?
116     sync_mode != UN
117   end
118   
119   def sync_shared?
120     sync_mode == SH
121   end
122   
123   def sync_exclusive?
124     sync_mode == EX
125   end
126   
127   # locking methods.
128   def sync_try_lock(mode = EX)
129     return unlock if sync_mode == UN
130     @sync_mutex.synchronize do
131       ret = sync_try_lock_sub(sync_mode)
132     end
133     ret
134   end
135   
136   def sync_lock(m = EX)
137     return unlock if m == UN
139     while true
140       @sync_mutex.synchronize do
141         if sync_try_lock_sub(m)
142           return self
143         else
144           if sync_sh_locker[Thread.current]
145             sync_upgrade_waiting.push [Thread.current, sync_sh_locker[Thread.current]]
146             sync_sh_locker.delete(Thread.current)
147           else
148             sync_waiting.push Thread.current
149           end
150           @sync_mutex.sleep
151         end
152       end
153     end
154     self
155   end
156   
157   def sync_unlock(m = EX)
158     wakeup_threads = []
159     @sync_mutex.synchronize do
160       if sync_mode == UN
161         Err::UnknownLocker.Fail(Thread.current)
162       end
163       
164       m = sync_mode if m == EX and sync_mode == SH
165       
166       runnable = false
167       case m
168       when UN
169         Err::UnknownLocker.Fail(Thread.current)
170         
171       when EX
172         if sync_ex_locker == Thread.current
173           if (self.sync_ex_count = sync_ex_count - 1) == 0
174             self.sync_ex_locker = nil
175             if sync_sh_locker.include?(Thread.current)
176               self.sync_mode = SH
177             else
178               self.sync_mode = UN
179             end
180             runnable = true
181           end
182         else
183           Err::UnknownLocker.Fail(Thread.current)
184         end
185         
186       when SH
187         if (count = sync_sh_locker[Thread.current]).nil?
188           Err::UnknownLocker.Fail(Thread.current)
189         else
190           if (sync_sh_locker[Thread.current] = count - 1) == 0 
191             sync_sh_locker.delete(Thread.current)
192             if sync_sh_locker.empty? and sync_ex_count == 0
193               self.sync_mode = UN
194               runnable = true
195             end
196           end
197         end
198       end
199       
200       if runnable
201         if sync_upgrade_waiting.size > 0
202           th, count = sync_upgrade_waiting.shift
203           sync_sh_locker[th] = count
204           th.wakeup
205           wakeup_threads.push th
206         else
207           wait = sync_waiting
208           self.sync_waiting = []
209           for th in wait
210             th.wakeup
211             wakeup_threads.push th
212           end
213         end
214       end
215     end
216     for th in wakeup_threads
217       th.run
218     end
219     self
220   end
221   
222   def sync_synchronize(mode = EX)
223     sync_lock(mode)
224     begin
225       yield
226     ensure
227       sync_unlock
228     end
229   end
231   attr_accessor :sync_mode
232     
233   attr_accessor :sync_waiting
234   attr_accessor :sync_upgrade_waiting
235   attr_accessor :sync_sh_locker
236   attr_accessor :sync_ex_locker
237   attr_accessor :sync_ex_count
239   def sync_inspect
240     sync_iv = instance_variables.select{|iv| /^@sync_/ =~ iv.id2name}.collect{|iv| iv.id2name + '=' + instance_eval(iv.id2name).inspect}.join(",")
241     print "<#{self.class}.extend Sync_m: #{inspect}, <Sync_m: #{sync_iv}>"
242   end
244   private
246   def sync_initialize
247     @sync_mode = UN
248     @sync_waiting = []
249     @sync_upgrade_waiting = []
250     @sync_sh_locker = Hash.new
251     @sync_ex_locker = nil
252     @sync_ex_count = 0
254     @sync_mutex = Mutex.new
255   end
257   def initialize(*args)
258     super
259     sync_initialize
260   end
261     
262   def sync_try_lock_sub(m)
263     case m
264     when SH
265       case sync_mode
266       when UN
267         self.sync_mode = m
268         sync_sh_locker[Thread.current] = 1
269         ret = true
270       when SH
271         count = 0 unless count = sync_sh_locker[Thread.current]
272         sync_sh_locker[Thread.current] = count + 1
273         ret = true
274       when EX
275         # in EX mode, lock will upgrade to EX lock
276         if sync_ex_locker == Thread.current
277           self.sync_ex_count = sync_ex_count + 1
278           ret = true
279         else
280           ret = false
281         end
282       end
283     when EX
284       if sync_mode == UN or
285           sync_mode == SH && sync_sh_locker.size == 1 && sync_sh_locker.include?(Thread.current) 
286         self.sync_mode = m
287         self.sync_ex_locker = Thread.current
288         self.sync_ex_count = 1
289         ret = true
290       elsif sync_mode == EX && sync_ex_locker == Thread.current
291         self.sync_ex_count = sync_ex_count + 1
292         ret = true
293       else
294         ret = false
295       end
296     else
297       Err::LockModeFailer.Fail mode
298     end
299     return ret
300   end
302 Synchronizer_m = Sync_m
304 class Sync
305   include Sync_m
307 Synchronizer = Sync