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