This is an automated email from the ASF dual-hosted git repository.

lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git


The following commit(s) were added to refs/heads/main by this push:
     new a43f0fecd fix(rust/driver_manager): allow cancel to be truly 
concurrent (#4858)
a43f0fecd is described below

commit a43f0fecda799a7d6aa3efbf454aa5a97630d865
Author: David Li <[email protected]>
AuthorDate: Thu Oct 8 15:39:13 2026 -0700

    fix(rust/driver_manager): allow cancel to be truly concurrent (#4858)
    
    N.B. the driver exporter is not covered by this PR.
    
    Closes #4817.
    
    Assisted-by: GPT-6 Astra
---
 rust/driver_manager/src/lib.rs            | 296 ++++++++++--------
 rust/driver_manager/tests/cancellation.rs | 495 ++++++++++++++++++++++++++++++
 rust/ffi/src/options.rs                   |  38 +++
 3 files changed, 704 insertions(+), 125 deletions(-)

diff --git a/rust/driver_manager/src/lib.rs b/rust/driver_manager/src/lib.rs
index 811886828..f42bcdc58 100644
--- a/rust/driver_manager/src/lib.rs
+++ b/rust/driver_manager/src/lib.rs
@@ -93,17 +93,21 @@
 // According to the ADBC specification, objects allow serialized access from
 // multiple threads: one thread may make a call, and once finished, another
 // thread may make a call. They do not allow concurrent access from multiple
-// threads.
+// threads, except that cancellation must be able to overlap ordinary calls.
 //
-// In order to implement these semantics, all mutable FFI objects are wrapped
-// in `Mutex`es. `FFI_Driver` is not wrapped in a `Mutex` because it is
-// an immutable struct of function pointers. Wrapping the driver in a `Mutex`
-// would prevent any parallelism between driver calls, which is not desirable.
+// Databases use a `Mutex`. Connections and statements use `NativeHandle` to
+// serialize ordinary calls while allowing cancellation through raw pointers.
+// Their shared inner objects release the native handles only after all calls
+// (including cancellation) have relinquished their strong references.
+// `FFI_Driver` is not wrapped in a `Mutex` because it is an immutable struct
+// of function pointers. Wrapping the driver in a `Mutex` would prevent any
+// parallelism between driver calls, which is not desirable.
 
 pub mod error;
 pub mod profile;
 pub mod search;
 
+use std::cell::UnsafeCell;
 use std::collections::HashSet;
 use std::ffi::{CString, OsStr};
 use std::ops::DerefMut;
@@ -111,11 +115,11 @@ use std::os::raw::c_char;
 use std::path::PathBuf;
 use std::pin::Pin;
 use std::ptr::{null, null_mut};
-use std::sync::{Arc, Mutex};
+use std::sync::{Arc, LockResult, Mutex, MutexGuard};
 
 use adbc_ffi::options::{
-    check_status, get_option_bytes, get_option_string, set_option_connection, 
set_option_database,
-    set_option_statement,
+    check_status, get_option_bytes, get_option_string, set_option_connection,
+    set_option_connection_raw, set_option_database, set_option_statement_raw,
 };
 use arrow_array::ffi::{FFI_ArrowSchema, to_ffi};
 use arrow_array::ffi_stream::{ArrowArrayStreamReader, FFI_ArrowArrayStream};
@@ -758,7 +762,7 @@ impl Database for ManagedDatabase {
         // Initialize the connection.
         let connection = self.connection_init(connection)?;
         let inner = ManagedConnectionInner {
-            connection: Mutex::new(connection),
+            connection: NativeHandle::new(connection),
             database: self.inner.clone(),
         };
         Ok(Self::ConnectionType {
@@ -782,7 +786,7 @@ impl Database for ManagedDatabase {
         // Initialize the connection.
         let connection = self.connection_init(connection)?;
         let inner = ManagedConnectionInner {
-            connection: Mutex::new(connection),
+            connection: NativeHandle::new(connection),
             database: self.inner.clone(),
         };
         Ok(Self::ConnectionType {
@@ -791,19 +795,52 @@ impl Database for ManagedDatabase {
     }
 }
 
+// Wrap a native handle with a mutex and an UnsafeCell. We need shared access
+// to the native handle for cancellation, so just a mutex isn't sufficient.
+struct NativeHandle<T> {
+    value: UnsafeCell<T>,
+    mu: Mutex<()>,
+}
+
+// SAFETY: Used only for connection and statement FFI handles. Ordinary access 
is
+// serialized by operation, and the only overlapping calls are cancellation,
+// which the driver must make thread-safe under the ADBC contract. Raw pointers
+// avoid creating aliased Rust references. The enclosing Arc keeps the handle,
+// its parents, and the driver library alive until all calls finish. Release 
runs
+// only in the enclosing inner object's Drop, with no concurrent access.
+unsafe impl<T: Send> Sync for NativeHandle<T> {}
+
+impl<T> NativeHandle<T> {
+    fn new(value: T) -> Self {
+        Self {
+            value: UnsafeCell::new(value),
+            mu: Mutex::new(()),
+        }
+    }
+
+    fn lock(&self) -> LockResult<MutexGuard<'_, ()>> {
+        self.mu.lock()
+    }
+
+    fn get(&self) -> *mut T {
+        self.value.get()
+    }
+}
+
 struct ManagedConnectionInner {
-    connection: Mutex<adbc_ffi::FFI_AdbcConnection>,
+    connection: NativeHandle<adbc_ffi::FFI_AdbcConnection>,
     database: Arc<ManagedDatabaseInner>,
 }
 
 impl Drop for ManagedConnectionInner {
     fn drop(&mut self) {
         let driver = &self.database.driver.driver;
-        if let Ok(mut connection) = self.connection.lock() {
+        if !self.connection.mu.is_poisoned() {
+            let connection = self.connection.get();
             let method = driver_method!(driver, ConnectionRelease);
             // TODO(alexandreyc): how should we handle `ConnectionRelease` 
failing?
             // See: 
https://github.com/apache/arrow-adbc/pull/1742#discussion_r1574388409
-            unsafe { method(connection.deref_mut(), null_mut()) };
+            unsafe { method(connection, null_mut()) };
         }
         // We could still drop here but if the lock is poisoned, we have no
         // clue what the status is. Since a panic comes from Rust code,
@@ -832,15 +869,17 @@ impl adbc_core::CancelHandle for ConnectionCancelHandle {
                 ));
             }
             let driver = &inner.database.driver.driver;
-            let mut connection = inner.connection.lock().map_err(|e| {
-                Error::with_message_and_status(
-                    format!("[Driver Manager] connection is poisoned: {e:?}"),
-                    Status::Internal,
-                )
-            })?;
+            let Some(method) = driver.ConnectionCancel else {
+                return Err(Error::with_message_and_status(
+                    "[Driver Manager] ConnectionCancel not supported by 
driver",
+                    Status::NotImplemented,
+                ));
+            };
+            let connection = inner.connection.get();
             let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
-            let method = driver_method!(driver, ConnectionCancel);
-            let status = unsafe { method(connection.deref_mut(), &mut error) };
+            // SAFETY: The upgraded Arc prevents release; ADBC requires 
cancellation
+            // to be thread-safe, including when an ordinary operation is 
running.
+            let status = unsafe { method(connection, &mut error) };
             check_status(status, error)
         } else {
             Ok(())
@@ -863,18 +902,19 @@ impl Optionable for ManagedConnection {
 
     fn get_option_bytes(&self, key: Self::Option) -> Result<Vec<u8>> {
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let method = driver_method!(driver, ConnectionGetOptionBytes);
         let populate = |key: *const c_char,
                         value: *mut u8,
                         length: *mut usize,
                         error: *mut adbc_ffi::FFI_AdbcError| unsafe {
-            method(connection.deref_mut(), key, value, length, error)
+            method(connection, key, value, length, error)
         };
         get_option_bytes(key, populate, driver)
     }
@@ -883,16 +923,16 @@ impl Optionable for ManagedConnection {
         let key = CString::new(key.as_ref())?;
         let mut value: f64 = f64::default();
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, ConnectionGetOptionDouble);
-        let status =
-            unsafe { method(connection.deref_mut(), key.as_ptr(), &mut value, 
&mut error) };
+        let status = unsafe { method(connection, key.as_ptr(), &mut value, 
&mut error) };
         check_status(status, error)?;
         Ok(value)
     }
@@ -901,53 +941,51 @@ impl Optionable for ManagedConnection {
         let key = CString::new(key.as_ref())?;
         let mut value: i64 = 0;
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, ConnectionGetOptionInt);
-        let status =
-            unsafe { method(connection.deref_mut(), key.as_ptr(), &mut value, 
&mut error) };
+        let status = unsafe { method(connection, key.as_ptr(), &mut value, 
&mut error) };
         check_status(status, error)?;
         Ok(value)
     }
 
     fn get_option_string(&self, key: Self::Option) -> Result<String> {
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let method = driver_method!(driver, ConnectionGetOption);
         let populate = |key: *const c_char,
                         value: *mut c_char,
                         length: *mut usize,
                         error: *mut adbc_ffi::FFI_AdbcError| unsafe {
-            method(connection.deref_mut(), key, value, length, error)
+            method(connection, key, value, length, error)
         };
         get_option_string(key, populate, driver)
     }
 
     fn set_option(&mut self, key: Self::Option, value: OptionValue) -> 
Result<()> {
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
-        set_option_connection(
-            driver,
-            connection.deref_mut(),
-            self.driver_version(),
-            key,
-            value,
-        )
+        let connection = self.inner.connection.get();
+        // SAFETY: The operation guard serializes ordinary calls; the owning 
Arc
+        // keeps the handle alive. Only driver-supported cancellation can 
overlap.
+        unsafe { set_option_connection_raw(driver, connection, 
self.driver_version(), key, value) }
     }
 }
 
@@ -956,20 +994,21 @@ impl Connection for ManagedConnection {
 
     fn new_statement(&mut self) -> Result<Self::StatementType> {
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut statement = adbc_ffi::FFI_AdbcStatement::default();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementNew);
-        let status = unsafe { method(connection.deref_mut(), &mut statement, 
&mut error) };
+        let status = unsafe { method(connection, &mut statement, &mut error) };
         check_status(status, error)?;
 
         let inner = Arc::new(ManagedStatementInner {
-            statement: Mutex::new(statement),
+            statement: NativeHandle::new(statement),
             connection: self.inner.clone(),
         });
 
@@ -984,29 +1023,31 @@ impl Connection for ManagedConnection {
 
     fn commit(&mut self) -> Result<()> {
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, ConnectionCommit);
-        let status = unsafe { method(connection.deref_mut(), &mut error) };
+        let status = unsafe { method(connection, &mut error) };
         check_status(status, error)
     }
 
     fn rollback(&mut self) -> Result<()> {
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, ConnectionRollback);
-        let status = unsafe { method(connection.deref_mut(), &mut error) };
+        let status = unsafe { method(connection, &mut error) };
         check_status(status, error)
     }
 
@@ -1022,23 +1063,16 @@ impl Connection for ManagedConnection {
             .map(|c| (c.as_ptr(), c.len()))
             .unwrap_or((null(), 0));
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, ConnectionGetInfo);
-        let status = unsafe {
-            method(
-                connection.deref_mut(),
-                codes_ptr,
-                codes_len,
-                &mut stream,
-                &mut error,
-            )
-        };
+        let status = unsafe { method(connection, codes_ptr, codes_len, &mut 
stream, &mut error) };
         check_status(status, error)?;
         let reader = ArrowArrayStreamReader::try_new(stream)?;
         Ok(Box::new(reader))
@@ -1083,19 +1117,20 @@ impl Connection for ManagedConnection {
         };
 
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, ConnectionGetObjects);
         let mut stream = FFI_ArrowArrayStream::empty();
 
         let status = unsafe {
             method(
-                connection.deref_mut(),
+                connection,
                 depth.into(),
                 catalog_ptr,
                 db_schema_ptr,
@@ -1136,17 +1171,18 @@ impl Connection for ManagedConnection {
 
         let mut stream = FFI_ArrowArrayStream::empty();
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, ConnectionGetStatistics);
         let status = unsafe {
             method(
-                connection.deref_mut(),
+                connection,
                 catalog_ptr,
                 db_schema_ptr,
                 table_name_ptr,
@@ -1169,15 +1205,16 @@ impl Connection for ManagedConnection {
         }
         let mut stream = FFI_ArrowArrayStream::empty();
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, ConnectionGetStatisticNames);
-        let status = unsafe { method(connection.deref_mut(), &mut stream, &mut 
error) };
+        let status = unsafe { method(connection, &mut stream, &mut error) };
         check_status(status, error)?;
         let reader = ArrowArrayStreamReader::try_new(stream)?;
         Ok(Box::new(reader))
@@ -1199,17 +1236,18 @@ impl Connection for ManagedConnection {
 
         let mut schema = FFI_ArrowSchema::empty();
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, ConnectionGetTableSchema);
         let status = unsafe {
             method(
-                connection.deref_mut(),
+                connection,
                 catalog_ptr,
                 db_schema_ptr,
                 table_name_ptr,
@@ -1224,15 +1262,16 @@ impl Connection for ManagedConnection {
     fn get_table_types(&self) -> Result<Box<dyn RecordBatchReader + Send + 
'static>> {
         let mut stream = FFI_ArrowArrayStream::empty();
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, ConnectionGetTableTypes);
-        let status = unsafe { method(connection.deref_mut(), &mut stream, &mut 
error) };
+        let status = unsafe { method(connection, &mut stream, &mut error) };
         check_status(status, error)?;
         let reader = ArrowArrayStreamReader::try_new(stream)?;
         Ok(Box::new(reader))
@@ -1244,18 +1283,19 @@ impl Connection for ManagedConnection {
     ) -> Result<Box<dyn RecordBatchReader + Send + 'static>> {
         let mut stream = FFI_ArrowArrayStream::empty();
         let driver = self.ffi_driver();
-        let mut connection = self.inner.connection.lock().map_err(|e| {
+        let _guard = self.inner.connection.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] connection is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let connection = self.inner.connection.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, ConnectionReadPartition);
         let partition = partition.as_ref();
         let status = unsafe {
             method(
-                connection.deref_mut(),
+                connection,
                 partition.as_ptr(),
                 partition.len(),
                 &mut stream,
@@ -1269,7 +1309,7 @@ impl Connection for ManagedConnection {
 }
 
 struct ManagedStatementInner {
-    statement: Mutex<adbc_ffi::FFI_AdbcStatement>,
+    statement: NativeHandle<adbc_ffi::FFI_AdbcStatement>,
     connection: Arc<ManagedConnectionInner>,
 }
 /// Implementation of [Statement].
@@ -1302,15 +1342,17 @@ impl adbc_core::CancelHandle for StatementCancelHandle {
                 ));
             }
             let driver = &inner.connection.database.driver.driver;
-            let mut statement = inner.statement.lock().map_err(|e| {
-                Error::with_message_and_status(
-                    format!("[Driver Manager] statement is poisoned: {e:?}"),
-                    Status::Internal,
-                )
-            })?;
+            let Some(method) = driver.StatementCancel else {
+                return Err(Error::with_message_and_status(
+                    "[Driver Manager] StatementCancel not supported by driver",
+                    Status::NotImplemented,
+                ));
+            };
+            let statement = inner.statement.get();
             let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
-            let method = driver_method!(driver, StatementCancel);
-            let status = unsafe { method(statement.deref_mut(), &mut error) };
+            // SAFETY: The upgraded Arc prevents release; ADBC requires 
cancellation
+            // to be thread-safe, including when an ordinary operation is 
running.
+            let status = unsafe { method(statement, &mut error) };
             check_status(status, error)
         } else {
             Ok(())
@@ -1321,33 +1363,35 @@ impl adbc_core::CancelHandle for StatementCancelHandle {
 impl Statement for ManagedStatement {
     fn bind(&mut self, batch: RecordBatch) -> Result<()> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementBind);
         let batch: StructArray = batch.into();
         let (mut array, mut schema) = to_ffi(&batch.to_data())?;
-        let status = unsafe { method(statement.deref_mut(), &mut array, &mut 
schema, &mut error) };
+        let status = unsafe { method(statement, &mut array, &mut schema, &mut 
error) };
         check_status(status, error)?;
         Ok(())
     }
 
     fn bind_stream(&mut self, reader: Box<dyn RecordBatchReader + Send>) -> 
Result<()> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementBindStream);
         let mut stream = FFI_ArrowArrayStream::new(reader);
-        let status = unsafe { method(statement.deref_mut(), &mut stream, &mut 
error) };
+        let status = unsafe { method(statement, &mut stream, &mut error) };
         check_status(status, error)?;
         Ok(())
     }
@@ -1360,16 +1404,17 @@ impl Statement for ManagedStatement {
 
     fn execute(&mut self) -> Result<Box<dyn RecordBatchReader + Send + 
'static>> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementExecuteQuery);
         let mut stream = FFI_ArrowArrayStream::empty();
-        let status = unsafe { method(statement.deref_mut(), &mut stream, 
null_mut(), &mut error) };
+        let status = unsafe { method(statement, &mut stream, null_mut(), &mut 
error) };
         check_status(status, error)?;
         let reader = ArrowArrayStreamReader::try_new(stream)?;
         Ok(Box::new(reader))
@@ -1377,51 +1422,47 @@ impl Statement for ManagedStatement {
 
     fn execute_schema(&mut self) -> Result<arrow_schema::Schema> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementExecuteSchema);
         let mut schema = FFI_ArrowSchema::empty();
-        let status = unsafe { method(statement.deref_mut(), &mut schema, &mut 
error) };
+        let status = unsafe { method(statement, &mut schema, &mut error) };
         check_status(status, error)?;
         Ok((&schema).try_into()?)
     }
 
     fn execute_update(&mut self) -> Result<Option<i64>> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementExecuteQuery);
         let mut rows_affected: i64 = -1;
-        let status = unsafe {
-            method(
-                statement.deref_mut(),
-                null_mut(),
-                &mut rows_affected,
-                &mut error,
-            )
-        };
+        let status = unsafe { method(statement, null_mut(), &mut 
rows_affected, &mut error) };
         check_status(status, error)?;
         Ok((rows_affected != -1).then_some(rows_affected))
     }
 
     fn execute_partitions(&mut self) -> Result<PartitionedResult> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementExecutePartitions);
         let mut schema = FFI_ArrowSchema::empty();
@@ -1429,7 +1470,7 @@ impl Statement for ManagedStatement {
         let mut rows_affected: i64 = -1;
         let status = unsafe {
             method(
-                statement.deref_mut(),
+                statement,
                 &mut schema,
                 &mut partitions,
                 &mut rows_affected,
@@ -1449,31 +1490,33 @@ impl Statement for ManagedStatement {
 
     fn get_parameter_schema(&self) -> Result<arrow_schema::Schema> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementGetParameterSchema);
         let mut schema = FFI_ArrowSchema::empty();
-        let status = unsafe { method(statement.deref_mut(), &mut schema, &mut 
error) };
+        let status = unsafe { method(statement, &mut schema, &mut error) };
         check_status(status, error)?;
         Ok((&schema).try_into()?)
     }
 
     fn prepare(&mut self) -> Result<()> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementPrepare);
-        let status = unsafe { method(statement.deref_mut(), &mut error) };
+        let status = unsafe { method(statement, &mut error) };
         check_status(status, error)?;
         Ok(())
     }
@@ -1481,32 +1524,33 @@ impl Statement for ManagedStatement {
     fn set_sql_query(&mut self, query: impl AsRef<str>) -> Result<()> {
         let query = CString::new(query.as_ref())?;
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementSetSqlQuery);
-        let status = unsafe { method(statement.deref_mut(), query.as_ptr(), 
&mut error) };
+        let status = unsafe { method(statement, query.as_ptr(), &mut error) };
         check_status(status, error)?;
         Ok(())
     }
 
     fn set_substrait_plan(&mut self, plan: impl AsRef<[u8]>) -> Result<()> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementSetSubstraitPlan);
         let plan = plan.as_ref();
-        let status =
-            unsafe { method(statement.deref_mut(), plan.as_ptr(), plan.len(), 
&mut error) };
+        let status = unsafe { method(statement, plan.as_ptr(), plan.len(), 
&mut error) };
         check_status(status, error)?;
         Ok(())
     }
@@ -1517,18 +1561,19 @@ impl Optionable for ManagedStatement {
 
     fn get_option_bytes(&self, key: Self::Option) -> Result<Vec<u8>> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let method = driver_method!(driver, StatementGetOptionBytes);
         let populate = |key: *const c_char,
                         value: *mut u8,
                         length: *mut usize,
                         error: *mut adbc_ffi::FFI_AdbcError| unsafe {
-            method(statement.deref_mut(), key, value, length, error)
+            method(statement, key, value, length, error)
         };
         get_option_bytes(key, populate, driver)
     }
@@ -1537,15 +1582,16 @@ impl Optionable for ManagedStatement {
         let key = CString::new(key.as_ref())?;
         let mut value: f64 = f64::default();
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementGetOptionDouble);
-        let status = unsafe { method(statement.deref_mut(), key.as_ptr(), &mut 
value, &mut error) };
+        let status = unsafe { method(statement, key.as_ptr(), &mut value, &mut 
error) };
         check_status(status, error)?;
         Ok(value)
     }
@@ -1554,63 +1600,63 @@ impl Optionable for ManagedStatement {
         let key = CString::new(key.as_ref())?;
         let mut value: i64 = 0;
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let mut error = adbc_ffi::FFI_AdbcError::with_driver(driver);
         let method = driver_method!(driver, StatementGetOptionInt);
-        let status = unsafe { method(statement.deref_mut(), key.as_ptr(), &mut 
value, &mut error) };
+        let status = unsafe { method(statement, key.as_ptr(), &mut value, &mut 
error) };
         check_status(status, error)?;
         Ok(value)
     }
 
     fn get_option_string(&self, key: Self::Option) -> Result<String> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
+        let statement = self.inner.statement.get();
         let method = driver_method!(driver, StatementGetOption);
         let populate = |key: *const c_char,
                         value: *mut c_char,
                         length: *mut usize,
                         error: *mut adbc_ffi::FFI_AdbcError| unsafe {
-            method(statement.deref_mut(), key, value, length, error)
+            method(statement, key, value, length, error)
         };
         get_option_string(key, populate, driver)
     }
 
     fn set_option(&mut self, key: Self::Option, value: OptionValue) -> 
