/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2016, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.referencing.factory.epsg;
import java.awt.geom.Rectangle2D;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.geotools.factory.GeoTools;
import org.geotools.factory.Hints;
import org.geotools.geometry.Envelope2D;
import org.geotools.referencing.CRS;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.operation.CoordinateOperationFactory;
import org.opengis.referencing.operation.OperationNotFoundException;
import org.opengis.referencing.operation.TransformException;
/**
* Unit test for <a href="https://jira.codehaus.org/browse/GEOT-4780">GEOT-4780</>.<br>
* Detailled description :<br>
* - One thread continuously retrieves the {@link CoordinateOperationFactory} through the {@link ReferencingFactoryFinder}.<br>
* - Another thread performs {@link #NUM_ITERATIONS} reprojections using CRS for which the HSQL database indicates a NTv2Transform.<br>
* When the second thread ends, the first one is stopped.<br>
* Using multiple iterations, we quite always produce a deadlock.<br>
* Note that I could not reproduce the problem with {@link #LENIENT} set to <code>true</code> here, but I could in another application.<br>
* <br>
*
*
* @author Stephane Wasserhardt
*/
public class ThreadedTransformTest {
private static final int NUM_ITERATIONS = 1000;
private static final Boolean LENIENT = false;
private static final Hints HINTS = GeoTools.getDefaultHints();
static {
HINTS.put(Hints.LENIENT_DATUM_SHIFT, LENIENT);
}
private final ExecutorService EXECUTOR = Executors.newCachedThreadPool();
private CoordinateReferenceSystem nad83;
private CoordinateReferenceSystem wgs84;
private Envelope2D envelope;
/**
* Instantiates the test data.
*
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
nad83 = CRS.decode("EPSG:4269");
wgs84 = CRS.decode("EPSG:4326");
envelope = new Envelope2D(wgs84, new Rectangle2D.Double(-77.145996, 39.04541, 0.1, 0.1));
}
@After
public void tearDown() throws Exception {
EXECUTOR.shutdown();
}
protected void retrieve() {
for (int iter = 0; iter < NUM_ITERATIONS; iter++) {
ReferencingFactoryFinder.getCoordinateOperationFactory(HINTS);
}
}
protected void transform() throws URISyntaxException, OperationNotFoundException,
FactoryException, TransformException {
for (int iter = 0; iter < NUM_ITERATIONS; iter++) {
CoordinateOperationFactory coordinateOperationFactory;
coordinateOperationFactory = ReferencingFactoryFinder
.getCoordinateOperationFactory(HINTS);
// The problem also appears using :
// coordinateOperationFactory = CRS
// .getCoordinateOperationFactory(LENIENT);
final CoordinateOperation operation = coordinateOperationFactory.createOperation(wgs84,
nad83);
CRS.transform(operation, envelope);
}
}
/**
* Main test method. Waits 30 seconds to perform envelope reprojections with multiple threads while one threads retrieves the coordinate operation
* factory. If this fails, there's most probably a deadlock that prevents the operations from finishing (use debugger to see threads &
* owned/waiting locks).<br>
* This may not fail all the time, but using multiple iterations should maximize chances of producing the bug. Although this may vary from a
* computer to another...
*
* @throws Exception If an exception occurs while performing {@link #retrieve} or {@link #transform}.
*/
@Test(timeout = 30000L)
public void testMultithreadDeadlock() throws Exception {
List<Future<Void>> futures = new ArrayList<>();
// raise some hell with 32 total threads
for (int i = 0; i < 16; i++) {
Future<Void> f = EXECUTOR.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
retrieve();
return null;
}
});
futures.add(f);
f = EXECUTOR.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
transform();
return null;
}
});
futures.add(f);
}
// wait for all
for (Future<Void> f : futures) {
f.get();
}
}
}