Class: QuackConcurrency::ReentrantMutex

Inherits:
ConcurrencyTool show all
Defined in:
lib/quack_concurrency/reentrant_mutex.rb,
lib/quack_concurrency/reentrant_mutex/error.rb

Defined Under Namespace

Classes: Error

Instance Method Summary collapse

Methods inherited from ConcurrencyTool

#setup_duck_types

Constructor Details

#initialize(duck_types: nil) ⇒ ReentrantMutex

Creates a new QuackConcurrency::ReentrantMutex concurrency tool.

Parameters:

  • duck_types (Hash) (defaults to: nil)

    hash of core Ruby classes to overload. If a Hash is given, the keys :condition_variable and :mutex must be present.



11
12
13
14
15
16
17
# File 'lib/quack_concurrency/reentrant_mutex.rb', line 11

def initialize(duck_types: nil)
  classes = setup_duck_types(duck_types)
  @condition_variable = classes[:condition_variable].new
  @mutex = classes[:mutex].new
  @owner = nil
  @lock_depth = 0
end

Instance Method Details

#lockvoid

This method returns an undefined value.

Locks this QuackConcurrency::ReentrantMutex. Will block until available.



21
22
23
24
25
26
27
28
29
# File 'lib/quack_concurrency/reentrant_mutex.rb', line 21

def lock
  @mutex.synchronize do
    @condition_variable.wait(@mutex) if @owner && @owner != caller
    raise 'internal error, invalid state' if @owner && @owner != caller 
    @owner = caller
    @lock_depth += 1
  end
  nil
end

#locked?Boolean

Checks if this QuackConcurrency::ReentrantMutex is locked by some thread.

Returns:

  • (Boolean)


33
34
35
# File 'lib/quack_concurrency/reentrant_mutex.rb', line 33

def locked?
  !!@owner
end

#locked_out?Boolean

Checks if this QuackConcurrency::ReentrantMutex is locked by a thread other than the caller.

Returns:

  • (Boolean)


39
40
41
# File 'lib/quack_concurrency/reentrant_mutex.rb', line 39

def locked_out?
  @mutex.synchronize { locked? && @owner != caller }
end

#owned?Boolean

Checks if this QuackConcurrency::ReentrantMutex is locked by the calling thread.

Returns:

  • (Boolean)


45
46
47
# File 'lib/quack_concurrency/reentrant_mutex.rb', line 45

def owned?
  @owner == caller
end

#sleep(timeout = nil) ⇒ void

This method returns an undefined value.

Releases the lock and sleeps. When the calling thread is next woken up, it will attempt to reacquire the lock.

Parameters:

  • timeout (Integer) (defaults to: nil)

    seconds to sleep, nil will sleep forever

Raises:



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

def sleep(timeout = nil)
  unlock
  # i would rather not need to get a ducktype for sleep so we will just take
  #   advantage of Mutex's sleep method that must take it into account already
  @mutex.synchronize do
    @mutex.sleep(timeout)
  end
  nil
ensure
  lock unless owned?
end

#synchronizeObject

Obtains a lock, runs the block, and releases the lock when the block completes.

Returns:

  • return value from yielded block



68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/quack_concurrency/reentrant_mutex.rb', line 68

def synchronize
  lock
  start_depth = @lock_depth
  start_owner = @owner
  result = yield
  result
ensure
  unless @lock_depth == start_depth && @owner == start_owner
    raise Error, 'could not unlock reentrant mutex as its state has been modified'
  end
  unlock
end

#try_lockBoolean

Attempts to obtain the lock and returns immediately.

Returns:

  • (Boolean)

    returns if the lock was granted



83
84
85
86
87
88
89
90
# File 'lib/quack_concurrency/reentrant_mutex.rb', line 83

def try_lock
  @mutex.synchronize do
    return false if @owner && @owner != caller
    @owner = caller
    @lock_depth += 1
    true
  end
end

#unlockvoid

This method returns an undefined value.

Releases the lock.

Raises:



95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/quack_concurrency/reentrant_mutex.rb', line 95

def unlock
  @mutex.synchronize do
    raise Error, 'can not unlock reentrant mutex, it is not locked' if @lock_depth == 0
    raise Error, 'can not unlock reentrant mutex, caller is not the owner' unless @owner == caller
    @lock_depth -= 1
    if @lock_depth == 0
      @owner = nil
      @condition_variable.signal
    end
  end
  nil
end