2016-04-08 24 views
7

Sono di fronte a un problema interessante che si verifica solo su Windows, mentre su Linux/Mono tutto funziona correttamente. Ho costruito un wrapper C++ attorno alla libreria MySQL Cluster NDB API che chiamo dal codice C# attraverso P/Invoke. Finora ho tre metodi:API NDB MySQL AccessViolationException

  1. init() che inizializza l'ambiente e restituisce un handle
  2. read() che legge i dati dal database utilizzando la maniglia
  3. release() che rilascia le risorse

init() funziona bene in ogni configurazione. Tuttavia, read() genera AccessViolationException, ma solo su Windows e solo nel caso in cui init() viene richiamato in un thread e read() in un altro. Se facciamo tutto in un singolo thread, funziona! Qualsiasi pensiero sulla possibile causa sarebbe apprezzato.

Aggiornamento 2016/12/04 - lo stesso comportamento è stato notato con un programma di test semplice C++ dove init() viene richiamato in un thread e read() in un altro. Pertanto, l'errore non ha nulla con l'interoperabilità C#/C++, è qualcosa all'interno della libreria API NDB che può essere scaricato here (cartella lib, mysqlclient.lib e ndbclient_static.lib).

codice C#:

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); 
    ... 
} 

codice C++: (sulla base di esempi tratti dalla 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 
    .. 
} 
+0

Intendi 'ndb_api_init' di' init'? – Ripple

+0

@Ripple Sì, ho commesso un errore durante il codice c/p. Modificato, tnx. –

+0

E se fai lo stesso (cioè esegui init poi leggi in thread diversi) in C++, senza usare il wrapper C#, quale sarebbe il risultato? – Evk

risposta

2

Con l'aiuto di MySQL mailing Cluster list, ho trovato una soluzione per questo problema .

Se si desidera leggere i dati attraverso NDB API in un thread diverso da quello in cui è stato richiamato ndb_init() ed ottenuto Ndb_cluster_connection, è necessario chiamare mysql_thread_init() all'inizio di che altro thread. Tuttavia, questo non è un comportamento previsto, né è documentato quindi ho archiviato a bug.