Extending with an exposed lock?

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Extending with an exposed lock?

Moran Avigdor
Extending a concurrent construct (for example CyclicBarrier, ThreadPoolExecutor, etc.) may impose limitations
especially when it regards to internal locking mechanisms used by the 'super' class.

My specific extended use has no additional value to this post, thus I will refrain from elaborating on it - unless interest arises.

The concurrency difficulty here is that my extended code needs to be atomic in respect to other code conducted
through the same API. If I choose an 'outer' locking it may lead to a deadlock if 'inner' (super's) locking is in await states
waiting for a condition to settle.

Exposing the lock (via getter) would impose ambiguities between different lock implementations (more specifically backport vs. 
java.util.concurrent package) - not to mention misuse.

The option that I am raising is having a protected V underLock(Callable<V> c) that will be used to delegate logic from the
extending class to be performed under the 'inner' lock used by the super implementation.
>From the extending class point of view, the 'inner' lock can be of any kind, including a synchronization of an Object used by backport.

The ThreadPoolExecutor has in this regard an afterExecute(Runnable, Throwable) and beforeExecute(Thread, Runnable) calls.
Those have been used successfully.

But, does anyone see another option under the current limitations (besides reflection to get the inner lock...)?

Thanks.
-- 
Moran Avigdor
GigaSpaces Technologies Inc.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Extending with an exposed lock?

Doug Lea
[First: Sorry that a misconfigured firewall elsewhere on our campus
was interfering with DNS, so some of you might not have gotten the
original or had a reply stall. This should now be fixed.]

Moran Avigdor wrote:

> Extending a concurrent construct (for example CyclicBarrier,
> ThreadPoolExecutor, etc.) may impose limitations
> especially when it regards to internal locking mechanisms used by the
> 'super' class.
>
> My specific extended use has no additional value to this post, thus I
> will refrain from elaborating on it - unless interest arises.
>
> The concurrency difficulty here is that my extended code needs to be
> atomic in respect to other code conducted
> through the same API. If I choose an 'outer' locking it may lead to a
> deadlock if 'inner' (super's) locking is in await states
> waiting for a condition to settle.
>
> Exposing the lock (via getter) would impose ambiguities between
> different lock implementations (more specifically backport vs.
> java.util.concurrent package) - not to mention misuse.
>
> The option that I am raising is having a /protected V
> underLock(Callable<V> c) /that will be used to delegate logic from the
> extending class to be performed under the 'inner' lock used by the super
> implementation.
>  >From the extending class point of view, the 'inner' lock can be of any
> kind, including a synchronization of an Object used by backport.
>

 > The ThreadPoolExecutor has in this regard an afterExecute(Runnable,
 > Throwable) and beforeExecute(Thread, Runnable) calls.
 > Those have been used successfully.
 >
 > But, does anyone see another option under the current limitations
 > (besides reflection to get the inner lock...)?
 >


There are several different kinds of cases here:

1. Components that do no internal blocking. In these, you can, if
desired, wrap all usages in an outer lock. This will probably
decrease throughput, but leaves you in control. This applies
for example to ConcurrentSkipLists, which don't even use locks
internally, so support a lot of internal concurrency. Using
an external before/after-style lock would disable internal
concurrency, but with no other effects on liveness.

2. Components like Semaphore, CountDownLatch, SynchronousQueue,
and Exchanger that may do internal blocking, but without using locks.
For these, you'd never want to "extend" the atomicity of methods even
with before/after locks, since a thread may block while holding
lock, preventing others from calling methods that would unblock
it (this is an analog of nested monitor lockout). In these cases,
the only kind of external before/after extensions you can use
with blocking methods are those that both acquire and release
external lock both before and after. As in:

class MySemaphore extends Semaphore {
   private synchronized void before() { ... stuff ...}
   private synchronized void after() { ... other stuff ... }
   public void acquire() throw InterruptedException {
     before(); try { super.acquire(); } finally { after(); }
   }
}

This might seem overly restrictive, but I don't know of use
cases where you'd want to do anything beyond this.

3. Components that are like (2) but just so happen to use
Lock/Condition for waiting. At the moment, this includes
CyclicBarrier, ArrayBlockingQueue, PriorityBlockingQueue.
But all of these might at any time change to instead use
strategies in (2), which are usually more efficient and
desirable on other grounds, so we really don't want to
expose locks in these cases and preclude further improvements.
So, in these cases, you also can only use the above style.
(There are also in-between cases like current LinkedBlockingQueue,
that use locks and conditions, but still permit some internal
concurrency.)


Can you live with this?

-Doug
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Extending with an exposed lock?

Moran Avigdor
Thank you for the elaborated response.
 
Components that are like (2) that use Lock/Condition for waiting are those that at the moment require some intrusive measures on my part.
The synchronized before would in my case block since the super calls wait on its internal lock.

If the incentive is to move to non-locking then I can live with the proposed solution.
Till then, the only solution is to acquire the super internal lock, and lock (or synchronize for 1.4 backwards compatibility) before hand, and release
afterwards.

Something in the form of:

   /**
     * calls CyclicBarrier.await(..) under the lock guarding the barrier entry. considers both
     * concurrent package and backport implementations of lock.
     */
    private int doUnderLock(long timeout, TimeUnit unit)   
        throws InterruptedException,
        BrokenBarrierException,
        TimeoutException {

        //concurrent package uses ReentrantLock as lock
        if (lockClass == Lock.class) {
            final Lock lock = (Lock)this.lock;
          lock.lock();
          try {
              //do internal await logic before call to super.await(timeout, unit)
              return dowait(timeout, unit);
            } finally {
             lock.unlock();
          }
        }else { //backport package uses Object as lock
            synchronized(lock) {
                //do internal await logic before call to super.await(timeout, unit)
                return dowait(timeout, unit);
            }
        }
    }

Thanks for your time.
-- 
Moran Avigdor
GigaSpaces Technologies Inc.


Doug Lea wrote:
[First: Sorry that a misconfigured firewall elsewhere on our campus
was interfering with DNS, so some of you might not have gotten the
original or had a reply stall. This should now be fixed.]

Moran Avigdor wrote:
Extending a concurrent construct (for example CyclicBarrier, ThreadPoolExecutor, etc.) may impose limitations
especially when it regards to internal locking mechanisms used by the 'super' class.

My specific extended use has no additional value to this post, thus I will refrain from elaborating on it - unless interest arises.

The concurrency difficulty here is that my extended code needs to be atomic in respect to other code conducted
through the same API. If I choose an 'outer' locking it may lead to a deadlock if 'inner' (super's) locking is in await states
waiting for a condition to settle.

Exposing the lock (via getter) would impose ambiguities between different lock implementations (more specifically backport vs. java.util.concurrent package) - not to mention misuse.

The option that I am raising is having a /protected V underLock(Callable<V> c) /that will be used to delegate logic from the
extending class to be performed under the 'inner' lock used by the super implementation.
 >From the extending class point of view, the 'inner' lock can be of any kind, including a synchronization of an Object used by backport.


> The ThreadPoolExecutor has in this regard an afterExecute(Runnable,
> Throwable) and beforeExecute(Thread, Runnable) calls.
> Those have been used successfully.
>
> But, does anyone see another option under the current limitations
> (besides reflection to get the inner lock...)?
>


There are several different kinds of cases here:

1. Components that do no internal blocking. In these, you can, if
desired, wrap all usages in an outer lock. This will probably
decrease throughput, but leaves you in control. This applies
for example to ConcurrentSkipLists, which don't even use locks
internally, so support a lot of internal concurrency. Using
an external before/after-style lock would disable internal
concurrency, but with no other effects on liveness.

2. Components like Semaphore, CountDownLatch, SynchronousQueue,
and Exchanger that may do internal blocking, but without using locks.
For these, you'd never want to "extend" the atomicity of methods even
with before/after locks, since a thread may block while holding
lock, preventing others from calling methods that would unblock
it (this is an analog of nested monitor lockout). In these cases,
the only kind of external before/after extensions you can use
with blocking methods are those that both acquire and release
external lock both before and after. As in:

class MySemaphore extends Semaphore {
  private synchronized void before() { ... stuff ...}
  private synchronized void after() { ... other stuff ... }
  public void acquire() throw InterruptedException {
    before(); try { super.acquire(); } finally { after(); }
  }
}

This might seem overly restrictive, but I don't know of use
cases where you'd want to do anything beyond this.

3. Components that are like (2) but just so happen to use
Lock/Condition for waiting. At the moment, this includes
CyclicBarrier, ArrayBlockingQueue, PriorityBlockingQueue.
But all of these might at any time change to instead use
strategies in (2), which are usually more efficient and
desirable on other grounds, so we really don't want to
expose locks in these cases and preclude further improvements.
So, in these cases, you also can only use the above style.
(There are also in-between cases like current LinkedBlockingQueue,
that use locks and conditions, but still permit some internal
concurrency.)


Can you live with this?

-Doug



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest