access violation inside ADODB recordset Close

1

Still trying to figure out what is happening with ADODB's connections and why a certain crash is occuring.

The problem was that we had a memory leak in our code:

void getDetailConfig()
{
    m_displayConf = new TestDetailDisplayCfg(); 
}

This function is called often so a basic memory leak. Fixed it with a unique pointer

void getDetailConfig()
{
    m_displayConf = std::make_unique<TestDetailDisplayCfg>();
}

Yay party, but now an acces violation started to happen inside ADODB's Recordset15::Close.

inline HRESULT Recordset15::Close ( ) {
    HRESULT _hr = raw_Close();
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _hr;
}

Unhandled exception at 0x679E653F (msado15.dll) in LaneControl.exe: 0xC000041D: An unhandled exception was encountered during a user callback.

So calling all destructors the right way caused a new issue, there are recordset's opened and closed somewhere.

After debugging it turns out that getDetailConfig is called from two different threads.

Thread1

void updateIconStatus()
{
    getDetailConfig();
}

thread1

Thread ID 5bA8

Thread2

void CVTSDetailDisplay::setCurrentTestIconStatus(int status)
{
    m_CurrentDialog->getDetailConfig();
}

thread2

Thread ID 6A4C

So these 2 threads call getDetailConfig where a recordset is closed which was opened on another thread and COM objects are Released and what not.

Is that a problem that you can't close ADO recordsets on another thread? Is it more a race condition? What is going wrong here at ADODB's level?

c++
com
ado

1 Answer

1

I'd think it is a race condition.

If the getDetailConfig() function was already called before, and then both threads call the getDetailConfig(), this can result in both threads calling the destructor (of the object which was there before) simultaneously (std::unique_ptr is not inherently thread-safe AFAIK).

You'd then need to ensure critical section for the exchange of the pointers, for example adding std::mutex m_mutex; as a member of your class (ideally to the first place in the members list so it remains valid longer than the m_displayConf member) and then adding

void getDetailConfig()
{
    std::unique_lock<std::mutex> lock(m_mutex);
    m_displayConf = std::make_unique<TestDetailDisplayCfg>();
}

to make sure the exchange is locked between threads.

answered on Stack Overflow Nov 23, 2018 by axalis • edited Nov 23, 2018 by axalis

User contributions licensed under CC BY-SA 3.0