views:

297

answers:

4

I have several classes that implement some interface. The interface has a contract, that some methods should be synchronized, and some should not, and I want to verify that contract through unit tests for all the implementations. The methods should use the synchronized keyword or be locked on this - very similar to the synchronizedCollection() wrapper. That means I should be able to observe it externally.

To continue the example of Collections.synchronizedCollection() if I have one thread calling iterator(), I should still be able to get into methods like add() with another thread because iterator() should not do any locking. On the other hand, I should be able to synchronize on the collection externally and see that another thread blocks on add().

Is there a good way to test that a method is synchronized in a JUnit test? I want to avoid long sleep statements.

A: 

Using reflection, get the method's Method object, and invoke toString() on it. The "synchronized" keyword should appear in toString()'s output.

Mike W
Synchronized methods do not per se have a synchronized keyword. They can also use a `synchronized(this){...}` block.
Pindatjuh
+3  A: 

If you just want to check if a method has the synchronized modifier, aside from the obvious (looking at the source code/Javadoc), you can also use reflection.

Modifier.isSynchronized(method.getModifiers())

The more general question of testing if a method guarantees proper synchronization in all concurrency scenarios is likely to be an undecidable problem.

polygenelubricants
What if it using a `synchronized` block inside?
fastcodejava
Static analysis, byte code inspection, etc.
polygenelubricants
Hmm I see that the question description has been edited to add a lot more new information now...
polygenelubricants
+4  A: 

These are all horrible ideas, but you could do this...

1

    // Substitute this LOCK with your monitor (could be you object you are
    // testing etc.)
    final Object LOCK = new Object();
    Thread locker = new Thread() {
        @Override
        public void run() {
            synchronized (LOCK) {
                try {
                    Thread.sleep(Long.MAX_VALUE);
                } catch (InterruptedException e) {
                    System.out.println("Interrupted.");
                    return;
                }
            }
        }
    };

    locker.start();

    Thread attempt = new Thread() {
        @Override
        public void run() {
            // Do your test.
        }
    };

    attempt.start();
    try {
        long longEnough = 3000 * 1000;// It's in nano seconds

        long before = System.nanoTime();
        attempt.join(longEnough);
        long after = System.nanoTime();

        if (after - before < longEnough) {
            throw new AssertionError("FAIL");
        } else {
            System.out.println("PASS");
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return;
    }
    locker.interrupt();

2

If you know that methods on the arguments are always invoked in any implementation, you can pass a mock object that disguises as the argument and calls holdsLock().

So like:

class Mock implements Argument {
    private final Object LOCK;
    private final Argument real;
    public Mock(Object obj, Argument real){
       this.LOCK=obj;
       this.real = real;
    }

    @Overrides
    public void something(){
        System.out.println("held:"+Thread.holdsLock(LOCK));
        this.real.something();
    }

Then wait for the class to invoke something() on Argument.

Enno Shioji
would only work if the class being tested uses a public monitor
matt b
This does answer what I'm looking for but I'm concerned about the sleep statements.
Craig P. Motlin
@matt b: Well, since he says it synchronizes on "this" I think that requirement is satisfied. I have a long sleep statement though...
Enno Shioji
+1 for "horrible idea"
ammoQ
+1 I wound up using this approach. Thank you very much.
Craig P. Motlin
A: 

A big thank you to Zwei steinen for writing up the approach I used. There are a few problems in the example code that I worked through, so I thought it would be worth posting my findings here.

  • The call to join() expects a number of milliseconds, not nanoseconds.
  • The two threads must be coordinated, otherwise the attempt thread can start and finish all before the locker thread grabs the lock.
  • The attempt thread should not be started until after we record the start time. Otherwise that thread gets enough of a head start that the recorded time can be slightly less than the timeout, causing spurious failures.

Here is the synchronization test code as a Scala trait:

trait SynchronizedTestTrait
{
    val classUnderTest: AnyRef

    class Gate
    {
        val latch = new java.util.concurrent.CountDownLatch(1)

        def open()
        {
            this.latch.countDown
        }

        def await()
        {
            this.latch.await
        }
    }

    def nanoTime(code: => Unit) =
    {
        val before = System.nanoTime
        code
        val after = System.nanoTime
        after - before
    }

    def assertSynchronized(code: => Unit)
    {
        this.assertThreadSafety(threadSafe = true, millisTimeout = 10L)(code)
    }

    def assertNotSynchronized(code: => Unit)
    {
        this.assertThreadSafety(threadSafe = false, millisTimeout = 60L * 1000L)(code)
    }

    def assertThreadSafety(threadSafe: Boolean, millisTimeout: Long)(code: => Unit)
    {
        def spawn(code: => Unit) =
        {
            val result = new Thread
            {
                override def run = code
            }
            result.start()
            result
        }

        val gate = new Gate

        val lockHolderThread = spawn
        {
            this.classUnderTest.synchronized
            {
                // Don't let the other thread start until we've got the lock
                gate.open()

                // Hold the lock until interruption
                try
                {
                    Thread.sleep(java.lang.Long.MAX_VALUE)
                }
                catch
                {
                    case ignore: InterruptedException => return;
                }
            }
        }

        val measuredNanoTime = nanoTime
        {
            // Don't start until the other thread is synchronized on classUnderTest
            gate.await()
            spawn(code).join(millisTimeout, 0)
        }

        val nanoTimeout = millisTimeout * 1000L * 1000L

        Assert.assertEquals(
            "Measured " + measuredNanoTime + " ns but timeout was " + nanoTimeout + " ns.",
            threadSafe,
            measuredNanoTime > nanoTimeout)

        lockHolderThread.interrupt
        lockHolderThread.join
    }
}

Now let's say we want to test a simple class:

class MySynchronized
{
    def synch = this.synchronized{}
    def unsynch = {}
}

The test looks this:

class MySynchronizedTest extends SynchronizedTestTrait
{
    val classUnderTest = new MySynchronized


    @Test
    def synch_is_synchronized
    {
        this.assertSynchronized
        {
            this.classUnderTest.synch
        }
    }

    @Test
    def unsynch_not_synchronized
    {
        this.assertNotSynchronized
        {
            this.classUnderTest.unsynch
        }
    }
}
Craig P. Motlin