home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / afxtls.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  13KB  |  501 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12. #include <stddef.h>
  13.  
  14. #ifdef AFX_INIT_SEG
  15. #pragma code_seg(AFX_INIT_SEG)
  16. #endif
  17.  
  18. #ifdef _DEBUG
  19. #undef THIS_FILE
  20. static char THIS_FILE[] = __FILE__;
  21. #endif
  22.  
  23. /////////////////////////////////////////////////////////////////////////////
  24. // CSimpleList
  25.  
  26. void CSimpleList::AddHead(void* p)
  27. {
  28.     ASSERT(p != NULL);
  29.     ASSERT(*GetNextPtr(p) == NULL);
  30.  
  31.     *GetNextPtr(p) = m_pHead;
  32.     m_pHead = p;
  33. }
  34.  
  35. BOOL CSimpleList::Remove(void* p)
  36. {
  37.     ASSERT(p != NULL);
  38.  
  39.     if (m_pHead == NULL)
  40.         return FALSE;
  41.  
  42.     BOOL bResult = FALSE;
  43.     if (m_pHead == p)
  44.     {
  45.         m_pHead = *GetNextPtr(p);
  46.         DEBUG_ONLY(*GetNextPtr(p) = NULL);
  47.         bResult = TRUE;
  48.     }
  49.     else
  50.     {
  51.         void* pTest = m_pHead;
  52.         while (pTest != NULL && *GetNextPtr(pTest) != p)
  53.             pTest = *GetNextPtr(pTest);
  54.         if (pTest != NULL)
  55.         {
  56.             *GetNextPtr(pTest) = *GetNextPtr(p);
  57.             DEBUG_ONLY(*GetNextPtr(p) = NULL);
  58.             bResult = TRUE;
  59.         }
  60.     }
  61.     return bResult;
  62. }
  63.  
  64. /////////////////////////////////////////////////////////////////////////////
  65. // CNoTrackObject
  66.  
  67. #if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
  68. void* PASCAL CNoTrackObject::operator new(size_t nSize, LPCSTR, int)
  69. {
  70.     return CNoTrackObject::operator new(nSize);
  71. }
  72.  
  73. #if _MSC_VER >= 1200
  74. void PASCAL CNoTrackObject::operator delete(void* pObject, LPCSTR, int)
  75. {
  76.     if (pObject != NULL)
  77.         ::LocalFree(pObject);
  78. }
  79. #endif
  80. #endif
  81.  
  82. void* PASCAL CNoTrackObject::operator new(size_t nSize)
  83. {
  84.     void* p = ::LocalAlloc(LPTR, nSize);
  85.     if (p == NULL)
  86.         AfxThrowMemoryException();
  87.     return p;
  88. }
  89.  
  90. void PASCAL CNoTrackObject::operator delete(void* p)
  91. {
  92.     if (p != NULL)
  93.         ::LocalFree(p);
  94. }
  95.  
  96. /////////////////////////////////////////////////////////////////////////////
  97. // CThreadSlotData
  98.  
  99. // global _afxThreadData used to allocate thread local indexes
  100. BYTE __afxThreadData[sizeof(CThreadSlotData)];
  101. CThreadSlotData* _afxThreadData;
  102.  
  103. struct CThreadData : public CNoTrackObject
  104. {
  105.     CThreadData* pNext; // required to be member of CSimpleList
  106.     int nCount;         // current size of pData
  107.     LPVOID* pData;      // actual thread local data (indexed by nSlot)
  108. };
  109.  
  110. struct CSlotData
  111. {
  112.     DWORD dwFlags;      // slot flags (allocated/not allocated)
  113.     HINSTANCE hInst;    // module which owns this slot
  114. };
  115.  
  116. // flags used for CSlotData::dwFlags above
  117. #define SLOT_USED   0x01    // slot is allocated
  118.  
  119. __declspec(nothrow) CThreadSlotData::CThreadSlotData()
  120. {
  121.     m_list.Construct(offsetof(CThreadData, pNext));
  122.  
  123.     // initialize state and allocate TLS index
  124.     m_nAlloc = 0;
  125.     m_nRover = 1;   // first slot (0) is always reserved
  126.     m_nMax = 0;
  127.     m_pSlotData = NULL;
  128.  
  129.     // init m_tlsIndex to -1 if !bThreadLocal, otherwise TlsAlloc
  130.     m_tlsIndex = TlsAlloc();
  131.     if (m_tlsIndex == (DWORD)-1)
  132.         AfxThrowMemoryException();
  133.  
  134.     InitializeCriticalSection(&m_sect);
  135. }
  136.  
  137. CThreadSlotData::~CThreadSlotData()
  138. {
  139.     if (m_tlsIndex != (DWORD)-1)
  140.     {
  141.         TlsFree(m_tlsIndex);
  142.         DEBUG_ONLY(m_tlsIndex = (DWORD)-1);
  143.     }
  144.  
  145.     CThreadData* pData = m_list;
  146.     while (pData != NULL)
  147.     {
  148.         CThreadData* pDataNext = pData->pNext;
  149.         DeleteValues(pData, NULL);
  150.         pData = pDataNext;
  151.     }
  152.  
  153.     if (m_pSlotData != NULL)
  154.     {
  155.         HGLOBAL hSlotData = GlobalHandle(m_pSlotData);
  156.         GlobalUnlock(hSlotData);
  157.         GlobalFree(hSlotData);
  158.         DEBUG_ONLY(m_pSlotData = NULL);
  159.     }
  160.  
  161.     DeleteCriticalSection(&m_sect);
  162. }
  163.  
  164. int CThreadSlotData::AllocSlot()
  165. {
  166.     EnterCriticalSection(&m_sect);
  167.     int nAlloc = m_nAlloc;
  168.     int nSlot = m_nRover;
  169.     if (nSlot >= nAlloc || (m_pSlotData[nSlot].dwFlags & SLOT_USED))
  170.     {
  171.         // search for first free slot, starting at beginning
  172.         for (nSlot = 1;
  173.             nSlot < nAlloc && (m_pSlotData[nSlot].dwFlags & SLOT_USED); nSlot++)
  174.             ;
  175.  
  176.         // if none found, need to allocate more space
  177.         if (nSlot >= nAlloc)
  178.         {
  179.             // realloc memory for the bit array and the slot memory
  180.             int nNewAlloc = m_nAlloc+32;
  181.  
  182.             // m_pSlotData is allocated GMEM_SHARE because on Win32s it needs
  183.             // to be shared between processes (memory is owned by the MFC DLL).
  184.             HGLOBAL hSlotData;
  185.             if (m_pSlotData == NULL)
  186.             {
  187.                 hSlotData = GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE,
  188.                     nNewAlloc*sizeof(CSlotData));
  189.             }
  190.             else
  191.             {
  192.                 hSlotData = GlobalHandle(m_pSlotData);
  193.                 GlobalUnlock(hSlotData);
  194.                 hSlotData = GlobalReAlloc(hSlotData, nNewAlloc*sizeof(CSlotData),
  195.                     GMEM_MOVEABLE|GMEM_SHARE);
  196.             }
  197.             if (hSlotData == NULL)
  198.             {
  199.                 GlobalLock(GlobalHandle(m_pSlotData));
  200.                 LeaveCriticalSection(&m_sect);
  201.                 AfxThrowMemoryException();
  202.             }
  203.             CSlotData* pSlotData = (CSlotData*)GlobalLock(hSlotData);
  204.  
  205.             // always zero initialize after success
  206.             memset(pSlotData+m_nAlloc, 0, (nNewAlloc-m_nAlloc)*sizeof(CSlotData));
  207.             m_nAlloc = nNewAlloc;
  208.             m_pSlotData = pSlotData;
  209.         }
  210.     }
  211.     ASSERT(nSlot != 0); // first slot (0) is reserved
  212.  
  213.     // adjust m_nMax to largest slot ever allocated
  214.     if (nSlot >= m_nMax)
  215.         m_nMax = nSlot+1;
  216.  
  217.     ASSERT(!(m_pSlotData[nSlot].dwFlags & SLOT_USED));
  218.     m_pSlotData[nSlot].dwFlags |= SLOT_USED;
  219.     // update m_nRover (likely place to find a free slot is next one)
  220.     m_nRover = nSlot+1;
  221.  
  222.     LeaveCriticalSection(&m_sect);
  223.     return nSlot;   // slot can be used for FreeSlot, GetValue, SetValue
  224. }
  225.  
  226. void CThreadSlotData::FreeSlot(int nSlot)
  227. {
  228.     EnterCriticalSection(&m_sect);
  229.     ASSERT(nSlot != 0 && nSlot < m_nMax);
  230.     ASSERT(m_pSlotData != NULL);
  231.     ASSERT(m_pSlotData[nSlot].dwFlags & SLOT_USED);
  232.  
  233.     // delete the data from all threads/processes
  234.     CThreadData* pData = m_list;
  235.     while (pData != NULL)
  236.     {
  237.         if (nSlot < pData->nCount)
  238.         {
  239.             delete (CNoTrackObject*)pData->pData[nSlot];
  240.             pData->pData[nSlot] = NULL;
  241.         }
  242.         pData = pData->pNext;
  243.     }
  244.     // free the slot itself
  245.     m_pSlotData[nSlot].dwFlags &= ~SLOT_USED;
  246.     LeaveCriticalSection(&m_sect);
  247. }
  248.  
  249. // special version of CThreadSlotData::GetData that only works with
  250. // thread local storage (and not process local storage)
  251. // this version is inlined and simplified for speed
  252. inline void* CThreadSlotData::GetThreadValue(int nSlot)
  253. {
  254.     ASSERT(nSlot != 0 && nSlot < m_nMax);
  255.     ASSERT(m_pSlotData != NULL);
  256.     ASSERT(m_pSlotData[nSlot].dwFlags & SLOT_USED);
  257.     ASSERT(m_tlsIndex != (DWORD)-1);
  258.  
  259.     CThreadData* pData = (CThreadData*)TlsGetValue(m_tlsIndex);
  260.     if (pData == NULL || nSlot >= pData->nCount)
  261.         return NULL;
  262.     return pData->pData[nSlot];
  263. }
  264.  
  265. void CThreadSlotData::SetValue(int nSlot, void* pValue)
  266. {
  267.     ASSERT(nSlot != 0 && nSlot < m_nMax);
  268.     ASSERT(m_pSlotData != NULL);
  269.     ASSERT(m_pSlotData[nSlot].dwFlags & SLOT_USED);
  270.  
  271.     CThreadData* pData = (CThreadData*)TlsGetValue(m_tlsIndex);
  272.     if (pData == NULL || nSlot >= pData->nCount && pValue != NULL)
  273.     {
  274.         // if pData is NULL then this thread has not been visited yet
  275.         if (pData == NULL)
  276.         {
  277.             pData = new CThreadData;
  278.             pData->nCount = 0;
  279.             pData->pData = NULL;
  280.             DEBUG_ONLY(pData->pNext = NULL);
  281.  
  282.             // protect while adding to global list
  283.             EnterCriticalSection(&m_sect);
  284.             m_list.AddHead(pData);
  285.             LeaveCriticalSection(&m_sect);
  286.         }
  287.  
  288.         // grow to now current size
  289.         if (pData->pData == NULL)
  290.             pData->pData = (void**)LocalAlloc(LMEM_FIXED, m_nMax*sizeof(LPVOID));
  291.         else
  292.             pData->pData = (void**)LocalReAlloc(pData->pData, m_nMax*sizeof(LPVOID),
  293.                 LMEM_MOVEABLE);
  294.         if (pData->pData == NULL)
  295.             AfxThrowMemoryException();
  296.  
  297.         // initialize the newly allocated part
  298.         memset(pData->pData + pData->nCount, 0,
  299.             (m_nMax - pData->nCount) * sizeof(LPVOID));
  300.         pData->nCount = m_nMax;
  301.         TlsSetValue(m_tlsIndex, pData);
  302.     }
  303.     ASSERT(pData->pData != NULL && nSlot < pData->nCount);
  304.     pData->pData[nSlot] = pValue;
  305. }
  306.  
  307. void CThreadSlotData::AssignInstance(HINSTANCE hInst)
  308. {
  309.     EnterCriticalSection(&m_sect);
  310.     ASSERT(m_pSlotData != NULL);
  311.     ASSERT(hInst != NULL);
  312.  
  313.     for (int i = 1; i < m_nMax; i++)
  314.     {
  315.         if (m_pSlotData[i].hInst == NULL && (m_pSlotData[i].dwFlags & SLOT_USED))
  316.             m_pSlotData[i].hInst = hInst;
  317.     }
  318.     LeaveCriticalSection(&m_sect);
  319. }
  320.  
  321. void CThreadSlotData::DeleteValues(CThreadData* pData, HINSTANCE hInst)
  322. {
  323.     // Note: does not lock critical section because only meant to be called
  324.     // from DeleteValues(HINSTANCE, BOOL).
  325.  
  326.     ASSERT(pData != NULL);
  327.  
  328.     // free each element in the table
  329.     BOOL bDelete = TRUE;
  330.     for (int i = 1; i < pData->nCount; i++)
  331.     {
  332.         if (hInst == NULL || m_pSlotData[i].hInst == hInst)
  333.         {
  334.             // delete the data since hInst matches (or is NULL)
  335.             delete (CNoTrackObject*)pData->pData[i];
  336.             pData->pData[i] = NULL;
  337.         }
  338.         else if (pData->pData[i] != NULL)
  339.         {
  340.             // don't delete thread data if other modules still alive
  341.             bDelete = FALSE;
  342.         }
  343.     }
  344.  
  345.     if (bDelete)
  346.     {
  347.         // remove from master list and free it
  348.         EnterCriticalSection(&m_sect);
  349.         m_list.Remove(pData);
  350.         LeaveCriticalSection(&m_sect);
  351.         LocalFree(pData->pData);
  352.         delete pData;
  353.  
  354.         // clear TLS index to prevent from re-use
  355.         TlsSetValue(m_tlsIndex, NULL);
  356.     }
  357. }
  358.  
  359. void CThreadSlotData::DeleteValues(HINSTANCE hInst, BOOL bAll)
  360. {
  361.     EnterCriticalSection(&m_sect);
  362.     if (!bAll)
  363.     {
  364.         // delete the values only for the current thread
  365.         CThreadData* pData = (CThreadData*)TlsGetValue(m_tlsIndex);
  366.         if (pData != NULL)
  367.             DeleteValues(pData, hInst);
  368.     }
  369.     else
  370.     {
  371.         // delete the values for all threads
  372.         CThreadData* pData = m_list;
  373.         while (pData != NULL)
  374.         {
  375.             CThreadData* pDataNext = pData->pNext;
  376.             DeleteValues(pData, hInst);
  377.             pData = pDataNext;
  378.         }
  379.     }
  380.     LeaveCriticalSection(&m_sect);
  381. }
  382.  
  383. /////////////////////////////////////////////////////////////////////////////
  384. // CThreadLocalObject
  385.  
  386. CNoTrackObject* CThreadLocalObject::GetData(
  387.     CNoTrackObject* (AFXAPI* pfnCreateObject)())
  388. {
  389.     if (m_nSlot == 0)
  390.     {
  391.         if (_afxThreadData == NULL)
  392.         {
  393.             _afxThreadData = new(__afxThreadData) CThreadSlotData;
  394.             ASSERT(_afxThreadData != NULL);
  395.         }
  396.         m_nSlot = _afxThreadData->AllocSlot();
  397.         ASSERT(m_nSlot != 0);
  398.     }
  399.     CNoTrackObject* pValue =
  400.         (CNoTrackObject*)_afxThreadData->GetThreadValue(m_nSlot);
  401.     if (pValue == NULL)
  402.     {
  403.         // allocate zero-init object
  404.         pValue = (*pfnCreateObject)();
  405.  
  406.         // set tls data to newly created object
  407.         _afxThreadData->SetValue(m_nSlot, pValue);
  408.         ASSERT(_afxThreadData->GetThreadValue(m_nSlot) == pValue);
  409.     }
  410.     return pValue;
  411. }
  412.  
  413. CNoTrackObject* CThreadLocalObject::GetDataNA()
  414. {
  415.     if (m_nSlot == 0 || _afxThreadData == NULL)
  416.         return NULL;
  417.  
  418.     CNoTrackObject* pValue =
  419.         (CNoTrackObject*)_afxThreadData->GetThreadValue(m_nSlot);
  420.     return pValue;
  421. }
  422.  
  423. CThreadLocalObject::~CThreadLocalObject()
  424. {
  425.     if (m_nSlot != 0 && _afxThreadData != NULL)
  426.         _afxThreadData->FreeSlot(m_nSlot);
  427.     m_nSlot = 0;
  428. }
  429.  
  430. /////////////////////////////////////////////////////////////////////////////
  431. // CProcessLocalData
  432.  
  433. CNoTrackObject* CProcessLocalObject::GetData(
  434.     CNoTrackObject* (AFXAPI* pfnCreateObject)())
  435. {
  436.     if (m_pObject == NULL)
  437.     {
  438.         AfxLockGlobals(CRIT_PROCESSLOCAL);
  439.         TRY
  440.         {
  441.             if (m_pObject == NULL)
  442.                 m_pObject = (*pfnCreateObject)();
  443.         }
  444.         CATCH_ALL(e)
  445.         {
  446.             AfxUnlockGlobals(CRIT_PROCESSLOCAL);
  447.             THROW_LAST();
  448.         }
  449.         END_CATCH_ALL
  450.         AfxUnlockGlobals(CRIT_PROCESSLOCAL);
  451.     }
  452.     return m_pObject;
  453. }
  454.  
  455. CProcessLocalObject::~CProcessLocalObject()
  456. {
  457.     if (m_pObject != NULL)
  458.         delete m_pObject;
  459. }
  460.  
  461. /////////////////////////////////////////////////////////////////////////////
  462. // Init/Term for thread/process local data
  463.  
  464. void AFXAPI AfxInitLocalData(HINSTANCE hInst)
  465. {
  466.     if (_afxThreadData != NULL)
  467.         _afxThreadData->AssignInstance(hInst);
  468. }
  469.  
  470. void AFXAPI AfxTermLocalData(HINSTANCE hInst, BOOL bAll)
  471. {
  472.     if (_afxThreadData != NULL)
  473.         _afxThreadData->DeleteValues(hInst, bAll);
  474. }
  475.  
  476. // This reference count is needed to support Win32s, such that the
  477. // thread-local and process-local data is not destroyed prematurely.
  478. // It is basically a reference count of the number of processes that
  479. // have attached to the MFC DLL.
  480.  
  481. AFX_STATIC_DATA long _afxTlsRef = 0;
  482.  
  483. void AFXAPI AfxTlsAddRef()
  484. {
  485.     ++_afxTlsRef;
  486. }
  487.  
  488. void AFXAPI AfxTlsRelease()
  489. {
  490.     if (_afxTlsRef == 0 || --_afxTlsRef == 0)
  491.     {
  492.         if (_afxThreadData != NULL)
  493.         {
  494.             _afxThreadData->~CThreadSlotData();
  495.             _afxThreadData = NULL;
  496.         }
  497.     }
  498. }
  499.  
  500. /////////////////////////////////////////////////////////////////////////////
  501.