我使用C ++编写的插件在MySQL上运行查询。它在Xojo(www.xojo.com)制作的应用程序中使用。
问题是,如果执行的查询太多,它会在linux上因分段错误而崩溃。
插件本身的工作原理是在执行查询之前从调用线程中分离,以便不阻塞主应用程序等,然后在完成后重新附加。我认为重新附加是问题(linux中的gdb调试看起来像这样)但是由于Xojo框架上没有符号,我不太确定。
这是用于分离和重新附加的两个方法/函数
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;
}
这里有一个叫做的地方:
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 a cursor
MySQLCursorData *curs = new MySQLCursorData;
bzero( curs, sizeof( MySQLCursorData ) );
curs->fCursor = new MySQLCursor( db );
retCursor = NewDBCursor( curs );
}
UnlockDatabaseUsage( db );
return retCursor;
}
我的问题是:上面的代码有什么问题吗?它是否会导致段错误,因为它不会在某种程度上小心等等?我不是一个C ++程序员,但在我的理解中似乎太 blunt ,就像没有试图查看线程是否可用。再次,我不是一个C ++程序员所以所有我说的可能是荒谬的等等......
"整体"插件的代码在这里: plugin's source
答案 0 :(得分:1)
代码至少有两个问题:
ReattachCurrentThread()
/ DetachCurrentThread()
的来电未同步UnlockDatabaseUsage()
并非总是被调用:函数MySQLPerformSelect()
可以在不调用UnlockDatabaseUsage()
的情况下返回第一个问题可以解决如下:
#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;
}
第二个问题可以解决如下:
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 a check for detachToken==nullptr is 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 a cursor
MySQLCursorData *curs = new MySQLCursorData;
bzero( curs, sizeof( MySQLCursorData ) );
curs->fCursor = new MySQLCursor( db );
retCursor = NewDBCursor( curs );
}
// Rely on the cleaner to call this: UnlockDatabaseUsage( db );
return retCursor;
}
答案 1 :(得分:0)
您确定DetachCurrentThread()
始终返回指向令牌的指针吗?添加以下检查,看看是否有帮助:
void *detachToken = DetachCurrentThread();
if (detachToken == nullptr) return nullptr; // ensure the pointer to a token is returned before proceeding further.
答案 2 :(得分:0)
如果代码仅在运行的查询过多时崩溃,您可能需要检查_UnsafeAttachCurrentThread
和_UnsafeDetachCurrentThread
是否存在可能导致段错误的竞争条件。如果查询太多,DetachCurrentThread
可能会返回nullptr吗?如果detachToken
在这种情况下有效,您可以添加一个检查。
此外,如果REALGetStringData
失败,可能会出现死锁。请在返回前致电UnlockDatabaseUsage
。
答案 3 :(得分:0)
我认为你的问题就在这里:
if (db->fConnection == nullptr) return nullptr;
变量db
是一个指针。在评估此条件之前,您需要进行检查:
if (!db)
{
// throw error, return from function call, etc.
}