Class: ProcessShared::Mutex

Inherits:
Object
  • Object
show all
Defined in:
lib/process_shared/mutex.rb

Overview

This Mutex class is implemented as a BoundedSemaphore with a maximum value of 1. Additionally, the locking process is tracked, and ProcessError is raised if either #unlock is called by a process different from the locking process, or if #lock is called while the process already holds the lock (i.e. the mutex is not re-entrant). This tracking is not without performance cost, of course (current implementation uses an additional BoundedSemaphore and SharedMemory segment).

The API is intended to be identical to the ::Mutex in the core Ruby library.

TODO: the core Ruby api has no #close method, but this Mutex must release its BoundedSemaphore and SharedMemory resources. For now, we rely on the object finalizers of those objects…

Instance Method Summary collapse

Constructor Details

#initializeMutex

def self.open(&block)

new.with_self(&block)

end



29
30
31
32
33
34
# File 'lib/process_shared/mutex.rb', line 29

def initialize
  @internal_sem = BoundedSemaphore.new(1)
  @locked_by = SharedMemory.new(:int)

  @sem = BoundedSemaphore.new(1)
end

Instance Method Details

#lockMutex

Returns:



37
38
39
40
41
42
43
44
45
# File 'lib/process_shared/mutex.rb', line 37

def lock
  if locked_by == ::Process.pid
    raise ProcessError, "already locked by this process #{::Process.pid}"
  end

  @sem.wait
  self.locked_by = ::Process.pid
  self
end

#locked?Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/process_shared/mutex.rb', line 48

def locked?
  locked_by > 0
end

#sleep(timeout = nil) ⇒ Numeric

Releases the lock and sleeps timeout seconds if it is given and non-nil or forever.

Returns:

  • (Numeric)


56
57
58
59
60
61
62
63
# File 'lib/process_shared/mutex.rb', line 56

def sleep(timeout = nil)
  unlock
  begin
    timeout ? Kernel.sleep(timeout) : Kernel.sleep
  ensure
    lock
  end
end

#synchronizeObject

Acquire the lock, yield the block, then ensure the lock is unlocked.

Returns:

  • (Object)

    the result of the block



93
94
95
96
97
98
99
100
# File 'lib/process_shared/mutex.rb', line 93

def synchronize
  lock
  begin
    yield
  ensure
    unlock
  end
end

#try_lockBoolean

Returns:

  • (Boolean)


66
67
68
69
70
71
72
73
74
75
76
# File 'lib/process_shared/mutex.rb', line 66

def try_lock
  with_internal_lock do
    if @locked_by.get_int(0) > 0
      false                 # was locked
    else
      @sem.wait
      self.locked_by = ::Process.pid
      true
    end
  end
end

#unlockMutex

Returns:



79
80
81
82
83
84
85
86
87
# File 'lib/process_shared/mutex.rb', line 79

def unlock
  if (p = locked_by) != ::Process.pid
    raise ProcessError, "lock is held by #{p} not #{::Process.pid}"
  end

  self.locked_by = 0
  @sem.post
  self
end