app/django/utils/synch.py
author Lennard de Rijk <ljvderijk@gmail.com>
Thu, 04 Jun 2009 22:01:57 +0200
changeset 2386 a518ea56f6b5
parent 54 03e267d67478
permissions -rw-r--r--
Fixed an issue where some access checks would fail due to missing arguments. This happens when an org document is being checked for write access when trying to show the edit link on the document's show page.

"""
Synchronization primitives:

    - reader-writer lock (preference to writers)

(Contributed to Django by eugene@lazutkin.com)
"""

try:
    import threading
except ImportError:
    import dummy_threading as threading

class RWLock:
    """
    Classic implementation of reader-writer lock with preference to writers.

    Readers can access a resource simultaneously.
    Writers get an exclusive access.

    API is self-descriptive:
        reader_enters()
        reader_leaves()
        writer_enters()
        writer_leaves()
    """
    def __init__(self):
        self.mutex     = threading.RLock()
        self.can_read  = threading.Semaphore(0)
        self.can_write = threading.Semaphore(0)
        self.active_readers  = 0
        self.active_writers  = 0
        self.waiting_readers = 0
        self.waiting_writers = 0

    def reader_enters(self):
        self.mutex.acquire()
        try:
            if self.active_writers == 0 and self.waiting_writers == 0:
                self.active_readers += 1
                self.can_read.release()
            else:
                self.waiting_readers += 1
        finally:
            self.mutex.release()
        self.can_read.acquire()

    def reader_leaves(self):
        self.mutex.acquire()
        try:
            self.active_readers -= 1
            if self.active_readers == 0 and self.waiting_writers != 0:
                self.active_writers  += 1
                self.waiting_writers -= 1
                self.can_write.release()
        finally:
            self.mutex.release()

    def writer_enters(self):
        self.mutex.acquire()
        try:
            if self.active_writers == 0 and self.waiting_writers == 0 and self.active_readers == 0:
                self.active_writers += 1
                self.can_write.release()
            else:
                self.waiting_writers += 1
        finally:
            self.mutex.release()
        self.can_write.acquire()

    def writer_leaves(self):
        self.mutex.acquire()
        try:
            self.active_writers -= 1
            if self.waiting_writers != 0:
                self.active_writers  += 1
                self.waiting_writers -= 1
                self.can_write.release()
            elif self.waiting_readers != 0:
                t = self.waiting_readers
                self.waiting_readers = 0
                self.active_readers += t
                while t > 0:
                    self.can_read.release()
                    t -= 1
        finally:
            self.mutex.release()