require 'protocol' Locking = Protocol do specification # not necessary, because Protocol defaults to specification # mode already def lock() end def unlock() end implementation def synchronize lock begin yield ensure unlock end end end if $0 == __FILE__ require 'thread' require 'tempfile' class FileMutex def initialize @tempfile = Tempfile.new 'file-mutex' end def path @tempfile.path end def lock puts "Locking '#{path}'." @tempfile.flock File::LOCK_EX end def unlock puts "Unlocking '#{path}'." @tempfile.flock File::LOCK_UN end conform_to Locking end FileMutex.conform_to? Locking # => true FileMutex.new.conform_to? Locking # => true # Outputs something like: # Locking '...'. # Synchronized with '...'.. # Unlocking '...'. mutex = FileMutex.new mutex.synchronize do puts "Synchronized with '#{mutex.path}'." end class MemoryMutex def initialize @mutex = Mutex.new end def lock @mutex.lock end def unlock @mutex.unlock end conform_to Locking # actually Mutex itself would conform as well ;) end mutex = MemoryMutex.new mutex.synchronize do puts "Synchronized in memory." end MemoryMutex.conform_to? Locking # => true MemoryMutex.new.conform_to? Locking # => true class MyClass def initialize @mutex = FileMutex.new end attr_reader :mutex def mutex=(mutex) Locking.check mutex @mutex = mutex end end obj = MyClass.new obj.mutex # => #> begin obj.mutex = Object.new rescue Protocol::CheckFailed => e e # => #>|#> end obj.mutex = MemoryMutex.new # => #> # This works as well: obj.mutex = Mutex.new # => # Locking.check Mutex # => true Mutex.conform_to? Locking # => true end # >> "Locking '/tmp/file-mutex.26553.1'.\nSynchronized with '/tmp/file-mutex.26553.1'.\nUnlocking '/tmp/file-mutex.26553.1'.\nSynchronized in memory.\n"