Unlock the power of threadcall

Working on writing drivers for databases, I see that the threadcall macro would be a good fit for calling external C code that does IO operations.

After some testing, it looks like the macro does not support a common pattern that is to use a pointer handle as arguments in the C funcion.

Any info on what would it take to tackle this issue?

https://github.com/JuliaLang/julia/issues/30864

1 Like

I’ve seen that issue and I originally implemented the threadcall macro but I must confess I don’t really get the issue. What is the error that happens? Is it cfunction that fails? Can’t you use a raw Ptr type instead of a Ref?

The error I get from a threadcall is:

ERROR: LoadError: argument is an abstract type; size is indeterminate.

After some debugging, I found out that this is happening because of the Ref arguments.

The API I’m calling makes use of output arguments.

So, in the example I posted, the C function expects a parameter of type dpiConn **conn, which is an output parameter.

Using pure C, the API would be called using:

    dpiConn *conn;

    dpiConn_create(context, params->mainUserName,
            params->mainUserNameLength, params->mainPassword,
            params->mainPasswordLength, params->connectString,
            params->connectStringLength, NULL, NULL, &conn);

To mimic that in Julia, I use a Ref{Ptr{Cvoid}} argument to pass to ccall. Then I’ll retrieve the value returned by the C API using:

conn_handle_ref = Ref{Ptr{Cvoid}}()
... # call C api
return conn_handle_ref[] # return the handle.

Have you tried using Ptr instead of Ref?

I might be missing some basic understanding on how Ptr works, but how am I supposed to use a Ptr argument as IN/OUT parameter under these conditions?

The function call is this:

conn_handle_ref = Ref{Ptr{Cvoid}}()
ora_common_params = OraCommonCreateParams(ctx, common_params)
ora_conn_create_params = OraConnCreateParams(ctx, conn_create_params)
result = dpiConn_create(ctx.handle, user, password, connect_string, Ref(ora_common_params), Ref(ora_conn_create_params), conn_handle_ref)
error_check(ctx, result)

my_new_connection = Connection(ctx, conn_handle_ref[], conn_create_params.pool)

Where

function dpiConn_create(context_handle::Ptr{Cvoid}, user::String, password::String, connect_string::String, common_params_ref::Ref{OraCommonCreateParams}, conn_create_params_ref::Ref{OraConnCreateParams}, dpi_conn_handle_ref::Ref{Ptr{Cvoid}})
    userNameLength = sizeof(user)
    passwordLength = sizeof(password)
    connectStringLength = sizeof(connect_string)

    ccall((:dpiConn_create, libdpi), OraResult, (Ptr{Cvoid}, Ptr{UInt8}, UInt32, Ptr{UInt8}, UInt32, Ptr{UInt8}, UInt32, Ref{OraCommonCreateParams}, Ref{OraConnCreateParams}, Ref{Ptr{Cvoid}}), context_handle, user, userNameLength, password, passwordLength, connect_string, connectStringLength, common_params_ref, conn_create_params_ref, dpi_conn_handle_ref)
end

and the C function definition is

int dpiConn_create(const dpiContext *context, const char *userName, uint32_t userNameLength, const char *password, uint32_t passwordLength, const char *connectString, uint32_t connectStringLength, dpiCommonCreateParams *commonParams, dpiConnCreateParams *createParams, dpiConn **conn)