Posted to tcl by patthoyts at Thu Aug 14 23:08:59 GMT 2008view raw
- // ThreadCrash.cpp -
- //
- // Written in Munich airport to test thread safety of CComBSTR assignment.
- // The CComBSTR in the CThing class is not being used safely -- equivalent to
- // the usage we see in the CWiREQueueCom error handling. What we expect to see
- // here is a context switch in the middle of a CComBSTR::operator= which leaves
- // the object in partial state - the BSTR is not fully assigned. Then the next
- // thread tries to free the partially assigned BSTR and we get an exception
- // where windows claims an invalid address has been passed to SysFreeAlloc().
- // The solution is to be thread safe - either by keeping the string local or
- // by using a lock around member variable access.
- //
- // On a single core cpu this program will work correctly. The problem shows
- // up when you have either hyperthreading or multiple-cores.
- //
- // Compile using:
- // cl -nologo -W4 -MD -O2 -Gs ThreadCrash.cpp -link -debug -subsystem:console
- //
- // $Id$
- #define STRICT
- #define WIN32_LEAN_AND_MEAN
- #define UNICODE
- #if defined(UNICODE) && !defined(_UNICODE)
- # define _UNICODE
- #endif
- #include <atlbase.h>
- #include <process.h>
- #include <tchar.h>
- class CThing
- {
- public:
- CComBSTR m_String;
- };
- class CThread
- {
- public:
- CThread() : m_ID(0xffffffff), m_pThing(0) {}
- DWORD m_ID;
- CThing *m_pThing;
- };
- DWORD WINAPI
- BeginThread(LPVOID pClientData)
- {
- CThread *pThread = (CThread *)pClientData;
- _tprintf(_T("Thread 0x%04x started\n"), pThread->m_ID);
- WCHAR wsz[18];
- _snwprintf(wsz, 17, L"Thread 0x%04x", pThread->m_ID);
- // This number should be sufficient that we get some context switches
- // before we have finished the job. We cannot use a Sleep() or the
- // context switch will occur when we call Sleep().
- const int max = 1000000;
- for (int n = 0; n < max; n++)
- {
- pThread->m_pThing->m_String = wsz;
- }
- _tprintf(_T("Thread 0x%04x completed\n"), pThread->m_ID);
- return 0;
- }
- static void
- printerror(DWORD dwError)
- {
- LPTSTR sMsg;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
- | FORMAT_MESSAGE_FROM_SYSTEM
- | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- dwError,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&sMsg,
- 0,
- NULL);
- _tprintf(sMsg);
- LocalFree(sMsg);
- }
- int
- _tmain(void)
- {
- CThing * pThing = new CThing;
- CThread * pThread[10];
- HANDLE hThread[10];
- unsigned long n, i;
- // Start all the threads in a suspended state. Each thread gets it's own
- // object but all objects point to the same CComBSTR via the same CThing
- // instance.
- for (n = 0; n < sizeof(hThread)/sizeof(hThread[0]); ++n)
- {
- pThread[n] = new CThread();
- pThread[n]->m_pThing = pThing;
- hThread[n] = CreateThread(0, 0, BeginThread,
- (LPVOID)pThread[n],
- CREATE_SUSPENDED,
- &pThread[n]->m_ID);
- }
- // Start all the threads at the same time.
- for (i = 0; i < n; ++i)
- ResumeThread(hThread[i]);
- // Wait for all threads to complete.
- while (1)
- {
- DWORD dwWait = WaitForMultipleObjects(n, hThread, TRUE, 1000);
- if (dwWait == WAIT_TIMEOUT)
- {
- _tprintf(_T("tick\n"));
- }
- else if (dwWait >= WAIT_OBJECT_0 && dwWait < (WAIT_OBJECT_0 + n))
- {
- break;
- }
- else
- {
- printerror(GetLastError());
- }
- }
- // Clean up politely.
- for (i = 0; i < n; i++)
- CloseHandle(hThread[i]);
- return 0;
- }