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