Class: Gin::RWLock

Inherits:
Object
  • Object
show all
Defined in:
lib/gin/rw_lock.rb

Overview

Read-Write lock pair for accessing data that is mostly read-bound. Reading is done without locking until a write operation is started.

lock = Gin::RWLock.new
lock.write_sync{ write_to_the_object }
value = lock.read_sync{ read_from_the_object }

The RWLock is built to work primarily in Thread-pool type environments and its effectiveness is much less for Thread-spawn models.

RWLock also shows increased performance in GIL-less Ruby implementations such as Rubinius 2.x.

Using write_sync from inside a read_sync block is safe, but the inverse isn’t:

lock = Gin::RWLock.new

# This is OK.
lock.read_sync do
  get_value || lock.write_sync{ update_value }
end

# This is NOT OK and will raise a ThreadError.
# It's also not necessary because read sync-ing is inferred
# during write syncs.
lock.write_sync do
  update_value
  lock.read_sync{ get_value }
end

Defined Under Namespace

Classes: WriteTimeout

Constant Summary collapse

TIMEOUT_MSG =
"Took too long to lock all config mutexes. \
Try increasing the value of Config#write_timeout."

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(write_timeout = nil) ⇒ RWLock

Returns a new instance of RWLock.



43
44
45
46
47
48
# File 'lib/gin/rw_lock.rb', line 43

def initialize write_timeout=nil
  @wmutex        = Mutex.new
  @write_timeout = write_timeout || 0.05
  @mutex_id      = :"rwlock_#{self.object_id}"
  @mutex_owned_id = :"#{@mutex_id}_owned"
end

Instance Attribute Details

#write_timeoutObject

The amount of time to wait for writer threads to get all the read locks.



40
41
42
# File 'lib/gin/rw_lock.rb', line 40

def write_timeout
  @write_timeout
end

Instance Method Details

#read_syncObject



88
89
90
91
92
93
94
# File 'lib/gin/rw_lock.rb', line 88

def read_sync
  was_locked = read_mutex.locked?
  read_mutex.lock unless was_locked
  yield
ensure
  read_mutex.unlock if was_locked
end

#write_syncObject



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/gin/rw_lock.rb', line 51

def write_sync
  lock_mutexes = []
  relock_curr  = false
  was_locked   = Thread.current[@mutex_owned_id]

  curr_mutex = read_mutex

  write_mutex.lock unless was_locked
  Thread.current[@mutex_owned_id] = true

  # Protect against same-thread deadlocks
  if curr_mutex && curr_mutex.locked?
    relock_curr = curr_mutex.unlock rescue false
  end

  start = Time.now

  Thread.list.each do |t|
    mutex = t[@mutex_id]
    next if !mutex || !relock_curr && t == Thread.current
    until mutex.try_lock
      raise WriteTimeout, TIMEOUT_MSG if Time.now - start > @write_timeout
    end
    lock_mutexes << mutex
  end

  yield
ensure
  lock_mutexes.each(&:unlock)
  curr_mutex.try_lock if relock_curr
  unless was_locked
    Thread.current[@mutex_owned_id] = false
    write_mutex.unlock
  end
end