C ++线程附加/删除段错误

时间:2015-06-25 11:19:00

标签: c++ mysql linux multithreading plugins

我使用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

4 个答案:

答案 0 :(得分:1)

代码至少有两个问题:

  1. ReattachCurrentThread() / DetachCurrentThread()的来电未同步
  2. UnlockDatabaseUsage()并非总是被调用:函数MySQLPerformSelect()可以在不调用UnlockDatabaseUsage()的情况下返回
  3. 第一个问题可以解决如下:

    #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.
}