@Override
      public void run2() {
        remoteLoaderInvoked = false;
        loaderInvoked = false;
        RegionFactory<Object, Object> factory = getCache().createRegionFactory();
        factory.setScope(Scope.DISTRIBUTED_ACK);
        factory.setConcurrencyChecksEnabled(true);
        factory.setCacheLoader(new CacheLoader<Object, Object>() {
          boolean firstInvocation = true;

          @Override
          public synchronized Object load(LoaderHelper helper) {
            System.out.println("invoked cache loader for " + helper.getKey());
            loaderInvoked = true;
            loaderInvokedLatch.countDown();
            if (firstInvocation) {
              firstInvocation = false;
              try {
                // wait for both threads to be ready for the exception to be thrown
                System.out.println("waiting for vm0t2 to be ready before throwing exception");
                readyForExceptionLatch.await(30, TimeUnit.SECONDS);
                // give the second thread time to get into loader code
                Thread.sleep(5000);
              } catch (InterruptedException e) {
                fail("interrupted");
              }
              System.out.println("throwing exception");
              throw new RuntimeException(exceptionString);
            }
            System.out.println("returning value=" + value);
            return value;
          }

          @Override
          public void close() {}
        });

        Region<Object, Object> region = createRegion(name, factory);
        region.create(objectName, null);
        IgnoredException.addIgnoredException(exceptionString);
      }
This test is for a bug in which a cache loader threw an exception that caused the wrong value
to be put in a Future in nonTxnFindObject. This in turn caused a concurrent search for the
object to not invoke the loader a second time.
<p>
VM0 is used to create a cache and a region having a loader that simulates the conditions that
caused the bug. One async thread then does a get() which invokes the loader. Another async
thread does a get() which reaches nonTxnFindObject and blocks waiting for the first thread's
load to complete. The loader then throws an exception that is sent back to the first thread.
The second thread should then cause the loader to be invoked again, and this time the loader
will return a value. Both threads then validate that they received the expected result.
geode-core/src/distributedTest/java/org/apache/geode/cache30/SearchAndLoadDUnitTest.java