【问题标题】:MySQL Connector/C++: get_driver_instance() crashes when called from global constructMySQL 连接器/C++:从全局构造调用时 get_driver_instance() 崩溃
【发布时间】:2019-01-22 15:10:08
【问题描述】:

我在 Windows 10 上使用 MySQL 连接器/C++。我观察到,当我从全局对象的 c'tor 调用 get_driver_instance 时,它只是崩溃并抛出异常:访问冲突读取位置。

示例代码:

#define CPPCONN_LIB_BUILD // Need to include this as we are linking mysql connector in static library
#include "MySQL\Src\mysql-connector-c++-1.1.5\cppconn\driver.h"

#pragma comment(lib,"MySQL\\Src\\mysql-connector-c++-1.1.5\\BLD\\driver\\Debug\\mysqlcppconn-static.lib")
#pragma comment(lib,"MySQL\\Src\\mysql-5.6.24\\BLD\\libmysql\\Debug\\mysqlclient.lib")

struct someclass
{
    sql::Driver *m_pDriver;

    someclass()
    {
        /* Create a connection */
        m_pDriver= get_driver_instance(); // It crashes here
        cout << "print something";
    }
} someclass_instance;

int main(int argc, char** argv) 
{
    // We don't need to do anything here. The problem occurs in the global class constructor which executes before main as we've defined its global instance.

    return 0;
}

抛出的异常类似于:

Sample.exe 中 0x000007F6BE17485D 处未处理的异常:0xC0000005: 访问冲突读取位置0x0000000000000008。

崩溃时的堆栈跟踪:

Sample.exe!std::_Tree<class std::_Tmap_traits<class sql::SQLString,class boost::shared_ptr<class sql::mysql::MySQL_Driver>,struct std::less<class sql::SQLString>,class std::allocator<struct std::pair<class sql::SQLString const ,class boost::shared_ptr<class sql::mysql::MySQL_Driver> > >,0> >::_Lbound(class sql::SQLString const &) Unknown
Sample.exe!std::_Tree<class std::_Tmap_traits<class sql::SQLString,class boost::shared_ptr<class sql::mysql::MySQL_Driver>,struct std::less<class sql::SQLString>,class std::allocator<struct std::pair<class sql::SQLString const ,class boost::shared_ptr<class sql::mysql::MySQL_Driver> > >,0> >::lower_bound(class sql::SQLString const &) Unknown
Sample.exe!std::_Tree<class std::_Tmap_traits<class sql::SQLString,class boost::shared_ptr<class sql::mysql::MySQL_Driver>,struct std::less<class sql::SQLString>,class std::allocator<struct std::pair<class sql::SQLString const ,class boost::shared_ptr<class sql::mysql::MySQL_Driver> > >,0> >::find(class sql::SQLString const &)    Unknown
Sample.exe!sql::mysql::get_driver_instance_by_name(char const * const)  Unknown
Sample.exe!sql::mysql::get_driver_instance(void)    Unknown
Sample.exe!get_driver_instance()    Unknown
Sample.exe!someclass::someclass() Line 72   C++
Sample.exe!`dynamic initializer for 'obj''() Line 76    C++
Sample.exe!_initterm(void (void) * * pfbegin, void (void) * * pfend) Line 894   C
Sample.exe!_cinit(int initFloatingPrecision) Line 303   C
Sample.exe!__tmainCRTStartup() Line 227 C
Sample.exe!mainCRTStartup() Line 164    C

令人惊讶的是,这只发生在调试版本中。但是,这与this 问题中的情况不同。 我已确定我正在链接 Debug 构建的连接器库。此外,如果我实例化相同的对象 someclass_instance 但在 main 函数内部它不会在那里崩溃。 这真的很奇怪。我怀疑与 CRT 初始化有关,但不确定。

注意:我花了好几个小时才弄清楚这一点。这是真正的问题。在投反对票之前,请至少尝试一次代码。

如果有人能发光就太好了。

【问题讨论】:

标签: c++ mysql mysql-connector


【解决方案1】:

您不能依赖静态初始化不同类的顺序(例如,在您的情况下,调试和发布版本之间的顺序不同)。

这被称为static initialization order ‘fiasco’

您必须避免调用静态初始化程序中的任何代码,该程序本身依赖于静态初始化。

MySQL 驱动程序有各种静态初始化程序,可能会导致您出现问题的是map of names to drivers

避免这种情况的一种方法是使用延迟构造的单例,使用函数级静态,该函数在第一次调用函数时初始化:

struct someclass
{
    sql::Driver *m_pDriver;

    someclass()
    {
        /* Create a connection */
        m_pDriver= get_driver_instance(); // It crashes here
        cout << "print something";
    }

    void foo() {}
};

someclass& someclass_instance()
{
    static someclass instance;
    return instance;
}

int main(int argc, char** argv) 
{
    someclass_instance().foo();
    return 0;
}

注意someclass_instance() 可能不是线程安全的,如果您没有使用最新的编译器(例如 Visual Studio 2013 或更早版本),如果是这种情况,您需要确保从单个线程调用您的函数一次在启动其他使用该函数的线程之前。

您还可以通过将驱动程序映射移动到 get_driver_instance_by_name 中来修复 MySQL 代码(或提交错误报告,要求他们为您执行此操作)。

【讨论】:

  • 这确实很棒,尤其是MySQL driver has various static initialisers。我没有想到它 :) 非常感谢您对其进行调查并仔细考虑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-21
  • 2011-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-27
  • 1970-01-01
相关资源
最近更新 更多