use std::{ops::Deref, sync::Arc};
use crate::errors::DatasetNotThreadSafeError;
use crate::{Dataset, GdalOpenFlags};
impl Dataset {
pub fn is_thread_safe(&self, scope_flags: GdalOpenFlags) -> bool {
unsafe {
use std::ptr;
gdal_sys::GDALDatasetIsThreadSafe(
self.c_dataset(),
scope_flags.bits() as i32,
ptr::null_mut(),
)
}
}
pub fn try_into_thread_safe(
self,
scope_flags: GdalOpenFlags,
) -> std::result::Result<ThreadSafeDataset, DatasetNotThreadSafeError> {
if self.is_thread_safe(scope_flags) {
let dataset = unsafe { ThreadSafeDataset::new(self) };
Ok(dataset)
} else {
let err = DatasetNotThreadSafeError(self);
Err(err)
}
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct ThreadSafeDataset {
inner: Arc<Dataset>,
}
unsafe impl Sync for ThreadSafeDataset {}
unsafe impl Send for ThreadSafeDataset {}
impl ThreadSafeDataset {
unsafe fn new(dataset: Dataset) -> Self {
Self {
#[allow(clippy::arc_with_non_send_sync)]
inner: Arc::new(dataset),
}
}
}
impl AsRef<Dataset> for ThreadSafeDataset {
fn as_ref(&self) -> &Dataset {
&self.inner
}
}
impl Deref for ThreadSafeDataset {
type Target = Dataset;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[cfg(test)]
mod tests {
use std::thread;
use crate::errors::{GdalError, Result};
use crate::test_utils::fixture;
use crate::{Dataset, DatasetOptions, GdalOpenFlags};
#[test]
fn test_thread_safe_dataset() -> Result<()> {
let ds = Dataset::open_ex(
fixture("tinymarble.tif"),
DatasetOptions {
open_flags: GdalOpenFlags::GDAL_OF_RASTER,
allowed_drivers: None,
open_options: None,
sibling_files: None,
},
)?;
assert!(!ds.is_thread_safe(GdalOpenFlags::empty()));
assert!(!ds.is_thread_safe(GdalOpenFlags::GDAL_OF_RASTER));
ds.try_into_thread_safe(GdalOpenFlags::GDAL_OF_RASTER)
.unwrap_err()
.into_inner();
let ds = Dataset::open_ex(
fixture("tinymarble.tif"),
DatasetOptions {
open_flags: GdalOpenFlags::GDAL_OF_RASTER | GdalOpenFlags::GDAL_OF_THREAD_SAFE,
..Default::default()
},
)?;
assert!(!ds.is_thread_safe(GdalOpenFlags::empty()));
assert!(ds.is_thread_safe(GdalOpenFlags::GDAL_OF_RASTER));
let ds = ds.try_into_thread_safe(GdalOpenFlags::GDAL_OF_RASTER)?;
thread::scope(|s| {
let threads = (0..10)
.map(|_| {
let ds = ds.clone();
s.spawn(move || {
let band = ds.rasterband(1)?;
let checksum = band.checksum((0, 0), band.size())?;
assert_eq!(checksum, 44419);
let band = ds.rasterband(2)?;
let checksum = band.checksum((0, 0), band.size())?;
assert_eq!(checksum, 55727);
let band = ds.rasterband(3)?;
let checksum = band.checksum((0, 0), band.size())?;
assert_eq!(checksum, 61385);
Ok::<_, GdalError>(())
})
})
.collect::<Vec<_>>();
for thread in threads {
thread.join().unwrap()?;
}
Ok(())
})
}
}