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