3

What's the difference between InterProcessMutex vs InterProcessSemaphoreMutex? The docs say InterProcessSemaphoreMutex is the same InterProcessMutex except it's not reentrant. But I don't know what reentrant means.

Glide
  • 20,235
  • 26
  • 86
  • 135

2 Answers2

7

I'm the main author of Apache Curator. Irrespective of what the docs may or may not say I'd like, for the record, to give the exact use cases for each of the two classes.

InterProcessMutex

InterProcessMutex should be used when you need to be able to lock in a re-entrant manner. This means that a given thread is said to "own" the lock once acquired and can lock it again if needed. This is useful if the thread passes the lock object around to other methods that need not be concerned if the lock has been acquired or not. Note that this also means that only the owning thread can release the lock. Here's an example:

InterProcessMutex lock = new InterProcessMutex(...);

if ( !lock.acquire(...) ) ... // if acquire failed: throw, return, etc
try {
   doWork(lock);    // doWork() can safely call lock.acquire() again on the lock
} finally {
    lock.release();
}

Once acquired, if the lock is released in a different thread than the one used to acquire the lock IllegalMonitorStateException is thrown.

InterProcessSemaphoreMutex

InterProcessSemaphoreMutex is a relaxed version of a lock that does not make note of the thread that acquired it. It has simpler semantics. Each InterProcessSemaphoreMutex instance can be acquired exactly once and must be balanced by a release (in any thread). i.e.

InterProcessSemaphoreMutex lock = new InterProcessSemaphoreMutex(...);

lock.acquire();
lock.acquire();    // this will block forever

I hope this helps. If the docs need clarification we'd appreciate a Pull Request with improvements.

Randgalt
  • 2,907
  • 1
  • 17
  • 31
  • And your definition of "re-entrancy" is not the same as the community-accepted definition. Reference https://stackoverflow.com/questions/1312259/ for instance: "*A reentrant lock is one where a process can claim the lock multiple times without blocking on itself.*" – antiduh Oct 11 '17 at 21:53
  • @antiduh InterProcessMutex meets that definition. This is getting tedious. – Randgalt Oct 11 '17 at 23:12
0

In this context, reentrancy means that a thread can call acquire on the same lock more than once and not block when calling it the second or third time. It must balance all acquires with the same number of releases, however. This StackOverflow post talks more about re-entrant locks:

What is the Re-entrant lock and concept in general?

Specific to Curator, here is what the documentation has to say about the different locks:

Shared Re-entrant lock aka InterProcessMutex:

public void acquire()
Acquire the mutex - blocking until it's available. Note: the same thread can call acquire re-entrantly. Each call to acquire must be balanced by a call to release()

Shared lock (non-reentrant) aka InterProcessSemaphoreMutex:

public void acquire()
Acquire the mutex - blocking until it's available. Must be balanced by a call to release().

In the following example, if lock were a reentrant lock, then the code below would run to completion and work normally. If lock were not a reentrant lock, the thread would deadlock while calling the second lock.acquire():

lock.acquire();
lock.acquire();
doWork();
lock.release();
lock.release();

Reentrant locks tend to be a little more costly in implementation, but easier to use.

The above pattern happens frequently when you have multiple public methods into your API that must lock, but your implementation has public methods calling other public methods. You can avoid this by having your public methods do locking and only locking, and then call a private method that assumes it is always executed under the lock; then your private methods can call other private methods without needing to acquire the lock more than once.

Edit to address @Randgalt's comments:

Curator's InterProcessMutex requires that the same thread that acquires the lock release it. InterProcessSemaphoreMutex does not. Maybe you misread what I wrote? Maybe I wasn't clear? Don't know. Anyway, this is the case.

This is patently false; neither lock allows you to release it from a thread other than the thread that acquired the lock. Furthermore, this still has nothing to do with the question of "what is re-entrancy" in this context - and in this context, re-entrancy is whether or not you can call acquire on the same lock more than once on the same thread.

InterProcessMutex.release():

public void release()
Perform one release of the mutex if the calling thread is the same thread that acquired it. If the thread had made multiple calls to acquire, the mutex will still be held when this method returns.

InterProcessSemaphoreMutex.release():

public void release()
Perform one release of the mutex if the calling thread is the same thread that acquired it.

Emphasis added. Neither lock allows you to unlock it from any thread other than the thread that owns the lock - and this makes sense, because both locks are mutexes, and that is one of the properties of a mutex.

antiduh
  • 11,853
  • 4
  • 43
  • 66
  • Thanks for the great response. Non-reentrant lock seems more prone to deadlocks. Is there a use case where it's better to use non-reentrant over reentrant lock? – Glide Oct 09 '17 at 20:52
  • Many times you are not able to release a lock from the same thread that acquired it (or you don't care). In those cases, the non-reentrant lock is the choice. – Randgalt Oct 10 '17 at 09:52
  • @Randgalt - that's not accurate. Reentrancy, in this context, is about acquiring the lock, not releasing it. Most mutex implementations specifically forbid your exact use case; they will report some kind of error if any thread other than the owning thread tries to release the lock, because that is the purpose of a mutex - to be owned by exactly one thread until that thread releases it. Your use case would be served by something a semaphore or something like it. Any attempt to use a mutex as you've described is a mistake, regardless of whether or not it happens to work. – antiduh Oct 10 '17 at 16:46
  • @antiduh Curator's InterProcessMutex requires that the same thread that acquires the lock release it. InterProcessSemaphoreMutex does not. Maybe you misread what I wrote? Maybe I wasn't clear? Don't know. Anyway, this is the case. – Randgalt Oct 10 '17 at 21:10
  • @Randgalt - see my edits; you are still completely wrong. Not only does re-entrancy not have anything to do with *releasing*, your assertion that 'InterProcessSemaphoreMutex does not [require releasing from the same thread that acquired it\]' is completely false. – antiduh Oct 10 '17 at 22:05
  • @antiduh I have no idea where you're coming from. I wrote InterProcessSemaphoreMutex so I'm pretty sure how it works :D I just double checked to make sure. You can release from any thread. Also, the JDK ReentrantLock requires releasing from the same thread that acquires. Have a look. I don't get what you're talking about. – Randgalt Oct 11 '17 at 08:06
  • @Randgalt - then you have to update your documentation, because InterProcessSemaphoreMutex explicitly says, and I quote: "`release()`: Perform one release of the mutex if the calling thread is the same thread that acquired it.". Also, your definition of re-entrancy does not match your own documentation, nor the canonical definition used by the rest of the CS community. Don't blame me for having incorrect documentation and confusing implementations. – antiduh Oct 11 '17 at 18:12
  • Apache Curator is a community supported project. We're very happy to accept Pull Requests that fix bugs in code, documentation or both. – Randgalt Oct 11 '17 at 19:27