I'm trying to implement ODBC functions. One of them has this signature in C:
SQLRETURN SQLAllocHandle(
SQLSMALLINT HandleType,
SQLHANDLE InputHandle,
SQLHANDLE * OutputHandlePtr);
This will be called from outside of Rust.
This basically creates handles of a specified type and returns pointers to them in OutputHandlePtr
at which point my Rust code "forgets" about them (unsafe operation) so it now lives in C land.
At some later point my code might be called again with that handle as an InputHandle
.
According to the specification I now need to make sure to synchronize access to this handle.
This is how it is implemented in the PostgreSQL ODBC driver:
#define ENTER_ENV_CS(x) pthread_mutex_lock(&((x)->cs))
#define LEAVE_ENV_CS(x) pthread_mutex_unlock(&((x)->cs))
...
case SQL_HANDLE_DBC:
ENTER_ENV_CS((EnvironmentClass *) InputHandle);
ret = PGAPI_AllocConnect(InputHandle, OutputHandle);
LEAVE_ENV_CS((EnvironmentClass *) InputHandle);
break;
This is my very first time using unsafe and I'm not a Rust expert to begin with.
I'm wondering how I can replicate the ENTER_ENV_CS (CS = CriticalSection) / LEAVE_ENV_CS logic.
In Rust my signature looks like this:
pub fn SQLAllocHandle(
handle_type: HandleType,
input_handle: *mut c_void,
output_handle: *mut *mut c_void,
) -> SqlReturn {
...
}
And I can easily wrap input_handle
in a Mutex
but this method can be called multiple times in parallel from multiple threads and all of them would end up creating their own Mutexes.
My only idea so far is to have the synchronization object be part of the Handle I return.
struct EnvironmentClass<T> {
inner: Mutex<T>
}
But I wonder if there is a more direct translation of the C code.