Class: ProcessShared::Mutex

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

Overview

This Mutex class is implemented as a Semaphore with a second internal Semaphore used to track the locking process and thread.

ProcessError is raised if either #unlock is called by a process + thread different from the locking process + thread, or if #lock is called while the process + thread already holds the lock (i.e. the mutex is not re-entrant). This tracking is not without performance cost, of course (current implementation uses the additional Semaphore 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 Semaphore and SharedMemory resources. For now, rely on the object finalizers of those objects…

Direct Known Subclasses

Monitor

Instance Method Summary collapse

Methods included from OpenWithSelf

open

Constructor Details

#initializeMutex

Returns a new instance of Mutex.



25
26
27
28
29
30
# File 'lib/process_shared/mutex.rb', line 25

def initialize
  @internal_sem = Semaphore.new
  @locked_by = SharedMemory.new(:uint64, 2)  # [Process ID, Thread ID]

  @sem = Semaphore.new
end

Instance Method Details

#lockMutex

Returns:



33
34
35
36
37
38
39
40
41
# File 'lib/process_shared/mutex.rb', line 33

def lock
  if (p, t = current_process_and_thread) == locked_by
    raise ProcessError, "already locked by this process #{p}, thread #{t}"
  end

  @sem.wait
  self.locked_by = current_process_and_thread
  self
end

#locked?Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/process_shared/mutex.rb', line 44

def locked?
  locked_by != UNLOCKED
end

#sleep(timeout = nil) ⇒ Numeric

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

Returns:

  • (Numeric)


52
53
54
55
56
57
58
59
# File 'lib/process_shared/mutex.rb', line 52

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



89
90
91
92
93
94
95
96
# File 'lib/process_shared/mutex.rb', line 89

def synchronize
  lock
  begin
    yield
  ensure
    unlock
  end
end

#try_lockBoolean

Returns:

  • (Boolean)


62
63
64
65
66
67
68
69
70
71
72
# File 'lib/process_shared/mutex.rb', line 62

def try_lock
  with_internal_lock do
    if locked?
      false                 # was locked
    else
      @sem.wait             # should return immediately
      self.locked_by = current_process_and_thread
      true
    end
  end
end

#unlockMutex

Returns:



75
76
77
78
79
80
81
82
83
# File 'lib/process_shared/mutex.rb', line 75

def unlock
  if (p, t = locked_by) != (cp, ct = current_process_and_thread)
    raise ProcessError, "lock is held by process #{p}, thread #{t}: not process #{cp}, thread #{ct}"
  end

  self.locked_by = UNLOCKED
  @sem.post
  self
end