[gdal-dev] Call for review on RFC 101: Raster dataset read-only thread-safety

Even Rouault even.rouault at spatialys.com
Fri Sep 6 15:34:59 PDT 2024


Frank,

> I'm actually surprised that it is considered impractical to make 
> some/most drivers thread safe (read-only) natively and I thought key 
> ones already were mostly there in the past though I'm not so confident 
> in my memory.
Things have got more sophisticated over time with more deferred loading 
strategies, but I don't think any driver has been close to be 
thread-safe even in read-only scenarios. Except maybe the RasterIO part 
of the RawDataset, which has pretty much no mutable state (if you 
exclude file operations, and anything involving the block cache)
>  Looking through the GTiff code I find myself not able to work out 
> where locking is done and how we are managing the directory 
> loading/switching as block reads are done.
No locking is done. To be noted that the logic of TIFF directory 
loading/switching changed "a few" years ago. In the previous situation, 
the main dataset and its overview datasets were sharing the same TIFF* 
handle. But that strategy of switching from TIFF directory when 
switching between overview datasets had also a major performance 
drawback of invalidating TIFF bytecount[] and offset[] arrays, which was 
particularly annoying when computing overviews, where that back&forth is 
done consistently. And that inconvenience increased with the size of 
datasets and those arrays. But even if there are now several TIFF* 
handles, they do share the same VSILFILE* underneath, and there's a 
layer between the driver and libtiff to ensure that the file pointer 
position is properly set when activating one TIFF* handle at the 
position it expected, that flushes are done propertly so that one TIFF* 
handle does see a consistent state on disk (for read/write scenarios), etc.
>
> I would certainly like to work towards read-only thread safety for 
> some drivers with:
>  - a shared block cache for all threads reading from a dataset
That easiest way of doing that would be to put a per-dataset mutex in 
classes GDALHashSetBandBlockCache and GDALArrayBandBlockCache, but that 
would hurt scalability even when several threads try to access different 
blocks at the same time (unless smart things are done to drop the mutex 
as soon as possible)
>  - no duplication of file handles per-thread

I just don't see how to do that. Even a mutex around file operations 
would not be sufficient since we'd want both Seek()+Read() to be atomic 
per thread. PRead() would solve that, but very few drivers or underlying 
libraries use PRead() (and there's no implementation of it for all file 
systems, like Windows file API which lacks it). Or one would need a 
VSIThreadSafeHandle that would maintain a per-thread position, but that 
would still involves locking around access to the underneath handle.

libtiff itself has a lot of state variables, in its core or in its 
codecs (e.g. the deflate codec has to maintain a zlib handle as a state 
variable if doing partial reads of a tile/strip).  Even getting the 
location of a TIFF tile is not thread-safe when defered 
byteount[]/offset[] array loading is enabled (which is necessary when 
dealing with extremely large remote COGs) since it involves file accesses.

You could raise the point that the GTiff driver does have multi-threaded 
read optimizations, but it takes a lot of precautions to make that 
happening safely, and it has the chance of fully controlling the 
conditions where that happens. So basically it starts by fetching the 
location and size of the needed blocks, then each thread will read the 
needed blocks (either using PRead() if available, or taking a mutex 
around file accesses), create per-thread TIFF* handle, replicating the 
strategic TIFF tags from the main TIFF handle so that the codecs are 
initialized with the appropriate state and using a new 
TIFFReadFromUserBuffer() call that was added in libtiff to decode an 
in-memory encoded buffer (without doing file accesses). And even with 
that, I remember having fixed last year or so a case that I totally 
missed initially, where the block cache accidentally triggered in one of 
those worker thread. So yes, multi-threading is hard to get right.

>  - a way of checking that a dataset is considered to be thread safe.
Not totally sure to understand what you mean here. The RFC introduces a 
IsThreadSafe() virtual method that a driver can potentially implement 
(only set to true currently by instances of GDALThreadSafeDataset). But 
finding if a driver is thread-safe seems to be a non-solvable/provable 
problem to me, apart from staring at the code for a sufficient long time 
to convince oneself it is actually + adding some torture tests and hope 
they catch most issues. But things can get very complicated if you need 
to test all combinations of virtual methods that can be called...
>
> I'm curious if you can talk a bit more about the challenges of 
> accomplishing the above.   I had been hoping to take advantage of my 
> (assumption) that this already mostly existed in GDAL for our new high 
> performance environment at Planet so it is of more than academic 
> interest to me.

Probably that implementing a restricted read-only thread-safe TIFF 
driver, only addressing the common cases of TIFF without all the edge 
cases GDAL and libtiff try to address, for a controlled set of codecs, 
assuming pread() is always available, assuming you always read full 
tiles & strips, re-implementing a very simplified libtiff, and doing it 
"at hand" would be workable. Perhaps also excluding raster queries not 
at a resolution that exactly matches one of the overview level to 
completely by-pass the block cache. But that would be a separate driver, 
with a more restricted set of functionality than our current GDAL 
GeoTIFF driver that handles all the possible cases.

>   Also, doesn't gdalwarp's multithreading support already depend on 
> read-only thread safety for source files?

Definitely not. The initial -multi mode works with 2 threads: one for 
I/O, one for compute. The later was further extended to also 
multi-thread the compute part. But I/O is still done by a single thread, 
hence no requirement on the thread-safety of drivers.

Even

-- 
http://www.spatialys.com
My software is free, but my time generally not.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osgeo.org/pipermail/gdal-dev/attachments/20240907/7e9e17af/attachment-0001.htm>


More information about the gdal-dev mailing list