Lock.isLockedByCurrentThread()

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

Lock.isLockedByCurrentThread()

Howard Lewis Ship
I've been doing some work using JDK 1.5 concurrency, as well as AspectJ.

My goal is to handle synchronization via annotations with AspectJ, i.e.

private int _counter;

@Synchronized.Read
public int getCounter() { return _counter; }

@Synchronized.Write
public void incrementCounter() { _counter++; }


This appears to be working quite well, but I had some issues
implementing it.  At the core of this is a ReentrantReadWriteLock.

First, if an @Read method invokes an @Write method (even indirectly),
the aspect takes care of temporarily releasing the read lock before
acquiring the write lock.

Secondly, if a @Read method invokes another @Read method (even
indirectly) I don't want
to re-lock the ReadWriteLock's read lock. Without this code, a thread
that goes through multiple @Read methods before hitting a @Write
method will hang, because only a single read lock will be released,
rather than the set of them (based on the number of chained method
calls).

To handle this case I had to resort to a ThreadLocal companion to the
ReentranReadWriteLock, to track whether the read lock was, in fact,
locked.  @Read methods only lock the read lock if the flag is clear.
@Write methods know to release the read lock before lock the write
lock.

This would be greatly simplified if Lock included a
isLockedByCurrentThread() method.

Is this something that could be added to API?

For your interest, I've included the aspect source below (ASL 2.0 license):

package org.apache.tapestry.internal.aspects;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.tapestry.internal.annotations.Synchronized;
import org.aspectj.lang.JoinPoint;

/**
 * Manages multi-threaded access to methods of an object instance using a
 * {@link java.util.concurrent.locks.ReentrantReadWriteLock}, driven by the
 * {@link Synchronized.Read} and {@link Synchronized.Write}
annotations. Methods that have the
 * Synchronized.Read annotation witll be advised to obtain and release
the read lock around their
 * execution. Methods with the Synchronized.Write annotation will
obtain and release the write lock
 * around their execution. Methods annotated with Synchronized.Read
that call a method annotated
 * with Synchronized.Write (within the same instance) will release the
read lock before invoking the
 * method.
 * <p>
 * This implementation makes use of a ThreadLocal to determine if the
current thread has acquired
 * the read lock (or not). This prevents the read lock from being
re-locked, and allows the aspect
 * to release the read lock temporarily when acquiring the write lock.
 * <p>
 * This aspect also enforces that the annotations are only applied to
instance (not static) methods,
 * and that a method may be either read or write, but not both.
 *
 * @author Howard M. Lewis Ship
 * @see org.apache.tapestry.internal.annotations.Synchronized
 */
public abstract aspect Synchronization extends AbstractClassTargetting
perthis(annotatedClasses())
{
    private final ReadWriteLock _lock = new ReentrantReadWriteLock();

    private final ThreadLocal<Boolean> _threadHasReadLock = new
ThreadLocal<Boolean>()
    {
        @Override
        protected Boolean initialValue()
        {
            return false;
        }
    };

    private void log(JoinPoint jp, String message)
    {
        if (false)
            System.out.println(String.format("%s\n    %s:\n    %s\n",
jp, _lock, message));
    }

    pointcut annotatedClasses() :
         targetClasses() && within(@Synchronized *);

    declare error :
        targetClasses() &&
        execution(@(Synchronized.Read || Synchronized.Write) static * *(..)) :
            "Synchronized.Read and Synchronized.Write annotations may
only be applied to instance methods.";

    declare error :
        targetClasses() &&
        execution(@(Synchronized.Read || Synchronized.Write) * *(..)) &&
        within(!@(Synchronized) Object+) :
            "The class must be annotated with Synchronized in order to
use the Synchronized.Read or Synchronized.Write annotations.";

    declare error :
        targetClasses() &&
        execution(@Synchronized.Read @Synchronized.Write * *(..)) :
            "A method may be annotated with Synchronized.Read or with
Synchronized.Write but not both.";

    pointcut writeMethods() :
            execution(@Synchronized.Write * *(..));

    pointcut readMethods() :
        execution(@Synchronized.Read * *(..));

    Object around() : readMethods()
    {
        boolean locked = _threadHasReadLock.get();

        if (!locked)
        {
            log(thisJoinPoint, "acquiring read lock");

            _lock.readLock().lock();

            _threadHasReadLock.set(true);
        }

        try
        {
            return proceed();
        }
        finally
        {
            if (!locked)
            {
                log(thisJoinPoint, "releasing read lock");

                _lock.readLock().unlock();

                _threadHasReadLock.set(false);
            }
        }
    }

    Object around() : writeMethods()
    {
        boolean locked = _threadHasReadLock.get();

        if (locked)
        {
            log(thisJoinPoint, "releasing read lock (for upgrade)");

            _lock.readLock().unlock();

            _threadHasReadLock.set(false);
        }

        log(thisJoinPoint, "acquiring write lock");

        _lock.writeLock().lock();

        try
        {
            return proceed();
        }
        finally
        {
            log(thisJoinPoint, "releasing write lock");
            _lock.writeLock().unlock();

            if (locked)
            {
                log(thisJoinPoint, "acquiring read lock (for downgrade)");

                _lock.readLock().lock();

                _threadHasReadLock.set(true);
            }
        }
    }
}


--
Howard M. Lewis Ship
Independent J2EE / Open-Source Java Consultant
Creator, Apache Tapestry
Creator, Jakarta HiveMind

Professional Tapestry training, mentoring, support
and project work.  http://howardlewisship.com

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

Re: Lock.isLockedByCurrentThread()

Doug Lea
Howard Lewis Ship wrote:

> To handle this case I had to resort to a ThreadLocal companion to the
> ReentranReadWriteLock, to track whether the read lock was, in fact,
> locked.  @Read methods only lock the read lock if the flag is clear.
> @Write methods know to release the read lock before lock the write
> lock.

For Mustang, we added new query methods that should be of use here.
See for example ReentrantReadWriteLock.getReadHoldCount. These
have been integrated into Mustang builds for a while now; see our
base source javadoc at:
http://gee.cs.oswego.edu/dl/jsr166/dist/docs/java/util/concurrent/locks/ReentrantReadWriteLock.html


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