Result<()> {
         let driver = self.ffi_driver();
-        let mut statement = self.inner.statement.lock().map_err(|e| {
+        let _guard = self.inner.statement.lock().map_err(|e| {
             Error::with_message_and_status(
                 format!("[Driver Manager] statement is poisoned: {e:?}"),
                 Status::Internal,
             )
         })?;
-        set_option_statement(
-            driver,
-            statement.deref_mut(),
-            self.driver_version(),
-            key,
-            value,
-        )
+        let statement = self.inner.statement.get();
+        // SAFETY: The operation guard serializes ordinary calls; the owning 
Arc
+        // keeps the handle alive. Only driver-supported cancellation can 
overlap.
+        unsafe { set_option_statement_raw(driver, statement, 
self.driver_version(), key, value) }
     }
 }
 
-impl Drop for ManagedStatement {
+impl Drop for ManagedStatementInner {
     fn drop(&mut self) {
-        let driver = self.ffi_driver();
-        if let Ok(mut statement) = self.inner.statement.lock() {
+        let driver = &self.connection.database.driver.driver;
+        if !self.statement.mu.is_poisoned() {
+            let statement = self.statement.get();
             let method = driver_method!(driver, StatementRelease);
             // TODO(alexandreyc): how should we handle `StatementRelease` 
failing?
             // See: 
https://github.com/apache/arrow-adbc/pull/1742#discussion_r1574388409
-            unsafe { method(statement.deref_mut(), null_mut()) };
+            unsafe { method(statement, null_mut()) };
         }
         // We could still drop here but if the lock is poisoned, we have no
         // clue what the status is. Since a panic comes from Rust code,
diff --git a/rust/driver_manager/tests/cancellation.rs 
b/rust/driver_manager/tests/cancellation.rs
new file mode 100644
index 000000000..f9d260bf4
--- /dev/null
+++ b/rust/driver_manager/tests/cancellation.rs
@@ -0,0 +1,495 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Tests of cancellation through the driver manager, focusing on making sure
+// cancellation is truly concurrent.
+
+use std::cell::RefCell;
+use std::ffi::{c_int, c_void};
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering::SeqCst};
+use std::thread;
+use std::time::{Duration, Instant};
+
+use adbc_core::error::{AdbcStatusCode, Result, Status};
+use adbc_core::options::AdbcVersion;
+use adbc_core::{CancelHandle, Connection, Database, Driver, Statement};
+use adbc_driver_manager::{ManagedConnection, ManagedDriver, ManagedStatement};
+use adbc_ffi::{
+    FFI_AdbcConnection, FFI_AdbcDatabase, FFI_AdbcDriver, 
FFI_AdbcDriverInitFunc, FFI_AdbcError,
+    FFI_AdbcStatement,
+};
+use arrow_array::ffi_stream::FFI_ArrowArrayStream;
+
+const TIMEOUT: Duration = Duration::from_secs(2);
+const CALLBACK_TIMEOUT: Duration = Duration::from_secs(10);
+
+// ------------------------------------------------------------
+// Simulated driver implementation
+// ------------------------------------------------------------
+
+struct AtomicFlag {
+    mu: std::sync::Mutex<bool>,
+    cv: std::sync::Condvar,
+}
+
+impl AtomicFlag {
+    fn new(value: bool) -> Self {
+        Self {
+            mu: std::sync::Mutex::new(value),
+            cv: std::sync::Condvar::new(),
+        }
+    }
+
+    fn clear(&self) {
+        let mut guard = self.mu.lock().unwrap();
+        *guard = false;
+        self.cv.notify_all();
+    }
+
+    fn set(&self) {
+        let mut guard = self.mu.lock().unwrap();
+        *guard = true;
+        self.cv.notify_all();
+    }
+
+    fn wait(&self, timeout: Duration) -> bool {
+        let (guard, _) = self
+            .cv
+            .wait_timeout_while(self.mu.lock().unwrap(), timeout, |flag| 
!*flag)
+            .unwrap();
+        *guard
+    }
+}
+
+// Track what's been done with the driver so we can verify invariants later.
+struct DriverObjectState {
+    // count of non-cancel operations
+    operations: AtomicUsize,
+    // count of cancel operations
+    cancellations: AtomicUsize,
+    // allow the operation to proceed
+    release_operation: AtomicFlag,
+    // allow the cancel to proceed
+    release_cancel: AtomicFlag,
+    // status to return from the cancel callback
+    cancel_status: AtomicU8,
+    // whether the operation or cancel timed out waiting for the other to 
release
+    timed_out: AtomicBool,
+    statement_releases: AtomicUsize,
+    connection_releases: AtomicUsize,
+    database_releases: AtomicUsize,
+    driver_releases: AtomicUsize,
+}
+
+impl DriverObjectState {
+    fn new() -> Self {
+        Self {
+            operations: AtomicUsize::new(0),
+            cancellations: AtomicUsize::new(0),
+            release_operation: AtomicFlag::new(false),
+            release_cancel: AtomicFlag::new(true),
+            cancel_status: AtomicU8::new(Status::Ok.into()),
+            timed_out: AtomicBool::new(false),
+            statement_releases: AtomicUsize::new(0),
+            connection_releases: AtomicUsize::new(0),
+            database_releases: AtomicUsize::new(0),
+            driver_releases: AtomicUsize::new(0),
+        }
+    }
+
+    // Do "something" with the driver, waiting on release_operation
+    fn operate(&self) -> AdbcStatusCode {
+        self.operations.fetch_add(1, SeqCst);
+        if !self.release_operation.wait(CALLBACK_TIMEOUT) {
+            self.timed_out.store(true, SeqCst);
+            return Status::Internal.into();
+        }
+        Status::Cancelled.into()
+    }
+
+    // Cancel operate() by flagging release_operation, waiting on 
release_cancel first
+    fn cancel(&self) -> AdbcStatusCode {
+        self.cancellations.fetch_add(1, SeqCst);
+        if !self.release_cancel.wait(CALLBACK_TIMEOUT) {
+            self.timed_out.store(true, SeqCst);
+            return Status::Internal.into();
+        }
+        self.release_operation.set();
+        self.cancel_status.load(SeqCst)
+    }
+
+    fn releases(&self) -> [usize; 4] {
+        [
+            self.statement_releases.load(SeqCst),
+            self.connection_releases.load(SeqCst),
+            self.database_releases.load(SeqCst),
+            self.driver_releases.load(SeqCst),
+        ]
+    }
+
+    // SAFETY: Each private_data is a Box<Arc<State>> initialized below. 
Callbacks
+    // clone the Arc before blocking and never borrow the mutable FFI handle 
itself.
+    unsafe fn from_raw(data: *mut c_void) -> Arc<Self> {
+        unsafe { Arc::clone(&*data.cast::<Arc<Self>>()) }
+    }
+}
+
+fn wait_for(condition: impl Fn() -> bool, timeout: Duration) -> bool {
+    let deadline = Instant::now() + timeout;
+    while !condition() && Instant::now() < deadline {
+        thread::sleep(Duration::from_millis(50));
+    }
+    condition()
+}
+
+thread_local! {
+    // We need a way to pass state to the driver impl, so use a thread local.
+    static INITIAL_STATE: RefCell<Option<Arc<DriverObjectState>>> = const { 
RefCell::new(None) };
+}
+
+// ------------------------------------------------------------
+// Simulated driver implementation (driver callbacks)
+// ------------------------------------------------------------
+
+macro_rules! release_callback {
+    ($name:ident, $ty:ty, $counter:ident) => {
+        unsafe extern "C" fn $name(handle: *mut $ty, _: *mut FFI_AdbcError) -> 
AdbcStatusCode {
+            unsafe {
+                if (*handle).private_data.is_null() {
+                    return Status::InvalidState.into();
+                }
+                let state = 
Box::from_raw((*handle).private_data.cast::<Arc<DriverObjectState>>());
+                state.$counter.fetch_add(1, SeqCst);
+                (*handle).private_data = std::ptr::null_mut();
+            }
+            Status::Ok.into()
+        }
+    };
+}
+
+release_callback!(driver_release, FFI_AdbcDriver, driver_releases);
+release_callback!(database_release, FFI_AdbcDatabase, database_releases);
+release_callback!(connection_release, FFI_AdbcConnection, connection_releases);
+release_callback!(statement_release, FFI_AdbcStatement, statement_releases);
+
+unsafe extern "C" fn database_new(
+    database: *mut FFI_AdbcDatabase,
+    _: *mut FFI_AdbcError,
+) -> AdbcStatusCode {
+    unsafe {
+        let state = 
DriverObjectState::from_raw((*(*database).private_driver).private_data);
+        (*database).private_data = Box::into_raw(Box::new(state)).cast();
+    }
+    Status::Ok.into()
+}
+
+unsafe extern "C" fn database_init(
+    _: *mut FFI_AdbcDatabase,
+    _: *mut FFI_AdbcError,
+) -> AdbcStatusCode {
+    Status::Ok.into()
+}
+
+unsafe extern "C" fn connection_new(
+    _: *mut FFI_AdbcConnection,
+    _: *mut FFI_AdbcError,
+) -> AdbcStatusCode {
+    Status::Ok.into()
+}
+
+unsafe extern "C" fn connection_init(
+    connection: *mut FFI_AdbcConnection,
+    database: *mut FFI_AdbcDatabase,
+    _: *mut FFI_AdbcError,
+) -> AdbcStatusCode {
+    unsafe {
+        (*connection).private_data = 
Box::into_raw(Box::new(DriverObjectState::from_raw(
+            (*database).private_data,
+        )))
+        .cast();
+    }
+    Status::Ok.into()
+}
+
+unsafe extern "C" fn statement_new(
+    connection: *mut FFI_AdbcConnection,
+    statement: *mut FFI_AdbcStatement,
+    _: *mut FFI_AdbcError,
+) -> AdbcStatusCode {
+    unsafe {
+        (*statement).private_data = 
Box::into_raw(Box::new(DriverObjectState::from_raw(
+            (*connection).private_data,
+        )))
+        .cast();
+    }
+    Status::Ok.into()
+}
+
+unsafe extern "C" fn commit(
+    connection: *mut FFI_AdbcConnection,
+    _: *mut FFI_AdbcError,
+) -> AdbcStatusCode {
+    unsafe { DriverObjectState::from_raw((*connection).private_data).operate() 
}
+}
+
+unsafe extern "C" fn execute(
+    statement: *mut FFI_AdbcStatement,
+    _: *mut FFI_ArrowArrayStream,
+    _: *mut i64,
+    _: *mut FFI_AdbcError,
+) -> AdbcStatusCode {
+    unsafe { DriverObjectState::from_raw((*statement).private_data).operate() }
+}
+
+unsafe extern "C" fn connection_cancel(
+    handle: *mut FFI_AdbcConnection,
+    _: *mut FFI_AdbcError,
+) -> AdbcStatusCode {
+    unsafe { DriverObjectState::from_raw((*handle).private_data).cancel() }
+}
+
+unsafe extern "C" fn statement_cancel(
+    handle: *mut FFI_AdbcStatement,
+    _: *mut FFI_AdbcError,
+) -> AdbcStatusCode {
+    unsafe { DriverObjectState::from_raw((*handle).private_data).cancel() }
+}
+
+unsafe extern "C" fn init<const CANCEL: bool>(
+    _: c_int,
+    driver: *mut c_void,
+    _: *mut FFI_AdbcError,
+) -> AdbcStatusCode {
+    let Some(state) = INITIAL_STATE.with(|state| state.borrow_mut().take()) 
else {
+        return Status::Internal.into();
+    };
+    unsafe {
+        *driver.cast::<FFI_AdbcDriver>() = FFI_AdbcDriver {
+            private_data: Box::into_raw(Box::new(state)).cast(),
+            release: Some(driver_release),
+            DatabaseNew: Some(database_new),
+            DatabaseInit: Some(database_init),
+            DatabaseRelease: Some(database_release),
+            ConnectionNew: Some(connection_new),
+            ConnectionInit: Some(connection_init),
+            ConnectionRelease: Some(connection_release),
+            ConnectionCommit: Some(commit),
+            ConnectionCancel: if CANCEL {
+                Some(connection_cancel)
+            } else {
+                None
+            },
+            StatementNew: Some(statement_new),
+            StatementRelease: Some(statement_release),
+            StatementExecuteQuery: Some(execute),
+            StatementCancel: if CANCEL { Some(statement_cancel) } else { None 
},
+            ..FFI_AdbcDriver::default()
+        };
+    }
+    Status::Ok.into()
+}
+
+// ------------------------------------------------------------
+// Test cases
+// ------------------------------------------------------------
+
+#[derive(Clone, Copy, Debug)]
+enum ObjectKind {
+    Connection,
+    Statement,
+}
+
+const OBJECT_KINDS: [ObjectKind; 2] = [ObjectKind::Connection, 
ObjectKind::Statement];
+
+#[derive(Clone)]
+enum Object {
+    Connection(ManagedConnection),
+    Statement(ManagedStatement),
+}
+
+impl Object {
+    fn cancel_handle(&self) -> Box<dyn CancelHandle> {
+        match self {
+            Self::Connection(connection) => connection.get_cancel_handle(),
+            Self::Statement(statement) => statement.get_cancel_handle(),
+        }
+    }
+
+    fn operate(&mut self) -> Result<()> {
+        match self {
+            Self::Connection(connection) => connection.commit(),
+            Self::Statement(statement) => statement.execute().map(|_| ()),
+        }
+    }
+}
+
+fn setup<const CANCEL: bool>(
+    kind: ObjectKind,
+    version: AdbcVersion,
+) -> Result<(Arc<DriverObjectState>, Object)> {
+    let state = Arc::new(DriverObjectState::new());
+    INITIAL_STATE.with(|slot| *slot.borrow_mut() = Some(state.clone()));
+    let mut driver =
+        ManagedDriver::load_static(&(init::<CANCEL> as 
FFI_AdbcDriverInitFunc), version)?;
+    let database = driver.new_database()?;
+    let mut connection = database.new_connection()?;
+    let object = match kind {
+        ObjectKind::Connection => Object::Connection(connection),
+        ObjectKind::Statement => 
Object::Statement(connection.new_statement()?),
+    };
+    Ok((state, object))
+}
+
+type TestResult = std::result::Result<(), Box<dyn std::error::Error>>;
+
+#[test]
+fn cancel_is_concurrent() -> TestResult {
+    for kind in OBJECT_KINDS {
+        let (state, mut object) = setup::<true>(kind, AdbcVersion::V110)?;
+        let cancel = object.cancel_handle();
+        let execution = thread::spawn(move || (object.operate(), object));
+        let entered = wait_for(|| state.operations.load(SeqCst) == 1, TIMEOUT);
+        let cancellation = thread::spawn(move || cancel.try_cancel());
+        let overlapped = wait_for(|| state.cancellations.load(SeqCst) == 1, 
TIMEOUT);
+        state.release_operation.set(); // in case cancel didn't work, unblock
+        let (result, object) = execution.join().map_err(|_| "worker thread 
panicked")?;
+        cancellation
+            .join()
+            .map_err(|_| "worker thread panicked")??;
+        assert!(entered, "{kind:?} did not enter the driver");
+        assert!(overlapped, "{kind:?} blocked cancellation");
+        assert_eq!(
+            result.err().map(|error| error.status),
+            Some(Status::Cancelled)
+        );
+        assert!(!state.timed_out.load(SeqCst));
+        drop(object);
+    }
+    Ok(())
+}
+
+#[test]
+fn cancellation_keeps_native_handles_alive() -> TestResult {
+    for kind in OBJECT_KINDS {
+        let (state, object) = setup::<true>(kind, AdbcVersion::V110)?;
+        let clone = object.clone();
+        drop(clone);
+        assert_eq!(
+            state.releases(),
+            [0; 4],
+            "dropping a clone released {kind:?}"
+        );
+        state.release_cancel.clear();
+        let cancel = object.cancel_handle();
+        let expired = object.cancel_handle();
+        let cancellation = thread::spawn(move || cancel.try_cancel());
+        let entered = wait_for(|| state.cancellations.load(SeqCst) == 1, 
TIMEOUT);
+        drop(object);
+        let releases_during_cancel = state.releases();
+        state.release_cancel.set();
+        cancellation
+            .join()
+            .map_err(|_| "worker thread panicked")??;
+        assert!(entered);
+        assert_eq!(releases_during_cancel, [0; 4]);
+        let expected = match kind {
+            ObjectKind::Connection => [0, 1, 1, 1],
+            ObjectKind::Statement => [1, 1, 1, 1],
+        };
+        assert_eq!(state.releases(), expected);
+        assert!(!state.timed_out.load(SeqCst));
+        expired.try_cancel()?;
+        assert_eq!(state.cancellations.load(SeqCst), 1);
+    }
+    Ok(())
+}
+
+#[test]
+fn concurrent_cancellation_and_callback_errors() -> TestResult {
+    for kind in OBJECT_KINDS {
+        let (state, object) = setup::<true>(kind, AdbcVersion::V110)?;
+        state.release_cancel.clear();
+        state
+            .cancel_status
+            .store(Status::InvalidState.into(), SeqCst);
+        let cancel: Arc<dyn CancelHandle> = object.cancel_handle().into();
+        let second_cancel = cancel.clone();
+        let first = thread::spawn(move || cancel.try_cancel());
+        let second = thread::spawn(move || second_cancel.try_cancel());
+        let overlapped = wait_for(|| state.cancellations.load(SeqCst) == 2, 
TIMEOUT);
+        state.release_cancel.set();
+        let results = [
+            first.join().map_err(|_| "worker thread panicked")?,
+            second.join().map_err(|_| "worker thread panicked")?,
+        ];
+        assert!(overlapped, "{kind:?} cancellation calls did not overlap");
+        for result in results {
+            assert_eq!(
+                result.err().map(|error| error.status),
+                Some(Status::InvalidState)
+            );
+        }
+        assert!(!state.timed_out.load(SeqCst));
+    }
+    Ok(())
+}
+
+fn unsupported<const CANCEL: bool>(kind: ObjectKind, version: AdbcVersion) -> 
TestResult {
+    let (state, mut object) = setup::<CANCEL>(kind, version)?;
+    let cancel = object.cancel_handle();
+    let execution = thread::spawn(move || (object.operate(), object));
+    let entered = wait_for(|| state.operations.load(SeqCst) == 1, TIMEOUT);
+    let finished = Arc::new(AtomicBool::new(false));
+    let cancel_finished = finished.clone();
+    let cancellation = thread::spawn(move || {
+        let result = cancel.try_cancel();
+        cancel_finished.store(true, SeqCst);
+        result
+    });
+    let returned = wait_for(|| finished.load(SeqCst), TIMEOUT);
+    state.release_operation.set();
+    let (result, object) = execution.join().map_err(|_| "worker thread 
panicked")?;
+    let cancel_result = cancellation.join().map_err(|_| "worker thread 
panicked")?;
+    assert!(
+        entered && returned,
+        "unsupported {kind:?} cancellation blocked"
+    );
+    assert_eq!(
+        cancel_result.err().map(|error| error.status),
+        Some(Status::NotImplemented)
+    );
+    assert_eq!(
+        result.err().map(|error| error.status),
+        Some(Status::Cancelled)
+    );
+    assert_eq!(state.cancellations.load(SeqCst), 0);
+    assert!(!state.timed_out.load(SeqCst));
+    let expired = object.cancel_handle();
+    drop(object);
+    expired.try_cancel()?;
+    Ok(())
+}
+
+#[test]
+fn unsupported_cancellation_does_not_wait() -> TestResult {
+    for kind in OBJECT_KINDS {
+        unsupported::<false>(kind, AdbcVersion::V110)?;
+        unsupported::<true>(kind, AdbcVersion::V100)?;
+    }
+    Ok(())
+}
diff --git a/rust/ffi/src/options.rs b/rust/ffi/src/options.rs
index 8266af252..41adcfe83 100644
--- a/rust/ffi/src/options.rs
+++ b/rust/ffi/src/options.rs
@@ -167,6 +167,25 @@ pub fn set_option_connection(
     version: AdbcVersion,
     key: impl AsRef<str>,
     value: OptionValue,
+) -> Result<()> {
+    // SAFETY: The exclusive borrow keeps the handle alive and prevents 
concurrent access.
+    unsafe { set_option_connection_raw(driver, connection, version, key, 
value) }
+}
+
+/// Set an option through a raw connection handle.
+///
+/// # Safety
+///
+/// The handle must be valid for the driver and remain alive throughout this 
call.
+/// Ordinary operations on the handle must be serialized. Concurrent 
cancellation
+/// is permitted only as supported by the driver's ADBC cancellation contract.
+/// No references to the handle may conflict with accesses made by the driver.
+pub unsafe fn set_option_connection_raw(
+    driver: &FFI_AdbcDriver,
+    connection: *mut FFI_AdbcConnection,
+    version: AdbcVersion,
+    key: impl AsRef<str>,
+    value: OptionValue,
 ) -> Result<()> {
     let key = CString::new(key.as_ref())?;
     let mut error = FFI_AdbcError::with_driver(driver);
@@ -213,6 +232,25 @@ pub fn set_option_statement(
     version: AdbcVersion,
     key: impl AsRef<str>,
     value: OptionValue,
+) -> Result<()> {
+    // SAFETY: The exclusive borrow keeps the handle alive and prevents 
concurrent access.
+    unsafe { set_option_statement_raw(driver, statement, version, key, value) }
+}
+
+/// Set an option through a raw statement handle.
+///
+/// # Safety
+///
+/// The handle must be valid for the driver and remain alive throughout this 
call.
+/// Ordinary operations on the handle must be serialized. Concurrent 
cancellation
+/// is permitted only as supported by the driver's ADBC cancellation contract.
+/// No references to the handle may conflict with accesses made by the driver.
+pub unsafe fn set_option_statement_raw(
+    driver: &FFI_AdbcDriver,
+    statement: *mut FFI_AdbcStatement,
+    version: AdbcVersion,
+    key: impl AsRef<str>,
+    value: OptionValue,
 ) -> Result<()> {
     let key = CString::new(key.as_ref())?;
     let mut error = FFI_AdbcError::with_driver(driver);

Reply via email to