### Question:
StoreCopyCheckPointMutex { public Resource storeCopy( ThrowingAction<IOException> beforeFirstConcurrentStoreCopy ) throws IOException { Lock readLock = lock.readLock(); boolean firstConcurrentRead = incrementCount() == 0; boolean success = false; try { if ( firstConcurrentRead ) { try { beforeFirstConcurrentStoreCopy.apply(); } catch ( IOException e ) { storeCopyActionError = e; throw e; } catch ( Throwable e ) { storeCopyActionError = e; throw new IOException( e ); } storeCopyActionCompleted = true; } else { waitForFirstStoreCopyActionToComplete(); } success = true; } finally { if ( success ) { readLock.lock(); } else { decrementCount(); } } return () -> { decrementCount(); readLock.unlock(); }; } StoreCopyCheckPointMutex();  StoreCopyCheckPointMutex( ReadWriteLock lock ); Resource storeCopy( ThrowingAction<IOException> beforeFirstConcurrentStoreCopy ); Resource tryCheckPoint(); Resource tryCheckPoint( BooleanSupplier timeoutPredicate ); Resource checkPoint();  }### Answer:
@Test public void storeCopyShouldAllowAnotherStoreCopy() throws Exception { try ( Resource lock = mutex.storeCopy( noop() ) ) { try ( Resource otherLock = mutex.storeCopy( noop() ) ) { } } }
@Test public void storeCopyShouldAllowAnotherStoreCopyButOnlyFirstShouldPerformBeforeAction() throws Exception { @SuppressWarnings( "unchecked" ) ThrowingAction<IOException> action = mock( ThrowingAction.class ); try ( Resource lock = mutex.storeCopy( action ) ) { verify( action, times( 1 ) ).apply(); try ( Resource otherLock = mutex.storeCopy( action ) ) { verify( action, times( 1 ) ).apply(); } } }
@Test public void shouldHandleMultipleConcurrentStoreCopyWhenBeforeActionPerformsCheckPoint() throws Throwable { CheckPointingAction checkPointingAction = new CheckPointingAction( mutex ); for ( int i = 0; i < 2; i++ ) { Resource firstLock = mutex.storeCopy( checkPointingAction ); assertNotNull( checkPointingAction.lock ); Resource secondLock = mutex.storeCopy( checkPointingAction ); firstLock.close(); Resource thirdLock = mutex.storeCopy( checkPointingAction ); thirdLock.close(); secondLock.close(); checkPointingAction.unlock(); } }
@Test public void shouldHandleMultipleConcurrentStoreCopyRequests() throws Throwable { Race race = new Race(); CountingAction action = new CountingAction(); int threads = Runtime.getRuntime().availableProcessors() * 10; race.addContestants( threads, throwing( () -> { parkARandomWhile(); try ( Resource lock = mutex.storeCopy( action ) ) { parkARandomWhile(); } } ) ); race.go(); assertThat( action.count(), lessThan( threads ) ); }
@Test public void shouldPropagateStoreCopyActionFailureToOtherStoreCopyRequests() throws Exception { Barrier.Control barrier = new Barrier.Control(); IOException controlledFailure = new IOException( "My own fault" ); AtomicReference<Future<Object>> secondRequest = new AtomicReference<>(); ThrowingAction<IOException> controllableAndFailingAction = () -> { secondRequest.set( t3.execute( state -> mutex.storeCopy( ASSERT_NOT_CALLED ) ) ); barrier.awaitUninterruptibly(); try { throw controlledFailure; } finally { barrier.release(); } }; Future<Object> firstRequest = t2.execute( state -> mutex.storeCopy( controllableAndFailingAction ) ); while ( secondRequest.get() == null ) { parkARandomWhile(); } t3.get().waitUntilWaiting( details -> details.isAt( StoreCopyCheckPointMutex.class, "waitForFirstStoreCopyActionToComplete" ) ); barrier.reached(); try { firstRequest.get(); } catch ( ExecutionException e ) { assertSame( controlledFailure, e.getCause() ); } try { secondRequest.get().get(); } catch ( ExecutionException e ) { Throwable cooperativeActionFailure = e.getCause(); assertThat( cooperativeActionFailure.getMessage(), containsString( "Co-operative" ) ); assertSame( controlledFailure, cooperativeActionFailure.getCause() ); } CountingAction action = new CountingAction(); try ( Resource lock = mutex.storeCopy( action ) ) { assertEquals( 1, action.count() ); } }