Description:
I have faced an interesting issue that happens only on Windows, while on Linux everything works just fine. I made a simple C++ application based on your official documentation (https://dev.mysql.com/doc/ndbapi/en/ndb-examples.html) where I perform NDB initialization in one thread, and read data in another. The error I get while trying to read data is „write access violation _my_thread_var(...) returned nullptr”. However, if I do everything in a single thread – it works. My C++ application references „ndbclient_static.lib“ and „mysqlclient.lib“ from the /lib folder of MySQL Cluster installation. Code is attached below.
This is a really simple scenario, where as a part of a larger project you wish to call ndb_init() and get Ndb_cluster_connection object in one thread (let's call it init thread), and perform NDB read/writes in another thread (let's call it worker thread). Worker thread should not obtain Ndb_cluster_connection every time when runs, because this is expensive and there is no performance benefit, which is otherwise tremendous.
How to repeat:
Build and run a C++ program that contains two methods listed below (first replace "my_database_name" and "my_table_name" with real values). Invoke init() method in one thread, and read() method in another thread and you should get the exception under Windows, while under Linux everything works fine.
#include <iostream>
#include <mysql.h>
#include <NdbApi.hpp>
Ndb_cluster_connection* 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 was not ready within 30 secs,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;
}
char** read(Ndb_cluster_connection* cluster_connection) {
int count = 10;
char** ret_val = new char*[count];
Ndb* ndb_obj = new Ndb(cluster_connection, "my_database_name");
if(ndb_obj->init()) {
std::cout << "Ndb not initialized" << std::endl;
exit(-1);
}
NdbRecAttr **attr = new NdbRecAttr*[count];
const NdbDictionary::Dictionary* dict = ndb_obj->getDictionary();
const NdbDictionary::Table *table = dict->getTable("my_table_name"); // THIS IS THE LINE THAT THROWS ACCESS VIOLATION EXCEPTION
// ..
return ret_val;
}
Suggested fix:
Based on the suggestion of Mauritz Sundell from MySQL Cluster mailing list, adding mysql_thread_init() in a worker thread before invoking read() resolves this issue. However, this is not documented anywhere and it is not clear why should we do this.