Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MySQL NDB API AccessViolationException

I am facing an interesting issue that happens only on Windows, while on Linux/Mono everything works fine. I have built a C++ wrapper around MySQL Cluster NDB API library that I call from C# code through P/Invoke. So far I have three methods:

  1. init() which initializes the environment and returns a handle
  2. read() which reads the data from the database using the handle
  3. release() which releases resources

init() works fine in every setup. However, read() throws AccessViolationException, but only on Windows and only in case when init() is invoked in one thread and read() in another. If we do everything in a single thread, it works! Any thoughts about the possible cause would be appreciated.

Update 12/4/2016 - the same behavior has been noticed with a simple C++ test program where init() is invoked in one thread and read() in another. Therefore, the error has nothing with C#/C++ interop, it is something within NDB API library that can be downloaded here (lib folder, mysqlclient.lib and ndbclient_static.lib).

C# code:

private const string ndbapi = "ndb";

[DllImport(ndbapi)]
public extern static IntPtr init(IntPtr conn_string);

[DllImport(ndbapi)]
public extern static void read(IntPtr ndb_cluster_conn);

[DllImport(ndbapi)]
public extern static void release(IntPtr ndb_cluster_conn, IntPtr char_arr_ptr);

private static IntPtr handle;

private static void InitNdb() {
    unsafe {
        fixed (byte* conn_buf = Encoding.UTF8.GetBytes("node1:1186")) {            
            handle = ndb_api_init(new IntPtr(conn_buf));
        }
    }
}
static void Main(string[] args) {
    Thread t = new Thread(InitNdb);// IF I CALL InitNDB IN THE SAME THREAD, EVERYTHING WORKS
    t.Start();
    .. //waiting for Thread t to complete...
    IntPtr bytes_read = read(handle);
    ...
}

C++ code: (based on examples from the official documentation):

#if defined(WIN32)

#define DLLEXPORT __declspec(dllexport)
#define CALLCV __stdcall

#else

#define DLLEXPORT __attribute__((visibility("default")))
#define CALLCV __attribute__((cdecl))

#endif
..
extern "C" DLLEXPORT Ndb_cluster_connection* CALLCV init(char* connection_string) {

    ndb_init();

    // Object representing the cluster
    Ndb_cluster_connection* cluster_connection = new Ndb_cluster_connection(connection_string); 

    if (cluster_connection->connect(3, 2, 1)) {
        std::cout << "Cluster management server not ready,error:" << cluster_connection->get_latest_error_msg() << "\n";
        exit(-1);
    }
    if (cluster_connection->wait_until_ready(10, 0) < 0) {
        std::cout << "Cluster not ready within 10 secs, error:" << cluster_connection->get_latest_error_msg() << std::endl;
    }

    return cluster_connection;
}
extern "C" DLLEXPORT char** CALLCV read(Ndb_cluster_connection* cluster_connection) {
    Ndb *ndb_obj = new Ndb(cluster_connection, db_name);
    if (ndb_obj->init()) {
        std::cout << "Error while initializing Ndb " << std::endl;
    }

    const NdbDictionary::Dictionary* dict = ndb_obj->getDictionary();
    const NdbDictionary::Table *table = dict->getTable("table_name"); <-- THIS IS THE LINE THAT THROWS ACCESS VIOLATION EXCEPTION
    ..
}
like image 876
Miljen Mikic Avatar asked Apr 08 '16 10:04

Miljen Mikic


1 Answers

With the help of MySQL Cluster mailing list, I have found a workaround for this issue.

If you want to read data through NDB API in a thread different from the one where you invoked ndb_init() and obtained Ndb_cluster_connection, it is necessary to call mysql_thread_init() at the beginning of that other thread. However, this is not an expected behavior, nor is documented so I filed a bug.

like image 157
Miljen Mikic Avatar answered Sep 18 '22 12:09

Miljen Mikic