Class: QuackConcurrency::Mutex

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

Overview

Note:

duck type for ‘::Thread::Mutex`

Direct Known Subclasses

ReentrantMutex

Instance Method Summary collapse

Constructor Details

#initializeMutex

Creates a new QuackConcurrency::Mutex concurrency tool.



8
9
10
11
12
# File 'lib/quack_concurrency/mutex.rb', line 8

def initialize
  @mutex = ::Mutex.new
  @condition_variable = UninterruptibleConditionVariable.new
  @owner = nil
end

Instance Method Details

#lockvoid #lock { ... } ⇒ Object

Overloads:

  • #lockvoid

    This method returns an undefined value.

    Obtains the lock or sleeps the current ‘Thread` until it is available.

  • #lock { ... } ⇒ Object

    Obtains the lock, runs the block, then releases the lock when the block completes.

    Yields:

    • block to run with the lock

    Returns:

    • (Object)

      result of the block

Raises:

  • (ThreadError)

    if current ‘Thread` is already locking it



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/quack_concurrency/mutex.rb', line 22

def lock(&block)
  raise ThreadError, 'this Thread is already locking this Mutex' if owned?
  if block_given?
    lock
    begin
      yield
    ensure
      unlock
    end
  else
    @mutex.synchronize do
      @condition_variable.wait(@mutex) if locked?
      @owner = caller
    end
    nil
  end
end

#locked?Boolean

Returns:

  • (Boolean)


40
41
42
# File 'lib/quack_concurrency/mutex.rb', line 40

def locked?
  !!@owner
end

#owned?Boolean

Returns:

  • (Boolean)


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

def owned?
  @owner == caller
end

#ownerObject



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

def owner
  @owner
end

#sleep(timeout = nil) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/quack_concurrency/mutex.rb', line 52

def sleep(timeout = nil)
  if timeout != nil && !timeout.is_a?(Numeric)
    raise ArgumentError, "'timeout' argument must be nil or a Numeric"
  end
  unlock do
    if timeout
      elapsed_time = Kernel.sleep(timeout)
    else
      elapsed_time = Kernel.sleep
    end
  end
end

#synchronize(&block) ⇒ Object



65
66
67
# File 'lib/quack_concurrency/mutex.rb', line 65

def synchronize(&block)
  lock(&block)
end

#try_lockBoolean

Attempts to obtain the lock and returns immediately.

Returns:

  • (Boolean)

    returns if the lock was granted

Raises:

  • (ThreadError)


71
72
73
74
75
76
77
78
79
80
81
# File 'lib/quack_concurrency/mutex.rb', line 71

def try_lock
  raise ThreadError, 'this Thread is already locking this Mutex' if owned?
  @mutex.synchronize do
    if locked?
      false
    else
      @owner = caller
      true
    end
  end
end

#unlock(&block) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/quack_concurrency/mutex.rb', line 83

def unlock(&block)
  if block_given?
    unlock
    begin
      yield
    ensure
      lock
    end
  else
    @mutex.synchronize do
      raise ThreadError, 'Mutex is not locked' unless locked?
      raise ThreadError, 'current Thread is not locking the Mutex' unless owned?
      if @condition_variable.any_waiting_threads?
        @condition_variable.signal
        
        # we do this to avoid a bug
        # consider what would happen if we set this to nil and then a thread called #lock
        #   before the resuming thread was able to set itself at the owner in #lock
        @owner = true
      else
        @owner = nil
      end
    end
    nil
  end
end

#waiting_threads_countObject



110
111
112
# File 'lib/quack_concurrency/mutex.rb', line 110

def waiting_threads_count
  @condition_variable.waiting_threads_count
end