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