2

In Java's implementation locking, there is no way to atomically upgrade a lock from a read lock to write lock. For example, the following code snippet fails

ReentrantReadWriteLock lock = new ...

lock.readLock().lock();

boolean mustWrite = false;
// do somestuff and determine you must instead write! :-O
if(mustWrite) {
    lock.writeLock().lock();
    writeSomeStuff();
    lock.writeLock().unlock();
}
lock.readLock.unlock();

The write lock acquisition has to wait for all read locks are done so it knows it's not overwriting data that readers might potentially be reading. That's bad. So the work around is to do something like this:

if(mustWrite) {
    lock.readLock().unlock(); // let go of read before writing
    lock.writeLock().lock();
    writeSomeStuff();
    lock.writeLock().unlock();
    lock.readLock().lock(); // get back to reading
}

But this isn't ideal - someone might go and get do something in between when you unlock the read and pick up the write. Now it's probably a good idea to double check those conditions anyway, but still - it's ugly.

Now typically, when you acquire a lock you want to force your code to wait for it to actually acquire before you go doing what you're doing. You wouldn't want to just trust your locking mechanism that it will have given you your lock before you start messing with the data.

But why does it force you to halt execution from when you've signaled that you want the lock to when you're actually read to wait? For example, why couldn't it allow something like this:

lock.writeLock().notifyIntentToLock(); // puts you in line to get the write lock
                                       // everyone else will block until you both
                                       // acquire and release the lock
lock.readLock().unlock(); // proceed with the unlock so you don't deadlock
lock.writeLock().makeGoodOnIntentToLock(); // actually acquire that lock

So in a sense, the current lock functionality could be theorized as them both being done at the same time, like

public void lock() {
    this.notifyIntentToLock();
    this.makeGoodOnIntentToLock();
}

What design decisions would make them not allow some kind of delayed intent to lock? Is there a serious problem with a lock design like that that I'm simply not seeing?

corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • A lock that lets you reserve it without waiting for it and allows you to check back later, at which time you have hopefully already obtained it and you don't have to wait any longer. I actually think that's a pretty good idea. It might have for a more complicated implementation than other kinds of locks – I'm not sure. One might be able to efficiently implement it on top of lower-level primitives, much like reader+writer locks are implemented on top of simpler primitives like mutexes and/or hardware compare-and-swap instructions. – Celada Apr 22 '13 at 22:28
  • Noob here but wouldn't such a feature encourage applications to be very greedy? For example, if I know I'm going to need a file later on in my program, I might `notifyIntentToLock` right at the start, denying any other app access to it for [potentially] much longer than actually needed. – Supericy Apr 22 '13 at 22:36
  • @Supericy Well, they certainly could. But they already can do that - they can just call `lock` right at the start. So it's not like it introduces any kind of new temptation to abuse the API that isn't there already. – corsiKa Apr 23 '13 at 14:07
  • @Celada, *hardware compare-and-swap instructions* - everything is implemented on top of them r LL/SC (load linked/store conditional) alternatives. Writing a simple RW lock is actually pretty hard. Java8 has StampedLock and its uses may require a loop but most of all it required adding [a load/load barrier](http://safari.ece.cmu.edu/MSPC2012/slides_posters/boehm-slides.pdf) to java to make it work properly. – bestsss May 27 '14 at 00:57

1 Answers1

0

All you have to do after the decision to take the exclusive lock is:

  • leave the read lock,
  • take the write lock and
  • check the condition again
  • based on the result, either proceed or bail out.

As for intents to take write locks, what happens when multiple concurrent intents exist? Possibly all of them have to check the initial conditions as the there is no way to ensure who would be victims (granted the lock after the winner).

There is more to that - the impl. of RW lock sucks to boot as the reads modify the metadata causing coherency traffic - hence RW locks don't scale well.

bestsss
  • 11,796
  • 3
  • 53
  • 63
  • Please, what's the difference in processing where the writeLock is used immediately ten performs (consistent) read and write, or solution above where you perform additional read with readLock and compare result anyhow with additional read within the writeLock block? A think the only different is recognition that the data has been changed, but what is an advantage opposite to single writeLock block where are the data always consistent? – Marek-A- Sep 17 '21 at 05:54