Posted to tcl by patthoyts at Thu Aug 14 23:08:59 GMT 2008view raw

  1. // ThreadCrash.cpp -
  2. //
  3. // Written in Munich airport to test thread safety of CComBSTR assignment.
  4. // The CComBSTR in the CThing class is not being used safely -- equivalent to
  5. // the usage we see in the CWiREQueueCom error handling. What we expect to see
  6. // here is a context switch in the middle of a CComBSTR::operator= which leaves
  7. // the object in partial state - the BSTR is not fully assigned. Then the next
  8. // thread tries to free the partially assigned BSTR and we get an exception
  9. // where windows claims an invalid address has been passed to SysFreeAlloc().
  10. // The solution is to be thread safe - either by keeping the string local or
  11. // by using a lock around member variable access.
  12. //
  13. // On a single core cpu this program will work correctly. The problem shows
  14. // up when you have either hyperthreading or multiple-cores.
  15. //
  16. // Compile using:
  17. // cl -nologo -W4 -MD -O2 -Gs ThreadCrash.cpp -link -debug -subsystem:console
  18. //
  19. // $Id$
  20.  
  21. #define STRICT
  22. #define WIN32_LEAN_AND_MEAN
  23. #define UNICODE
  24. #if defined(UNICODE) && !defined(_UNICODE)
  25. # define _UNICODE
  26. #endif
  27.  
  28. #include <atlbase.h>
  29. #include <process.h>
  30. #include <tchar.h>
  31.  
  32. class CThing
  33. {
  34. public:
  35. CComBSTR m_String;
  36. };
  37.  
  38. class CThread
  39. {
  40. public:
  41. CThread() : m_ID(0xffffffff), m_pThing(0) {}
  42. DWORD m_ID;
  43. CThing *m_pThing;
  44. };
  45.  
  46. DWORD WINAPI
  47. BeginThread(LPVOID pClientData)
  48. {
  49. CThread *pThread = (CThread *)pClientData;
  50. _tprintf(_T("Thread 0x%04x started\n"), pThread->m_ID);
  51.  
  52. WCHAR wsz[18];
  53. _snwprintf(wsz, 17, L"Thread 0x%04x", pThread->m_ID);
  54.  
  55. // This number should be sufficient that we get some context switches
  56. // before we have finished the job. We cannot use a Sleep() or the
  57. // context switch will occur when we call Sleep().
  58. const int max = 1000000;
  59. for (int n = 0; n < max; n++)
  60. {
  61. pThread->m_pThing->m_String = wsz;
  62. }
  63.  
  64. _tprintf(_T("Thread 0x%04x completed\n"), pThread->m_ID);
  65. return 0;
  66. }
  67.  
  68.  
  69. static void
  70. printerror(DWORD dwError)
  71. {
  72. LPTSTR sMsg;
  73. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
  74. | FORMAT_MESSAGE_FROM_SYSTEM
  75. | FORMAT_MESSAGE_IGNORE_INSERTS,
  76. NULL,
  77. dwError,
  78. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  79. (LPTSTR)&sMsg,
  80. 0,
  81. NULL);
  82.  
  83. _tprintf(sMsg);
  84. LocalFree(sMsg);
  85. }
  86.  
  87. int
  88. _tmain(void)
  89. {
  90. CThing * pThing = new CThing;
  91. CThread * pThread[10];
  92. HANDLE hThread[10];
  93. unsigned long n, i;
  94.  
  95. // Start all the threads in a suspended state. Each thread gets it's own
  96. // object but all objects point to the same CComBSTR via the same CThing
  97. // instance.
  98. for (n = 0; n < sizeof(hThread)/sizeof(hThread[0]); ++n)
  99. {
  100. pThread[n] = new CThread();
  101. pThread[n]->m_pThing = pThing;
  102. hThread[n] = CreateThread(0, 0, BeginThread,
  103. (LPVOID)pThread[n],
  104. CREATE_SUSPENDED,
  105. &pThread[n]->m_ID);
  106. }
  107.  
  108. // Start all the threads at the same time.
  109. for (i = 0; i < n; ++i)
  110. ResumeThread(hThread[i]);
  111.  
  112. // Wait for all threads to complete.
  113. while (1)
  114. {
  115. DWORD dwWait = WaitForMultipleObjects(n, hThread, TRUE, 1000);
  116. if (dwWait == WAIT_TIMEOUT)
  117. {
  118. _tprintf(_T("tick\n"));
  119. }
  120. else if (dwWait >= WAIT_OBJECT_0 && dwWait < (WAIT_OBJECT_0 + n))
  121. {
  122. break;
  123. }
  124. else
  125. {
  126. printerror(GetLastError());
  127. }
  128. }
  129.  
  130. // Clean up politely.
  131. for (i = 0; i < n; i++)
  132. CloseHandle(hThread[i]);
  133.  
  134. return 0;
  135. }