Hi,
I've encountered what appears to be a concurrency issue when calling 
OGRCoordinateTransformation::Transform from multiple threads.  What I've found 
is that when Transform is called from multiple asynchronous tasks, I'm getting 
inconsistent results given the same inputs.  This is pronounced with the 
projection used in the GINA Elevation dataset (Alaska); sample data can be 
found here:
http://ifsar.gina.alaska.edu/data/2013/DTM/IFSAR.SDMI.2013.FUGRO.DTM_N59W144/IFSAR.SDMI.2013.FUGRO.DTM_N59W144.tar.gz

The projection used by this datasource is:
PROJCS["NAD_1983_CORS96_Alaska_Albers",GEOGCS["GCS_NAD_1983_CORS96",DATUM["NAD_1983_CORS96",SPHEROID["GRS_1980",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],PROJECTION["Albers_Conic_Equal_Area"],PARAMETER["standard_parallel_1",55],PARAMETER["standard_parallel_2",65],PARAMETER["latitude_of_center",50],PARAMETER["longitude_of_center",-154],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]]]

Or in proj4:
+proj=aea +lat_1=55 +lat_2=65 +lat_0=50 +lon+0-154 +x_0=0 +y_0=0 +ellps=GRS80 + 
units=m +no_defs

The following code snippet reproduced the problem for me.  If I were comment 
out the async tasks and simply perform the transform synchronously, the outputs 
all remain consistent with the expected result.  Also make note of the 
CPLConfigOption "USE_PROJ_480_FEATURES".  Setting this to "NO" will also 
produce consistent results, as indicated in the inline comments below.

bool TestGdalConcurrency(const std::string& filename)
{
    auto dataset = GDALOpen(filename.c_str(), GDALAccess::GA_ReadOnly);
    auto projectionName = GDALGetProjectionRef(dataset);

    // By default, this is set to yes
    // Configuring this setting to "NO" will force GDAL to acquire a lock when 
calling into proj
    // This is a workaround, as we'd prefer lockfree concurrent access to proj.
    CPLSetConfigOption("USE_PROJ_480_FEATURES", "YES");

    // Setup "From" SpatialReference
    OGRSpatialReference wgs84SpatialReference;
    wgs84SpatialReference.SetWellKnownGeogCS("WGS84");
    // Setup "To" SpatialReference
    OGRSpatialReference spatialReference(projectionName);
    // Create transformation from WGS84 to the sourcefile's projection
    auto pFromPlanet = 
OGRCreateCoordinateTransformation(&wgs84SpatialReference, &spatialReference);

    // Will also provide this consistent latitude, longitude as input
    // The expectation is that given constant inputs, we should always receive 
the exact same output.
    double longitude = -143.8996;
    double latitude = 60.0074;
    double x = longitude;
    double y = latitude;

    // Our truth data (we're confident in this answer since there's no 
concurrent access into proj yet.
    pFromPlanet->Transform(1, &x, &y, nullptr);

    // Asynchronously call Transform from as many threads as I can get, every 
result should match the truth data
    std::vector<std::future<bool>> results;
    for (int i = 0; i < 100000; i++)
    {
        results.emplace_back(std::async(std::launch::async, [longitude, 
latitude, pFromPlanet, x, y]()
        {
            double _x = longitude;
            double _y = latitude;

            pFromPlanet->Transform(1, &_x, &_y, nullptr);
            return _x == x && _y == y;
        }));
    }

    // Sum the number of failed comparisons
    auto numFailures = 0;
    for (auto& result : results)
    {
        if (!result.get())
            ++numFailures;
    }

    OGRCoordinateTransformation::DestroyCT(pFromPlanet);
    GDALClose(dataset);

    return numFailures == 0;
}

If there's something I'm missing that would allow for lock-free coordinate 
transformations I'd be happy to hear about it.

Thanks,
Alex
_______________________________________________
gdal-dev mailing list
[email protected]
http://lists.osgeo.org/mailman/listinfo/gdal-dev

Reply via email to