mysql - C++ thread attach/dettach segfaults -
i use plugin written in c++ running queries on mysql. it's used inside xojo (www.xojo.com) made application.
the problem if many queries executed crashes on linux segmentation fault.
the plugin works detaching calling thread before executing query in order not block main application etc , re-attaching once it's done. think re-attaching problem (gdb debugging in linux seems this) due not having symbols on xojo's framework i'm not sure.
this 2 methods/functions used detaching , re-attaching
void reattachcurrentthread(void *token) { static void (*pattachthread)(void*) = nullptr; if (!pattachthread) pattachthread = (void (*)(void *)) gresolver("_unsafeattachcurrentthread"); if (pattachthread) pattachthread( token ); } void * detachcurrentthread(void) { static void * (*pdetachthread)(void) = nullptr; if (!pdetachthread) pdetachthread = (void * (*)(void)) gresolver("_unsafedetachcurrentthread"); if (pdetachthread) return pdetachthread(); return nullptr; }
and here 1 place called:
realdbcursor mysqlperformselect(mysqldatabasedata *db, realstring querystr) { if (db->fconnection == nullptr) return nullptr; if (!lockdatabaseusage( db )) return nullptr; realstringdata stringdata; if (!realgetstringdata( querystr, realgetstringencoding( querystr ), &stringdata )) return nullptr; void *detachtoken = detachcurrentthread(); int err = mysql_real_query( db->fconnection, (const char *)stringdata.data, stringdata.length ); reattachcurrentthread( detachtoken ); db->capturelasterror(); realdisposestringdata( &stringdata ); realdbcursor retcursor = nullptr; if (0 == err) { // allocate cursor mysqlcursordata *curs = new mysqlcursordata; bzero( curs, sizeof( mysqlcursordata ) ); curs->fcursor = new mysqlcursor( db ); retcursor = newdbcursor( curs ); } unlockdatabaseusage( db ); return retcursor; }
my question is: there wrong the code above , expected cause segfault because it's not being careful somehow etc? i'm not c++ programmer seems blunt in understanding, not trying see if thread available first etc. again, i'm not c++ programmer i'm saying may absurd etc...
the "whole" plugin's code here: plugin's source
there @ least 2 problems code:
- the calls
reattachcurrentthread()
/detachcurrentthread()
not synchronized unlockdatabaseusage()
not called: functionmysqlperformselect()
can return without callingunlockdatabaseusage()
the first problem can fixed follows:
#include <mutex> std::mutex g_attachmentmutex; void reattachcurrentthread(void *token) { std::lock_guard<std::mutex> mlg(g_attachmentmutex); static void (*pattachthread)(void*) = nullptr; if (!pattachthread) pattachthread = (void (*)(void *)) gresolver("_unsafeattachcurrentthread"); if (pattachthread) pattachthread( token ); } void * detachcurrentthread(void) { std::lock_guard<std::mutex> mlg(g_attachmentmutex); static void * (*pdetachthread)(void) = nullptr; if (!pdetachthread) pdetachthread = (void * (*)(void)) gresolver("_unsafedetachcurrentthread"); if (pdetachthread) return pdetachthread(); return nullptr; }
the second problem can fixed follows:
class mysqlperformselectcleaner { mysqldatabasedata *_db; public: mysqlperformselectcleaner(mysqldatabasedata *db) : _db(db) { } ~mysqlperformselectcleaner() { unlockdatabaseusage(_db); } }; realdbcursor mysqlperformselect(mysqldatabasedata *db, realstring querystr) { if (db == nullptr || db->fconnection == nullptr) return nullptr; if (!lockdatabaseusage( db )) return nullptr; mysqlperformselectcleaner c(db); realstringdata stringdata; if (!realgetstringdata( querystr, realgetstringencoding( querystr ), &stringdata )) { return nullptr; } void *detachtoken = detachcurrentthread(); // perhaps check detachtoken==nullptr needed here int err = mysql_real_query( db->fconnection, (const char *)stringdata.data, stringdata.length ); reattachcurrentthread( detachtoken ); db->capturelasterror(); realdisposestringdata( &stringdata ); realdbcursor retcursor = nullptr; if (0 == err) { // allocate cursor mysqlcursordata *curs = new mysqlcursordata; bzero( curs, sizeof( mysqlcursordata ) ); curs->fcursor = new mysqlcursor( db ); retcursor = newdbcursor( curs ); } // rely on cleaner call this: unlockdatabaseusage( db ); return retcursor; }
Comments
Post a Comment