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 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
49
# 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"
  @rmutex_owned_id = :"#{@mutex_id}_r_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


81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/gin/rw_lock.rb', line 81

def read_sync
  was_locked = Thread.current[@rmutex_owned_id]
  unless was_locked
    read_mutex.lock
    Thread.current[@rmutex_owned_id] = true
  end
  yield
ensure
  if !was_locked
    Thread.current[@rmutex_owned_id] = false
    read_mutex.unlock
  end
end

#write_syncObject


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
# File 'lib/gin/rw_lock.rb', line 52

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

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

  start = Time.now

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

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