# Copyright Mateusz Kobos, (c) 2011
# https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/
# released under the MIT licence
import threading
__author__ =
"Mateusz Kobos"
class RWLock:
"""
Read-Write locking primitive
Synchronization object used
in a solution of so-called second
readers-writers problem.
In this problem, many readers can simultaneously
access a share,
and a writer has an exclusive access to this share.
Additionally, the following constraints should be met:
1) no reader should be kept waiting
if the share
is currently opened
for
reading unless a writer
is also waiting
for the share,
2) no writer should be kept waiting
for the share longer than absolutely
necessary.
The implementation
is based on [
1, secs.
4.
2.
2,
4.
2.
6,
4.
2.
7]
with a modification -- adding an additional lock (C{self.__readers_queue})
--
in accordance
with [
2].
Sources:
[
1] A.B. Downey:
"The little book of semaphores", Version
2.
1.
5,
2008
[
2] P.J. Courtois, F. Heymans, D.L. Parnas:
"Concurrent Control with 'Readers' and 'Writers'",
Communications of the ACM,
1971 (via [
3])
[
3]
http://en.wikipedia.org/wiki/Readers-writers_problem
"""
def __init__(self):
"""
A lock giving an even higher priority to the writer
in certain
cases (see [
2]
for a discussion).
"""
self.__read_switch = _LightSwitch()
self.__write_switch = _LightSwitch()
self.__no_readers = threading.Lock()
self.__no_writers = threading.Lock()
self.__readers_queue = threading.Lock()
def reader_acquire(self):
self.__readers_queue.acquire()
self.__no_readers.acquire()
self.__read_switch.acquire(self.__no_writers)
self.__no_readers.release()
self.__readers_queue.release()
def reader_release(self):
self.__read_switch.release(self.__no_writers)
def writer_acquire(self):
self.__write_switch.acquire(self.__no_readers)
self.__no_writers.acquire()
def writer_release(self):
self.__no_writers.release()
self.__write_switch.release(self.__no_readers)
class _LightSwitch:
"""An auxiliary "light switch
"-like object. The first thread turns on the
"switch", the last one turns it off (see [
1, sec.
4.
2.
2]
for details).
"""
def __init__(self):
self.__counter =
0
self.__mutex = threading.Lock()
def acquire(self, lock):
self.__mutex.acquire()
self.__counter +=
1
if self.__counter ==
1:
lock.acquire()
self.__mutex.release()
def release(self, lock):
self.__mutex.acquire()
self.__counter -=
1
if self.__counter ==
0:
lock.release()
self.__mutex.release()