commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Benedikt Ritter <brit...@apache.org>
Subject Re: [LANG] Handling LANG-1086 (Was: Re: svn commit: r1661762 - in /commons/proper/lang/trunk/src: changes/ main/java/org/apache/commons/lang3/concurrent/ test/java/org/apache/commons/lang3/concurrent/=
Date Tue, 03 Mar 2015 07:35:48 GMT
2015-03-02 21:34 GMT+01:00 Oliver Heger <oliver.heger@oliver-heger.de>:

>
>
> Am 02.03.2015 um 07:23 schrieb Benedikt Ritter:
> > 2015-03-01 22:20 GMT+01:00 Oliver Heger <oliver.heger@oliver-heger.de>:
> >
> >>
> >>
> >> Am 01.03.2015 um 17:52 schrieb Benedikt Ritter:
> >>> 2015-02-26 21:29 GMT+01:00 Oliver Heger <oliver.heger@oliver-heger.de
> >:
> >>>
> >>>> Hi Arthur,
> >>>>
> >>>> I don't have any principle objections against your implementation.
> >>>>
> >>>> I just don't want it as a replacement for the AtomicSafeInitializer
> >>>> class. The latter was designed and announces itself to use only atomic
> >>>> variables for its synchronization and no other types of locks. This
> >>>> premise no longer holds. If the implementation is broken and does not
> >>>> work as expected, we should deprecate and later remove it.
> >>>>
> >>>
> >>> Just deprecating the class if it has a bug (hanging threads when an
> >>> exception is thrown) doesn't seem right to me.
> >>>
> >>> How about we apply the patch and make it very clear in the release
> notes
> >>> and JavaDoc, that it's behavior has been changed?
> >>
> >> But then the name of the class is completely wrong. It is a bit like
> >>
> >> public class ClassWithoutSynchronization {
> >>     public void doSomething() {
> >>         *synchronized*(this) {
> >>             ...
> >>         }
> >>     }
> >> }
> >>
> >> For me the name AtomicSafeInitializer indicates that only atomic
> >> variables are used to ensure thread-safety. Everything else would be
> >> surprising, even if it was mentioned in JavaDoc.
> >>
> >
> > We have several issues here:
> >
> > 1. Find a name for the proposed initializer implementation. How about
> > BlockingInitizalizer?
> > 2. Fix the bug in AtomicSafeInitializer (hanging threads on exception).
> My
> > opinion is, that BusyWaitInitializer would be a much better name for this
> > class :o)
> > 3. Compare the new implementation and LazyInitializer. Is the
> > BlockingInitializer a replacement for LazyInitializer?
> Who said that the new initializer is a replacement for LazyInitializer?
>

Nobody, I was asking a question ;-)


