c++concurrencymutexreaderwriterlock

Reader Writer Lock supporting low priority writers


I am trying to find (or implement) a Reader/Writer Lock which supports low-priority writers, but have been unsuccessful in researching any existing solutions.

What I mean by low-priority writer is: "will yield its place in line" to incoming readers or normal writers.

Certainly will lead to starvation if there is a constant stream of readers, but this could be solved either with a timed lock variant ("try timed low priority writer lock" and then switching to a normal lock on timeout) or by changing the way readers are issued (perhaps periodically halting reads for a short window).

If there is any literature describing something along these lines, I have not found it.

If there is a known (correct!) solution leveraging regular locks, I would would appreciate a description.


Solution

  • I don't know anything 100% like your proposal, but there's some existing interfaces that are close:

    Many existing r/w lock APIs have a "try lock" interface, like pthread_rwlock_trywrlock() on UN*X systems. These are wait-free and will only acquire the lock if noone owns it nor waits for it already.

    You'd normally use this spinning for the lock, and/or artificially delaying the try code (back off). I.e. have code like:

    for (count = 0; count < MAX_SPINS && (locked = trywrlock(lock)); count++);
    if (locked) {
        delay(some_backoff);
        wrlock(lock);         /* or try spinning loop above again ? */
    }
    

    Unfortunately this isn't exactly what you ask for; it'll get the lock late alright, but the low-prio writer will spin on CPU and/or get it in a delayed fashion due to the (possibly unnecessary) backoff.

    The Solaris kernel has an interface rw_tryupgrade(9f) that can be used to test if the current thread is the only reader on the lock with no writer waiting and if so, upgrade the lock to exclusive/write, i.e. you'd code like:

    if (!rw_tryenter(lock, RW_WRITER)) {
        rw_enter(lock, RW_READER);    /* this might wait for a writer */
        if (!rw_tryupgrade(lock)) {   /* this fails if >1 reader / writer waiting */
            /* do what you must to if you couldn't get in immediately */
        }
    }
    

    Which is a bit closer to what you ask for but still not quite the same - if it fails, you'd have to drop the readlock, possibly back off (wait), reaquire the readlock, try to upgrade. That process again amounts to spinning.

    Also, many UNIX systems at least actually perform waiter wakeups in order of scheduling priority; so you'd have to make your thread lowest priority (if necessarily, artificially) before attempting an ordinary, waiting wrlock() call; whoever else wants the same writelock while your thread is waiting will get it before that, by virtue of how the scheduler works. Though on multiprocessor/core systems that's not necessarily guaranteed ...

    Finally, SymbianOS (the Symbian^3 version) has a RRWlock class that can be made to prioritize readers over writers, so that it will deliberately starve writers if there are readers waiting / coming in. Again, not quite exactly the behaviour you want, as it affects all writers on a given lock, not just a specific one.

    I'm afraid you'd have to write your own prioritized lock, with two writer wakeup queues.