Class: Rex::ReadWriteLock

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/sync/read_write_lock.rb

Overview

This class implements a read/write lock synchronization primitive. It is meant to allow for more efficient access to resources that are more often read from than written to and many times can have concurrent reader threads. By allowing the reader threads to lock the resource concurrently rather than serially, a large performance boost can be seen. Acquiring a write lock results in exclusive access to the resource and thereby prevents any read operations during the time that a write lock is acquired. Only one write lock may be acquired at a time.

Instance Method Summary collapse

Constructor Details

#initializeReadWriteLock

Initializes a reader/writer lock instance.



23
24
25
26
27
28
29
# File 'lib/rex/sync/read_write_lock.rb', line 23

def initialize
	@read_sync_mutex  = Mutex.new
	@write_sync_mutex = Mutex.new
	@exclusive_mutex  = Mutex.new
	@readers          = 0
	@writer           = false
end

Instance Method Details

#lock_readObject

Acquires the read lock for the calling thread.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/rex/sync/read_write_lock.rb', line 34

def lock_read
	read_sync_mutex.lock

	begin
		# If there are a non-zero number of readers and a
		# writer is waiting to acquire the exclusive lock,
		# free up the sync mutex temporarily and lock/unlock
		# the exclusive lock.  This is to give the writer
		# thread a chance to acquire the lock and prevents
		# it from being constantly starved.
		if ((@readers > 0) and
		    (@writer))
			read_sync_mutex.unlock
			exclusive_mutex.lock
			exclusive_mutex.unlock
			read_sync_mutex.lock
		end

		# Increment the active reader count
		@readers += 1

		# If we now have just one reader, acquire the exclusive
		# lock.  Track the thread owner so that we release the
		# lock from within the same thread context later on.
		if (@readers == 1)
			exclusive_mutex.lock

			@owner = Thread.current
		end
	ensure
		read_sync_mutex.unlock
	end
end

#lock_writeObject

Acquire the exclusive write lock.



113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/rex/sync/read_write_lock.rb', line 113

def lock_write
	write_sync_mutex.lock

	begin
		@writer = true

		exclusive_mutex.lock

		@owner  = Thread.current
	ensure
		write_sync_mutex.unlock
	end
end

#synchronize_readObject

Synchronize a block for read access.



146
147
148
149
150
151
152
153
# File 'lib/rex/sync/read_write_lock.rb', line 146

def synchronize_read
	lock_read
	begin
		yield
	ensure
		unlock_read
	end
end

#synchronize_writeObject

Synchronize a block for write access.



158
159
160
161
162
163
164
165
# File 'lib/rex/sync/read_write_lock.rb', line 158

def synchronize_write
	lock_write
	begin
		yield
	ensure
		unlock_write
	end
end

#unlock_readObject

Releases the read lock for the calling thread.



71
72
73
74
75
76
77
78
79
80
81
82
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/rex/sync/read_write_lock.rb', line 71

def unlock_read
	read_sync_mutex.lock

	begin
		unlocked = false

		# Keep looping until we've lost this thread's reader
		# lock
		while (!unlocked)
			# If there are no more readers left after this one
			if (@readers - 1 == 0)
				# If the calling thread is the owner of the exclusive
				# reader lock, then let's release it
				if (Thread.current == @owner)
					@owner = nil

					exclusive_mutex.unlock
				end
			# If there is more than one reader left and this thread is
			# the owner of the exclusive lock, then keep looping so that
			# we can eventually unlock the exclusive mutex in this thread's
			# context
			elsif (Thread.current == @owner)
				read_sync_mutex.unlock

				next
			end

			# Unlocked!
			unlocked = true

			# Decrement the active reader count
			@readers -= 1
		end
	ensure
		read_sync_mutex.unlock
	end
end

#unlock_writeObject

Release the exclusive write lock.



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/rex/sync/read_write_lock.rb', line 130

def unlock_write
	# If the caller is not the owner of the write lock, then someone is
	# doing something broken, let's let them know.
	if (Thread.current != @owner)
		raise RuntimeError, "Non-owner calling thread attempted to release write lock", caller
	end

	# Otherwise, release the exclusive write lock
	@writer = false

	exclusive_mutex.unlock
end