>
> LazyInitializer is a direct implementation of the double-check idiom
> for an instance field (refer to Bloch's Effective Java). It is pretty
> lean and elegant.
>
> I assume that the new initializer behaves similar to this class: It uses
> fields with volatile semantics in the first level and synchronization if
> a conflict occurs. But it is more complex. So the question is do these
> two classes have different properties and behavior so that it makes
> sense to include both of them?
>
> Oliver
>
> >
> > Benedikt
> >
> >
> >>
> >> Oliver
> >>>
> >>> Benedikt
> >>>
> >>>
> >>>>
> >>>> What I would be interested in is a comparison between your
> >>>> implementation and LazyInitializer. How do these classes behave in
> >>>> typical scenarios and which one is the preferred option under which
> >>>> conditions?
> >>>>
> >>>> If we add your proposal as an additional initializer implementation,
> we
> >>>> will also have to update the user's guide and give our users
> appropriate
> >>>> recommendations when they should use which class.
> >>>>
> >>>> And, we will have to pick a meaningful name for your class.
> >>>>
> >>>> Oliver
> >>>>
> >>>> Am 26.02.2015 um 20:51 schrieb Arthur Naseef:
> >>>>> A few questions coming to mind here:
> >>>>>
> >>>>>    - First off, how do we move this forward?
> >>>>>       - What do we do with the new implementation?
> >>>>>       - What is a good way to address the busy-wait in the original
> >> code?
> >>>>>       - What is a good way to address the fact that all threads will
> >>>>>       busy-wait in the original code if the initialize() method
> throws
> >> an
> >>>>>       exception?
> >>>>>       - What is a good way to address the original comments which
> >>>> indicate
> >>>>>       this class will perform better when, in fact, the busy waiting
> >> may
> >>>> have a
> >>>>>       significant impact on performance?
> >>>>>    - How can we address the fears?  Honestly, I think the comment is
> >>>> less a
> >>>>>    "fear" than a belief, so how do we prove/disprove?
> >>>>>       - What is this other initializer implementation?
> >>>>>       - What are the differences in operation between the two?
> >>>>>
> >>>>> Oliver - I'm reading a lot of concerns here, but not seeing how you
> >> would
> >>>>> like to address those concerns.  Please help me on that front.
> >>>>>
> >>>>> Note, by the way, that the new implementation operates the same as
> the
> >>>>> original code, to the application using it, except in the case of an
> >>>>> exception thrown on the initialize() call, which is problematic now.
> >>>> That
> >>>>> is, this new implementation guarantees initialize() will only ever be
> >>>>> called one time, and it ensures all callers receive the result of
> that
> >>>>> initialize() method.
> >>>>>
> >>>>> Art
> >>>>>
> >>>>>
> >>>>>
> >>>>> On Mon, Feb 23, 2015 at 1:47 PM, Oliver Heger <
> >>>> oliver.heger@oliver-heger.de>
> >>>>> wrote:
> >>>>>
> >>>>>>
> >>>>>>
> >>>>>> Am 23.02.2015 um 21:35 schrieb Benedikt Ritter:
> >>>>>>> Oliver Heger has raised concerns about this commit in JIRA [1]:
> >>>>>>>
> >>>>>>>> This is a strong change in the behavior of this class. The main
> >>>> property
> >>>>>>> of atomic initializers was that they are non
> >>>>>>>> blocking. Now a blocking wait has been introduced. When there is
> so
> >>>> much
> >>>>>>> contention that the busy wait is
> >>>>>>>> actually a problem, wouln't it then be better to directly use a
> >>>> blocking
> >>>>>>> variant like lazy initializer?
> >>>>>>>
> >>>>>>> I've looked through the JavaDoc of AtomicInitializer once more. It
> >>>> says:
> >>>>>>> "Because {@code AtomicSafeInitializer} does not use synchronization
> >> at
> >>>>>> all
> >>>>>>> it probably outruns {@link LazyInitializer}, at least under low or
> >>>>>> moderate
> >>>>>>> concurrent access."
> >>>>>>>
> >>>>>>> This is the only thing I can find regarding concurrency properties
> of
> >>>>>>> AtomicInitializer. I think this still holds, doesn't it?
> >>>>>>
> >>>>>> No, the CountDownLatch is synchronized.
> >>>>>>
> >>>>>> There are multiple initializer implementations with different
> >> properties
> >>>>>> which are suitable for different use cases and scenarios. The atomic
> >>>>>> initializers are useful if you want to avoid blocking calls, but
> they
> >>>>>> might be an inferior solution under high contention.
> >>>>>>
> >>>>>> My fear is that this commit optimizes the class for a use case which
> >> can
> >>>>>> be served better by another initializer implementation which is
> >> already
> >>>>>> blocking; and this optimization has negative impact on the original
> >>>>>> intention of AtomicSafeInitializer.
> >>>>>>
> >>>>>> Oliver
> >>>>>>
> >>>>>>>
> >>>>>>> Benedikt
> >>>>>>>
> >>>>>>> [1] https://issues.apache.org/jira/browse/LANG-1086
> >>>>>>>
> >>>>>>> 2015-02-23 21:15 GMT+01:00 <britter@apache.org>:
> >>>>>>>
> >>>>>>>> Author: britter
> >>>>>>>> Date: Mon Feb 23 20:15:49 2015
> >>>>>>>> New Revision: 1661762
> >>>>>>>>
> >>>>>>>> URL: http://svn.apache.org/r1661762
> >>>>>>>> Log:
> >>>>>>>> LANG-1086: Remove busy wait from AtomicSafeInitializer.get(). This
> >>>> also
> >>>>>>>> fixes #46 from github. Thanks to github user artnaseef.
> >>>>>>>>
> >>>>>>>> Modified:
> >>>>>>>>     commons/proper/lang/trunk/src/changes/changes.xml
> >>>>>>>>
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java
> >>>>>>>>
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java
> >>>>>>>>
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java
> >>>>>>>>
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java
> >>>>>>>>
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/LazyInitializerTest.java
> >>>>>>>>
> >>>>>>>> Modified: commons/proper/lang/trunk/src/changes/changes.xml
> >>>>>>>> URL:
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/changes/changes.xml?rev=1661762&r1=1661761&r2=1661762&view=diff
> >>>>>>>>
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> ==============================================================================
> >>>>>>>> --- commons/proper/lang/trunk/src/changes/changes.xml [utf-8]
> >>>> (original)
> >>>>>>>> +++ commons/proper/lang/trunk/src/changes/changes.xml [utf-8] Mon
> >> Feb
> >>>> 23
> >>>>>>>> 20:15:49 2015
> >>>>>>>> @@ -22,6 +22,7 @@
> >>>>>>>>    <body>
> >>>>>>>>
> >>>>>>>>    <release version="3.4" date="tba" description="tba">
> >>>>>>>> +    <action issue="LANG-1086" type="update" dev="britter">Remove
> >> busy
> >>>>>>>> wait from AtomicSafeInitializer.get()</action>
> >>>>>>>>      <action issue="LANG-1081" type="fix" dev="britter"
> >>>> due-to="Jonathan
> >>>>>>>> Baker">DiffBuilder.append(String, Object left, Object right) does
> >> not
> >>>>>> do a
> >>>>>>>> left.equals(right) check</action>
> >>>>>>>>      <action issue="LANG-1055" type="fix" dev="britter"
> >>>> due-to="Jonathan
> >>>>>>>> Baker">StrSubstitutor.replaceSystemProperties does not work
> >>>>>>>> consistently</action>
> >>>>>>>>      <action issue="LANG-1082" type="add" dev="britter"
> >>>> due-to="Jonathan
> >>>>>>>> Baker">Add option to disable the "objectsTriviallyEqual" test in
> >>>>>>>> DiffBuilder</action>
> >>>>>>>>
> >>>>>>>> Modified:
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java
> >>>>>>>> URL:
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java?rev=1661762&r1=1661761&r2=1661762&view=diff
> >>>>>>>>
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> ==============================================================================
> >>>>>>>> ---
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java
> >>>>>>>> (original)
> >>>>>>>> +++
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java
> >>>>>>>> Mon Feb 23 20:15:49 2015
> >>>>>>>> @@ -16,6 +16,7 @@
> >>>>>>>>   */
> >>>>>>>>  package org.apache.commons.lang3.concurrent;
> >>>>>>>>
> >>>>>>>> +import java.util.concurrent.CountDownLatch;
> >>>>>>>>  import java.util.concurrent.atomic.AtomicReference;
> >>>>>>>>
> >>>>>>>>  /**
> >>>>>>>> @@ -62,20 +63,44 @@ public abstract class AtomicSafeInitiali
> >>>>>>>>      /** Holds the reference to the managed object. */
> >>>>>>>>      private final AtomicReference<T> reference = new
> >>>>>> AtomicReference<T>();
> >>>>>>>>
> >>>>>>>> +    /** Holds the exception that terminated the initialize()
> >> method,
> >>>> if
> >>>>>>>> an exception was thrown */
> >>>>>>>> +    private final AtomicReference<ConcurrentException>
> >> referenceExc =
> >>>>>> new
> >>>>>>>> AtomicReference<ConcurrentException>();
> >>>>>>>> +
> >>>>>>>> +    /** Latch for those threads waiting for initialization to
> >>>>>> complete. */
> >>>>>>>> +    private final CountDownLatch latch = new CountDownLatch(1);
> >>>>>>>> +
> >>>>>>>>      /**
> >>>>>>>>       * Get (and initialize, if not initialized yet) the required
> >>>> object
> >>>>>>>>       *
> >>>>>>>>       * @return lazily initialized object
> >>>>>>>>       * @throws ConcurrentException if the initialization of the
> >>>> object
> >>>>>>>> causes an
> >>>>>>>> -     * exception
> >>>>>>>> +     * exception or the thread is interrupted waiting for another
> >>>>>> thread
> >>>>>>>> to
> >>>>>>>> +     * complete the initialization
> >>>>>>>>       */
> >>>>>>>>      @Override
> >>>>>>>>      public final T get() throws ConcurrentException {
> >>>>>>>>          T result;
> >>>>>>>>
> >>>>>>>> -        while ((result = reference.get()) == null) {
> >>>>>>>> +        if ((result = reference.get()) == null) {
> >>>>>>>>              if (factory.compareAndSet(null, this)) {
> >>>>>>>> -                reference.set(initialize());
> >>>>>>>> +                try {
> >>>>>>>> +                    reference.set(result = initialize());
> >>>>>>>> +                } catch ( ConcurrentException exc ) {
> >>>>>>>> +                    referenceExc.set(exc);
> >>>>>>>> +                    throw exc;
> >>>>>>>> +                } finally {
> >>>>>>>> +                    latch.countDown();
> >>>>>>>> +                }
> >>>>>>>> +            } else {
> >>>>>>>> +                try {
> >>>>>>>> +                    latch.await();
> >>>>>>>> +                    if ( referenceExc.get() != null ) {
> >>>>>>>> +                        throw new
> >>>>>>>> ConcurrentException(referenceExc.get().getMessage(),
> >>>>>>>> referenceExc.get().getCause());
> >>>>>>>> +                    }
> >>>>>>>> +                    result = reference.get();
> >>>>>>>> +                } catch (InterruptedException intExc) {
> >>>>>>>> +                    throw new ConcurrentException("interrupted
> >>>> waiting
> >>>>>>>> for initialization to complete", intExc);
> >>>>>>>> +                }
> >>>>>>>>              }
> >>>>>>>>          }
> >>>>>>>>
> >>>>>>>>
> >>>>>>>> Modified:
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java
> >>>>>>>> URL:
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java?rev=1661762&r1=1661761&r2=1661762&view=diff
> >>>>>>>>
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> ==============================================================================
> >>>>>>>> ---
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java
> >>>>>>>> (original)
> >>>>>>>> +++
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java
> >>>>>>>> Mon Feb 23 20:15:49 2015
> >>>>>>>> @@ -18,6 +18,8 @@ package org.apache.commons.lang3.concurr
> >>>>>>>>
> >>>>>>>>  import static org.junit.Assert.assertEquals;
> >>>>>>>>  import static org.junit.Assert.assertNotNull;
> >>>>>>>> +import static org.junit.Assert.assertSame;
> >>>>>>>> +import static org.junit.Assert.assertTrue;
> >>>>>>>>
> >>>>>>>>  import java.util.concurrent.CountDownLatch;
> >>>>>>>>
> >>>>>>>> @@ -72,7 +74,41 @@ public abstract class AbstractConcurrent
> >>>>>>>>      @Test
> >>>>>>>>      public void testGetConcurrent() throws ConcurrentException,
> >>>>>>>>              InterruptedException {
> >>>>>>>> -        final ConcurrentInitializer<Object> initializer =
> >>>>>>>> createInitializer();
> >>>>>>>> +
> >>>>>>>> +        this.testGetConcurrentOptionallyWithException(false,
> null,
> >>>>>> null);
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>> +     * Tests the handling of exceptions thrown on the initialized
> >>>> when
> >>>>>>>> multiple threads execute concurrently.
> >>>>>>>> +     * Always an exception with the same message and cause should
> >> be
> >>>>>>>> thrown.
> >>>>>>>> +     *
> >>>>>>>> +     * @throws
> >>>> org.apache.commons.lang3.concurrent.ConcurrentException
> >>>>>>>> because the object under test may throw it
> >>>>>>>> +     * @throws java.lang.InterruptedException because the
> threading
> >>>> API
> >>>>>>>> my throw it
> >>>>>>>> +     */
> >>>>>>>> +    public void testGetConcurrentWithException(String
> >>>> expectedMessage,
> >>>>>>>> +                                               Exception
> >>>> expectedCause)
> >>>>>>>> +            throws ConcurrentException, InterruptedException {
> >>>>>>>> +
> >>>>>>>> +        this.testGetConcurrentOptionallyWithException(true,
> >>>>>>>> expectedMessage, expectedCause);
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>> +     * Tests whether get() can be invoked from multiple threads
> >>>>>>>> concurrently.  Supports the exception-handling case
> >>>>>>>> +     * and the normal, non-exception case.
> >>>>>>>> +     *
> >>>>>>>> +     * Always the same object should be returned, or an exception
> >>>> with
> >>>>>>>> the same message and cause should be thrown.
> >>>>>>>> +     *
> >>>>>>>> +     * @throws
> >>>> org.apache.commons.lang3.concurrent.ConcurrentException
> >>>>>>>> because the object under test may throw it
> >>>>>>>> +     * @throws java.lang.InterruptedException because the
> threading
> >>>> API
> >>>>>>>> my throw it
> >>>>>>>> +     */
> >>>>>>>> +    protected void
> testGetConcurrentOptionallyWithException(boolean
> >>>>>>>> expectExceptions, String expectedMessage,
> >>>>>>>> +
> >> Exception
> >>>>>>>> expectedCause)
> >>>>>>>> +            throws ConcurrentException, InterruptedException {
> >>>>>>>> +
> >>>>>>>> +        final ConcurrentInitializer<Object> initializer =
> >>>>>>>> expectExceptions ?
> >>>>>>>> +                createExceptionThrowingInitializer() :
> >>>>>>>> +                createInitializer();
> >>>>>>>> +
> >>>>>>>>          final int threadCount = 20;
> >>>>>>>>          final CountDownLatch startLatch = new CountDownLatch(1);
> >>>>>>>>          class GetThread extends Thread {
> >>>>>>>> @@ -106,9 +142,18 @@ public abstract class AbstractConcurrent
> >>>>>>>>          }
> >>>>>>>>
> >>>>>>>>          // check results
> >>>>>>>> -        final Object managedObject = initializer.get();
> >>>>>>>> -        for (final GetThread t : threads) {
> >>>>>>>> -            assertEquals("Wrong object", managedObject,
> t.object);
> >>>>>>>> +        if ( expectExceptions ) {
> >>>>>>>> +            for (GetThread t : threads) {
> >>>>>>>> +                assertTrue(t.object instanceof Exception);
> >>>>>>>> +                Exception exc = (Exception) t.object;
> >>>>>>>> +                assertEquals(expectedMessage, exc.getMessage());
> >>>>>>>> +                assertSame(expectedCause, exc.getCause());
> >>>>>>>> +            }
> >>>>>>>> +        } else {
> >>>>>>>> +            final Object managedObject = initializer.get();
> >>>>>>>> +            for (final GetThread t : threads) {
> >>>>>>>> +                assertEquals("Wrong object", managedObject,
> >>>> t.object);
> >>>>>>>> +            }
> >>>>>>>>          }
> >>>>>>>>      }
> >>>>>>>>
> >>>>>>>> @@ -119,4 +164,12 @@ public abstract class AbstractConcurrent
> >>>>>>>>       * @return the initializer object to be tested
> >>>>>>>>       */
> >>>>>>>>      protected abstract ConcurrentInitializer<Object>
> >>>>>> createInitializer();
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>> +     * Creates a {@link ConcurrentInitializer} object that always
> >>>>>> throws
> >>>>>>>> +     * exceptions.
> >>>>>>>> +     *
> >>>>>>>> +     * @return
> >>>>>>>> +     */
> >>>>>>>> +    protected abstract ConcurrentInitializer<Object>
> >>>>>>>> createExceptionThrowingInitializer();
> >>>>>>>>  }
> >>>>>>>>
> >>>>>>>> Modified:
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java
> >>>>>>>> URL:
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java?rev=1661762&r1=1661761&r2=1661762&view=diff
> >>>>>>>>
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> ==============================================================================
> >>>>>>>> ---
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java
> >>>>>>>> (original)
> >>>>>>>> +++
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java
> >>>>>>>> Mon Feb 23 20:15:49 2015
> >>>>>>>> @@ -16,12 +16,29 @@
> >>>>>>>>   */
> >>>>>>>>  package org.apache.commons.lang3.concurrent;
> >>>>>>>>
> >>>>>>>> +import org.junit.Test;
> >>>>>>>> +
> >>>>>>>>  /**
> >>>>>>>>   * Test class for {@code AtomicInitializer}.
> >>>>>>>>   *
> >>>>>>>>   * @version $Id$
> >>>>>>>>   */
> >>>>>>>>  public class AtomicInitializerTest extends
> >>>>>>>> AbstractConcurrentInitializerTest {
> >>>>>>>> +    private Exception testCauseException;
> >>>>>>>> +    private String testExceptionMessage;
> >>>>>>>> +
> >>>>>>>> +    public AtomicInitializerTest() {
> >>>>>>>> +        testExceptionMessage = "x-test-exception-message-x";
> >>>>>>>> +        testCauseException = new Exception(testExceptionMessage);
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +    @Test
> >>>>>>>> +    public void testGetConcurrentWithException ()
> >>>>>>>> +            throws ConcurrentException, InterruptedException {
> >>>>>>>> +
> >>>>>>>> +
> super.testGetConcurrentWithException(testExceptionMessage,
> >>>>>>>> testCauseException);
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>>      /**
> >>>>>>>>       * Returns the initializer to be tested.
> >>>>>>>>       *
> >>>>>>>> @@ -36,4 +53,20 @@ public class AtomicInitializerTest exten
> >>>>>>>>              }
> >>>>>>>>          };
> >>>>>>>>      }
> >>>>>>>> +
> >>>>>>>> +    @Override
> >>>>>>>> +    protected ConcurrentInitializer<Object>
> >>>>>>>> createExceptionThrowingInitializer() {
> >>>>>>>> +        return new
> >> ExceptionThrowingAtomicSafeInitializerTestImpl();
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>> +     * A concrete test implementation of {@code
> >>>> AtomicSafeInitializer}.
> >>>>>>>> This
> >>>>>>>> +     * implementation always throws an exception.
> >>>>>>>> +     */
> >>>>>>>> +    private class ExceptionThrowingAtomicSafeInitializerTestImpl
> >>>>>> extends
> >>>>>>>> AtomicSafeInitializer<Object> {
> >>>>>>>> +        @Override
> >>>>>>>> +        protected Object initialize() throws ConcurrentException
> {
> >>>>>>>> +            throw new ConcurrentException(testExceptionMessage,
> >>>>>>>> testCauseException);
> >>>>>>>> +        }
> >>>>>>>> +    }
> >>>>>>>>  }
> >>>>>>>>
> >>>>>>>> Modified:
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java
> >>>>>>>> URL:
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java?rev=1661762&r1=1661761&r2=1661762&view=diff
> >>>>>>>>
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> ==============================================================================
> >>>>>>>> ---
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java
> >>>>>>>> (original)
> >>>>>>>> +++
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java
> >>>>>>>> Mon Feb 23 20:15:49 2015
> >>>>>>>> @@ -17,7 +17,11 @@
> >>>>>>>>  package org.apache.commons.lang3.concurrent;
> >>>>>>>>
> >>>>>>>>  import static org.junit.Assert.assertEquals;
> >>>>>>>> +import static org.junit.Assert.assertFalse;
> >>>>>>>> +import static org.junit.Assert.assertSame;
> >>>>>>>> +import static org.junit.Assert.assertTrue;
> >>>>>>>>
> >>>>>>>> +import java.util.concurrent.CountDownLatch;
> >>>>>>>>  import java.util.concurrent.atomic.AtomicInteger;
> >>>>>>>>
> >>>>>>>>  import org.junit.Before;
> >>>>>>>> @@ -30,12 +34,19 @@ import org.junit.Test;
> >>>>>>>>   */
> >>>>>>>>  public class AtomicSafeInitializerTest extends
> >>>>>>>>          AbstractConcurrentInitializerTest {
> >>>>>>>> +
> >>>>>>>>      /** The instance to be tested. */
> >>>>>>>>      private AtomicSafeInitializerTestImpl initializer;
> >>>>>>>> +    private ExceptionThrowingAtomicSafeInitializerTestImpl
> >>>>>>>> exceptionThrowingInitializer;
> >>>>>>>> +    private Exception testCauseException;
> >>>>>>>> +    private String testExceptionMessage;
> >>>>>>>>
> >>>>>>>>      @Before
> >>>>>>>>      public void setUp() throws Exception {
> >>>>>>>>          initializer = new AtomicSafeInitializerTestImpl();
> >>>>>>>> +        exceptionThrowingInitializer = new
> >>>>>>>> ExceptionThrowingAtomicSafeInitializerTestImpl();
> >>>>>>>> +        testExceptionMessage = "x-test-exception-message-x";
> >>>>>>>> +        testCauseException = new Exception(testExceptionMessage);
> >>>>>>>>      }
> >>>>>>>>
> >>>>>>>>      /**
> >>>>>>>> @@ -49,6 +60,17 @@ public class AtomicSafeInitializerTest e
> >>>>>>>>      }
> >>>>>>>>
> >>>>>>>>      /**
> >>>>>>>> +     * Returns the exception-throwing initializer to be tested.
> >>>>>>>> +     *
> >>>>>>>> +     * @return the {@code AtomicSafeInitializer} under test when
> >>>>>>>> validating
> >>>>>>>> +     * exception handling
> >>>>>>>> +     */
> >>>>>>>> +    @Override
> >>>>>>>> +    protected ConcurrentInitializer<Object>
> >>>>>>>> createExceptionThrowingInitializer() {
> >>>>>>>> +        return exceptionThrowingInitializer;
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>>       * Tests that initialize() is called only once.
> >>>>>>>>       *
> >>>>>>>>       * @throws
> >>>> org.apache.commons.lang3.concurrent.ConcurrentException
> >>>>>>>> because {@link #testGetConcurrent()} may throw it
> >>>>>>>> @@ -62,6 +84,92 @@ public class AtomicSafeInitializerTest e
> >>>>>>>>                  initializer.initCounter.get());
> >>>>>>>>      }
> >>>>>>>>
> >>>>>>>> +    @Test
> >>>>>>>> +    public void testExceptionOnInitialize() throws
> >>>> ConcurrentException,
> >>>>>>>> +            InterruptedException {
> >>>>>>>> +
> >>>>>>>> +        testGetConcurrentWithException(testExceptionMessage,
> >>>>>>>> testCauseException);
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>> +     * Validate the handling of an interrupted exception on a
> >> thread
> >>>>>>>> waiting for another thread to finish calling the
> >>>>>>>> +     * initialize() method.
> >>>>>>>> +     *
> >>>>>>>> +     * @throws Exception
> >>>>>>>> +     */
> >>>>>>>> +    @Test(timeout = 3000)
> >>>>>>>> +    public void testInterruptedWaitingOnInitialize() throws
> >>>> Exception {
> >>>>>>>> +        this.execTestWithWaitingOnInitialize(true);
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>> +     * Test the success path of two threads reaching the
> >>>> initialization
> >>>>>>>> point at the same time.
> >>>>>>>> +     */
> >>>>>>>> +    @Test(timeout = 3000)
> >>>>>>>> +    public void testOneThreadWaitingForAnotherToInitialize ()
> >> throws
> >>>>>>>> Exception {
> >>>>>>>> +        execTestWithWaitingOnInitialize(false);
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>> +     * Execute a test that requires one thread to be waiting on
> the
> >>>>>>>> initialize() method of another thread.  This test
> >>>>>>>> +     * uses latches to guarantee the code path being tested.
> >>>>>>>> +     *
> >>>>>>>> +     * @throws Exception
> >>>>>>>> +     */
> >>>>>>>> +    protected void execTestWithWaitingOnInitialize(boolean
> >>>>>> interruptInd)
> >>>>>>>> throws Exception {
> >>>>>>>> +        final CountDownLatch startLatch = new CountDownLatch(1);
> >>>>>>>> +        final CountDownLatch finishLatch = new CountDownLatch(1);
> >>>>>>>> +        final WaitingInitializerTestImpl initializer = new
> >>>>>>>> WaitingInitializerTestImpl(startLatch, finishLatch);
> >>>>>>>> +
> >>>>>>>> +        InitializerTestThread execThread1 = new
> >>>>>>>> InitializerTestThread(initializer);
> >>>>>>>> +        InitializerTestThread execThread2 = new
> >>>>>>>> InitializerTestThread(initializer);
> >>>>>>>> +
> >>>>>>>> +        // Start the first thread and wait for it to get into the
> >>>>>>>> initialize method so we are sure it is the thread
> >>>>>>>> +        //  executing initialize().
> >>>>>>>> +        execThread1.start();
> >>>>>>>> +        startLatch.await();
> >>>>>>>> +
> >>>>>>>> +        // Start the second thread and interrupt it to force the
> >>>>>>>> InterruptedException.  There is no need to make sure
> >>>>>>>> +        //  the thread reaches the await() call before
> interrupting
> >>>> it.
> >>>>>>>> +        execThread2.start();
> >>>>>>>> +
> >>>>>>>> +        if ( interruptInd ) {
> >>>>>>>> +            // Interrupt the second thread now and wait for it to
> >>>>>>>> complete to ensure it reaches the wait inside the
> >>>>>>>> +            //  get() method.
> >>>>>>>> +            execThread2.interrupt();
> >>>>>>>> +            execThread2.join();
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        // Signal the completion of the initialize method now.
> >>>>>>>> +        finishLatch.countDown();
> >>>>>>>> +
> >>>>>>>> +        // Wait for the initialize() to finish.
> >>>>>>>> +        execThread1.join();
> >>>>>>>> +
> >>>>>>>> +        // Wait for thread2 to finish, if it was not already done
> >>>>>>>> +        if ( ! interruptInd ) {
> >>>>>>>> +            execThread2.join();
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        //
> >>>>>>>> +        // Validate: thread1 should have the valid result;
> thread2
> >>>>>> should
> >>>>>>>> have caught an interrupted exception, if
> >>>>>>>> +        //  interrupted, or should have the same result
> otherwise.
> >>>>>>>> +        //
> >>>>>>>> +        assertFalse(execThread1.isCaughtException());
> >>>>>>>> +        assertSame(initializer.getAnswer(),
> >> execThread1.getResult());
> >>>>>>>> +
> >>>>>>>> +        if ( interruptInd ) {
> >>>>>>>> +            assertTrue(execThread2.isCaughtException());
> >>>>>>>> +            Exception exc = (Exception) execThread2.getResult();
> >>>>>>>> +            assertTrue(exc.getCause() instanceof
> >>>> InterruptedException);
> >>>>>>>> +            assertEquals("interrupted waiting for initialization
> to
> >>>>>>>> complete", exc.getMessage());
> >>>>>>>> +        } else {
> >>>>>>>> +            assertFalse(execThread2.isCaughtException());
> >>>>>>>> +            assertSame(initializer.getAnswer(),
> >>>>>> execThread2.getResult());
> >>>>>>>> +        }
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>>      /**
> >>>>>>>>       * A concrete test implementation of {@code
> >>>> AtomicSafeInitializer}.
> >>>>>>>> This
> >>>>>>>>       * implementation also counts the number of invocations of
> the
> >>>>>>>> initialize()
> >>>>>>>> @@ -78,4 +186,90 @@ public class AtomicSafeInitializerTest e
> >>>>>>>>              return new Object();
> >>>>>>>>          }
> >>>>>>>>      }
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>> +     * A concrete test implementation of {@code
> >>>> AtomicSafeInitializer}.
> >>>>>>>> This
> >>>>>>>> +     * implementation always throws an exception.
> >>>>>>>> +     */
> >>>>>>>> +    private class ExceptionThrowingAtomicSafeInitializerTestImpl
> >>>>>> extends
> >>>>>>>> AtomicSafeInitializer<Object> {
> >>>>>>>> +        @Override
> >>>>>>>> +        protected Object initialize() throws ConcurrentException
> {
> >>>>>>>> +            throw new ConcurrentException(testExceptionMessage,
> >>>>>>>> testCauseException);
> >>>>>>>> +        }
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>> +     * Initializer that signals it has started and waits to
> >> complete
> >>>>>>>> until signalled in order to enable a guaranteed
> >>>>>>>> +     * order-of-operations.  This allows the test code to peg one
> >>>>>> thread
> >>>>>>>> to the initialize method for a period of time
> >>>>>>>> +     * that the test can dictate.
> >>>>>>>> +     */
> >>>>>>>> +    private class WaitingInitializerTestImpl extends
> >>>>>>>> AtomicSafeInitializer<Object> {
> >>>>>>>> +        private final CountDownLatch startedLatch;
> >>>>>>>> +        private final CountDownLatch finishLatch;
> >>>>>>>> +        private final Object answer = new Object();
> >>>>>>>> +
> >>>>>>>> +        public WaitingInitializerTestImpl(CountDownLatch
> >>>> startedLatch,
> >>>>>>>> CountDownLatch finishLatch) {
> >>>>>>>> +            this.startedLatch = startedLatch;
> >>>>>>>> +            this.finishLatch = finishLatch;
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        @Override
> >>>>>>>> +        protected Object initialize() throws ConcurrentException
> {
> >>>>>>>> +            this.startedLatch.countDown();
> >>>>>>>> +            try {
> >>>>>>>> +                this.finishLatch.await();
> >>>>>>>> +            } catch (InterruptedException intExc) {
> >>>>>>>> +                throw new ConcurrentException(intExc);
> >>>>>>>> +            }
> >>>>>>>> +
> >>>>>>>> +            return  answer;
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        public Object getAnswer () {
> >>>>>>>> +            return answer;
> >>>>>>>> +        }
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>> +     * Test executor of the initializer get() operation that
> >> captures
> >>>>>> the
> >>>>>>>> result.
> >>>>>>>> +     */
> >>>>>>>> +    private class InitializerTestThread extends Thread {
> >>>>>>>> +        private AtomicSafeInitializer<Object>   initializer;
> >>>>>>>> +        private Object result;
> >>>>>>>> +        private boolean caughtException;
> >>>>>>>> +
> >>>>>>>> +        public
> InitializerTestThread(AtomicSafeInitializer<Object>
> >>>>>>>> initializer) {
> >>>>>>>> +            super("AtomicSafeInitializer test thread");
> >>>>>>>> +            this.initializer = initializer;
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        @Override
> >>>>>>>> +        public void run() {
> >>>>>>>> +            try {
> >>>>>>>> +                this.result = initializer.get();
> >>>>>>>> +            } catch ( ConcurrentException concurrentExc ) {
> >>>>>>>> +                this.caughtException = true;
> >>>>>>>> +                this.result = concurrentExc;
> >>>>>>>> +            }
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        /**
> >>>>>>>> +         * Resulting object, if the get() method returned
> >>>> successfully,
> >>>>>>>> or exception if an exception was thrown.
> >>>>>>>> +         *
> >>>>>>>> +         * @return resulting object or exception from the get()
> >>>> method
> >>>>>>>> call.
> >>>>>>>> +         */
> >>>>>>>> +        public Object getResult () {
> >>>>>>>> +            return  this.result;
> >>>>>>>> +        }
> >>>>>>>> +
> >>>>>>>> +        /**
> >>>>>>>> +         * Determine whether an exception was caught on the get()
> >>>> call.
> >>>>>>>> Does not guarantee that the get() method was
> >>>>>>>> +         * called or completed.
> >>>>>>>> +         *
> >>>>>>>> +         * @return true => exception was caught; false =>
> exception
> >>>> was
> >>>>>>>> not caught.
> >>>>>>>> +         */
> >>>>>>>> +        public boolean  isCaughtException () {
> >>>>>>>> +            return  this.caughtException;
> >>>>>>>> +        }
> >>>>>>>> +    }
> >>>>>>>>  }
> >>>>>>>>
> >>>>>>>> Modified:
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/LazyInitializerTest.java
> >>>>>>>> URL:
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/LazyInitializerTest.java?rev=1661762&r1=1661761&r2=1661762&view=diff
> >>>>>>>>
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> ==============================================================================
> >>>>>>>> ---
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/LazyInitializerTest.java
> >>>>>>>> (original)
> >>>>>>>> +++
> >>>>>>>>
> >>>>>>
> >>>>
> >>
> commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/concurrent/LazyInitializerTest.java
> >>>>>>>> Mon Feb 23 20:15:49 2015
> >>>>>>>> @@ -17,6 +17,7 @@
> >>>>>>>>  package org.apache.commons.lang3.concurrent;
> >>>>>>>>
> >>>>>>>>  import org.junit.Before;
> >>>>>>>> +import org.junit.Test;
> >>>>>>>>
> >>>>>>>>  /**
> >>>>>>>>   * Test class for {@code LazyInitializer}.
> >>>>>>>> @@ -26,10 +27,16 @@ import org.junit.Before;
> >>>>>>>>  public class LazyInitializerTest extends
> >>>>>>>> AbstractConcurrentInitializerTest {
> >>>>>>>>      /** The initializer to be tested. */
> >>>>>>>>      private LazyInitializerTestImpl initializer;
> >>>>>>>> +    private ExceptionThrowingLazyInitializerTestImpl
> >>>>>>>> exceptionThrowingInitializer;
> >>>>>>>> +    private Exception testCauseException;
> >>>>>>>> +    private String testExceptionMessage;
> >>>>>>>>
> >>>>>>>>      @Before
> >>>>>>>>      public void setUp() throws Exception {
> >>>>>>>>          initializer = new LazyInitializerTestImpl();
> >>>>>>>> +        exceptionThrowingInitializer = new
> >>>>>>>> ExceptionThrowingLazyInitializerTestImpl();
> >>>>>>>> +        testExceptionMessage = "x-test-exception-message-x";
> >>>>>>>> +        testCauseException = new Exception(testExceptionMessage);
> >>>>>>>>      }
> >>>>>>>>
> >>>>>>>>      /**
> >>>>>>>> @@ -43,6 +50,18 @@ public class LazyInitializerTest extends
> >>>>>>>>          return initializer;
> >>>>>>>>      }
> >>>>>>>>
> >>>>>>>> +    @Override
> >>>>>>>> +    protected ConcurrentInitializer<Object>
> >>>>>>>> createExceptionThrowingInitializer() {
> >>>>>>>> +        return exceptionThrowingInitializer;
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>> +    @Test
> >>>>>>>> +    public void testGetConcurrentWithException ()
> >>>>>>>> +            throws ConcurrentException, InterruptedException {
> >>>>>>>> +
> >>>>>>>> +
> super.testGetConcurrentWithException(testExceptionMessage,
> >>>>>>>> testCauseException);
> >>>>>>>> +    }
> >>>>>>>> +
> >>>>>>>>      /**
> >>>>>>>>       * A test implementation of LazyInitializer. This class
> >> creates a
> >>>>>>>> plain
> >>>>>>>>       * Object. As Object does not provide a specific equals()
> >> method,
> >>>>>> it
> >>>>>>>> is easy
> >>>>>>>> @@ -55,4 +74,16 @@ public class LazyInitializerTest extends
> >>>>>>>>              return new Object();
> >>>>>>>>          }
> >>>>>>>>      }
> >>>>>>>> +
> >>>>>>>> +
> >>>>>>>> +    /**
> >>>>>>>> +     * A concrete test implementation of {@code
> >>>> AtomicSafeInitializer}.
> >>>>>>>> This
> >>>>>>>> +     * implementation always throws an exception.
> >>>>>>>> +     */
> >>>>>>>> +    private class ExceptionThrowingLazyInitializerTestImpl
> extends
> >>>>>>>> LazyInitializer<Object> {
> >>>>>>>> +        @Override
> >>>>>>>> +        protected Object initialize() throws ConcurrentException
> {
> >>>>>>>> +            throw new ConcurrentException(testExceptionMessage,
> >>>>>>>> testCauseException);
> >>>>>>>> +        }
> >>>>>>>> +    }
> >>>>>>>>  }
> >>>>>>>>
> >>>>>>>>
> >>>>>>>>
> >>>>>>>
> >>>>>>>
> >>>>>>
> >>>>>>
> ---------------------------------------------------------------------
> >>>>>> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> >>>>>> For additional commands, e-mail: dev-help@commons.apache.org
> >>>>>>
> >>>>>>
> >>>>>
> >>>>
> >>>> ---------------------------------------------------------------------
> >>>> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> >>>> For additional commands, e-mail: dev-help@commons.apache.org
> >>>>
> >>>>
> >>>
> >>>
> >>
> >> ---------------------------------------------------------------------
> >> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> >> For additional commands, e-mail: dev-help@commons.apache.org
> >>
> >>
> >
> >
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>
>


-- 
http://people.apache.org/~britter/
http://www.systemoutprintln.de/
http://twitter.com/BenediktRitter
http://github.com/britter

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message