MySQL NDB API AccessViolationException
Asked Answered
R

1

7

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
    ..
}
Rennie answered 8/4, 2016 at 10:23 Comment(11)
Do you mean ndb_api_init by init?Anomalous
@Anomalous Yes, I've made a mistake while c/p code. Edited, tnx.Rennie
And if you do the same (i.e. run init then read in different threads) in C++, without using C# wrapper, what would be the result?Reduplicate
@Reduplicate Nice one, didn't think of trying it. It breaks as well, on the same line, with the message: Exception thrown: write access violation._my_thread_var(...) returned nullptr.Rennie
Is that library you use open sourced? Oherwise it might be not easy to debug it. Maybe it stores something in thread local storage, then when you call read from another thread it expects this something is there, but it's not and hence this error. But without source that's just guesswork...Reduplicate
@Reduplicate Yes, it is open source, it is a part of MySQL Cluster that can be downloaded from dev.mysql.com/downloads/cluster (after the download you should use cmake and Visual Studio to create a VS solution). I tried to follow the stacktrace, but it is quite complicated. Also thought about TLS as a possible cause, but no luck so far.Rennie
This is only a guess, but it is most likely an issue with the locking mechanism used by MySQL and the differences in how Mutex works between Linux and Windows. It's been a while, but I believe one has named mutexes and the other has thread mutexes. I assume you are using handle as a singleton to share the connection between threads. I would suggest that probably doesn't work in Windows.Sumptuary
@IanThompson Yes, handle is obtained once and then shared. Will try to verify your claim, source code is a bit complicated..Rennie
@MiljenMikic I've experienced problems on Windows with MySQL and shared connections. It might be worth getting a new handle for each thread. Then you will most likely have a different problem, but it will solve this one. I don't think you will be able to solve the problem if it is in MySQL, but it may well be worth raising a ticket with the MySql team.Sumptuary
@IanThompson The reason I use NDB API is a tremendous performance increase compared to plain SQL. However, the most expensive part is obtaining a handle, so if you this once then it's worth using it but if you do it all the time then it's not. Posted a question to MySql Cluster mailing list.Rennie
@IanThompson I have found a workaround, a guy from MySQL Cluster mailing list gave me a pointer in a right direction. Thanks for the help!Rennie
R
2

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.

Rennie answered 15/4, 2016 at 13:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.