//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995. // // File: tls.cxx // // Contents: Thread Local Storage initialization and cleanup. // // Classes: // // Functions: // // History: 12-02-95 JohannP (Johann Posch) Created // //---------------------------------------------------------------------------- #include #ifndef unix #include "..\trans\transact.hxx" #include "..\download\cdl.h" #else #include "../trans/transact.hxx" #include "../download/cdl.h" #endif /* unix */ #include PerfDbgExtern(tagUrlDll) DbgTag(tagUrlDllErr, "Urlmon", "Log CBinding Errors", DEB_BINDING|DEB_ERROR); // Thread Local Storage index. DWORD gTlsIndex; HINSTANCE g_hInst = 0; HANDLE g_hHeap = 0; // used for tls data // Heap Handle extern HANDLE g_hHeap; #define HEAP_SERIALIZE 0 BOOL UnregisterUrlMkWndClass(); HRESULT DeleteOInetSession(DWORD dwReserved); extern URLMON_TS* g_pHeadURLMONTSList; HRESULT AddTSToList(URLMON_TS* pts) { CLock lck(g_mxsMedia); pts->_pNext = g_pHeadURLMONTSList; g_pHeadURLMONTSList = pts; return NOERROR; } HRESULT RemoveTSFromList(DWORD tid) { // this can only be called from ThreadDetach time CLock lck(g_mxsMedia); URLMON_TS* pts = NULL; URLMON_TS* ptsPrev = NULL; pts = g_pHeadURLMONTSList; while( pts ) { if( pts->_dwTID == tid ) { if( ptsPrev == NULL ) { // this is the head of the list g_pHeadURLMONTSList = pts->_pNext; } else { ptsPrev->_pNext = pts->_pNext; } // destroy the window // can only be called from current thread DestroyWindow(pts->_hwndNotify); // delete pts delete pts; break; } // advance ptsPrev = pts; pts = pts->_pNext; } return NOERROR; } URLMON_TS* GetTS(DWORD tid) { CLock lck(g_mxsMedia); URLMON_TS* pts = NULL; pts = g_pHeadURLMONTSList; while( pts ) { if( pts->_dwTID == tid ) { break; } // advance pts = pts->_pNext; } return pts; } HRESULT CleanupTSOnProcessDetach() { CLock lck(g_mxsMedia); URLMON_TS* pts = NULL; URLMON_TS* ptsToFree = NULL; pts = g_pHeadURLMONTSList; while( pts ) { if( pts->_dwTID == GetCurrentThreadId() ) { // destroy the window (the owner thread can do so) DestroyWindow(pts->_hwndNotify); DbgLog2( tagUrlDllErr, NULL, ">>> tid: %lx -> DestroyWindow :%p", pts->_dwTID, pts->_hwndNotify ); } else { // we are on a thread different from the window owner // so we can only Post message // set wndproc to user32's SetWindowLongPtr( pts->_hwndNotify, GWLP_WNDPROC, (LONG_PTR)DefWindowProc); // post message PostMessage(pts->_hwndNotify, WM_CLOSE, 0, 0); DbgLog2( tagUrlDllErr, NULL, ">>> tid: %lx -> PostMessage WM_CLOSE :%p", pts->_dwTID, pts->_hwndNotify ); } // save this pts since we are to free it ptsToFree = pts; // walk down the list pts = pts->_pNext; // free the pts if( ptsToFree ) { delete ptsToFree; ptsToFree = NULL; } } // mark list empty g_pHeadURLMONTSList = NULL; return NOERROR; } //+------------------------------------------------------------------------- // // Function: TLSAllocData // // Synopsis: Allocates the thread local storage block // // Returns: S_OK - allocated the data // E_OUTOFMEMORY - could not allocate the data // //-------------------------------------------------------------------------- HRESULT CUrlMkTls::TLSAllocData(void) { Win4Assert(TlsGetValue(gTlsIndex) == 0); Win4Assert(g_hHeap != NULL); _pData = (SUrlMkTlsData *) HeapAlloc(g_hHeap, HEAP_SERIALIZE, sizeof(SUrlMkTlsData)); if (_pData) { // This avoids having to set most fields to NULL, 0, etc and // is needed cause on debug builds memory is not guaranteed to // be zeroed. memset(_pData, 0, sizeof(SUrlMkTlsData)); // fill in the non-zero values _pData->dwFlags = URLMKTLS_LOCALTID; #ifdef ENABLE_DEBUG _pData->ThreadId = GetCurrentThreadId(); #endif // ENABLE_DEBUG // store the data ptr in TLS if (TlsSetValue(gTlsIndex, _pData)) { return S_OK; } // error, cleanup and fallthru to error exit HeapFree(g_hHeap, HEAP_SERIALIZE, _pData); _pData = NULL; } UrlMkDebugOut((DEB_TRACE, "TLSAllocData failed.\n")); return E_OUTOFMEMORY; } //+--------------------------------------------------------------------------- // // Function: DoThreadCleanup // // Synopsis: Called to perform cleanup on all this threads data // structures, and to call CoUninitialize() if needed. // // Could be called by DLL_THREAD_DETACH or DLL_PROCESS_DETACH // // //---------------------------------------------------------------------------- extern CMutexSem g_mxsTransMgr; void DoThreadCleanup(BOOL bInThreadDetach) { SUrlMkTlsData *pTls = (SUrlMkTlsData *) TlsGetValue(gTlsIndex); if (pTls != NULL) { // Because of the DLL unload rules in NT we need to be careful // what we do in clean up. We notify the routines with special // behavior here. pTls->dwFlags |= URLMKTLS_INTHREADDETACH; if (pTls->pCTransMgr != NULL) { // If the Release() returns non-zero, // AND we're not really in ThreadDetach, then we have other references on // the Transaction Manager. Put back our reference and leave. // if (bInThreadDetach == FALSE) { CLock lck(g_mxsTransMgr); if (pTls->pCTransMgr->Release()) { pTls->pCTransMgr->AddRef(); pTls->dwFlags &= ~URLMKTLS_INTHREADDETACH; goto Exit; } } else { pTls->pCTransMgr->Release(); } } if (pTls->pCodeDownloadList != NULL) { delete pTls->pCodeDownloadList; } if (pTls->pRejectedFeaturesList != NULL) { LISTPOSITION curpos; LPCWSTR pwszRejectedFeature = NULL; int iNumRejected; int i; iNumRejected = pTls->pRejectedFeaturesList->GetCount(); curpos = pTls->pRejectedFeaturesList->GetHeadPosition(); // walk thru all the rejected features in the thread and delete for (i=0; i < iNumRejected; i++) { pwszRejectedFeature = pTls->pRejectedFeaturesList->GetNext(curpos); delete (LPWSTR)pwszRejectedFeature; } delete pTls->pRejectedFeaturesList; } if (pTls->pSetupCookie != NULL) { delete pTls->pSetupCookie; } if (pTls->pTrustCookie != NULL) { delete pTls->pTrustCookie; } if (pTls->pCDLPacketMgr != NULL) { delete pTls->pCDLPacketMgr; } #ifdef PER_THREAD if (pTls->pCMediaHolder != NULL) { delete pTls->pCMediaHolder; } #endif //PER_THREAD // reset the index so we dont find this data again. TlsSetValue(gTlsIndex, NULL); // cleanup hwnd (not on TLS, but on urlmon's global table) DWORD tid = GetCurrentThreadId(); if( GetTS(tid)) { RemoveTSFromList(tid); } if (pTls->hwndUrlMkNotify != NULL) { DbgLog1(tagUrlDllErr, NULL, "ASSERT!!! tld: %lx ->hwnd !NULL", tid); } /******************************************************************* if (pTls->hwndUrlMkNotify != NULL) { HWND h = pTls->hwndUrlMkNotify; DestroyWindow(pTls->hwndUrlMkNotify); } ********************************************************************/ HeapFree(g_hHeap, HEAP_SERIALIZE, pTls); } // else // there is no TLS for this thread, so there can't be anything // to cleanup. Exit:; } //+------------------------------------------------------------------------- // // Function: TlsDllMain // // Synopsis: Dll entry point // // Arguments: [hIntance] -- a handle to the dll instance // [dwReason] -- the reason LibMain was called // [lpvReserved] - NULL - called due to FreeLibrary // - non-NULL - called due to process exit // // Returns: TRUE on success, FALSE otherwise // // Notes: other one time initialization occurs in ctors for // global objects // // WARNING: if we are called because of FreeLibrary, then we should do as // much cleanup as we can. If we are called because of process // termination, we should not do any cleanup, as other threads in // this process will have already been killed, potentially while // holding locks around resources. // // //-------------------------------------------------------------------------- STDAPI_(BOOL) TlsDllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved ) { BOOL fResult = FALSE; #if DBG==1 || defined(PERFTAGS) if (dwReason == DLL_THREAD_ATTACH || dwReason == DLL_THREAD_DETACH) PerfDbgLog1(tagUrlDll, NULL, "+TlsDllMain %s", dwReason == DLL_THREAD_ATTACH ? "DLL_THREAD_ATTACH" : "DLL_THREAD_DETACH"); #endif switch (dwReason) { case DLL_THREAD_ATTACH: // new thread is starting { HRESULT hr; CUrlMkTls tls(hr); if (FAILED(hr)) { goto ret; } } break; case DLL_THREAD_DETACH: // Thread is exiting, clean up resources associated with threads. DoThreadCleanup(TRUE); break; case DLL_PROCESS_ATTACH: // Initial setup. Get a thread local storage index for use by OLE g_hInst = hInstance; if ((g_hHeap = GetProcessHeap()) == 0) { // can continue E_OUTOFMEMORY; UrlMkAssert("Call GetProcessHeap failed."); goto ret; } gTlsIndex = TlsAlloc(); if (gTlsIndex == 0xffffffff) { UrlMkAssert("Could not get TLS Index."); goto ret; } { HRESULT hr; CUrlMkTls tls(hr); if (FAILED(hr)) { goto ret; } } break; case DLL_PROCESS_DETACH: UrlMkDebugOut((DEB_DLL,"DLL_PROCESS_DETACH:\n")); //if (NULL == lpvReserved) { // exiting because of FreeLibrary, so try to cleanup // DLL_PROCESS_DETACH is called when we unload. The thread that is // currently calling has not done thread specific cleanup yet. // DoThreadCleanup(TRUE); UnregisterUrlMkWndClass(); if (g_pCMHolder != NULL) { UrlMkDebugOut((DEB_DLL | DEB_ITRACE,">>> DoThreadCleanup delete process pCMediaHolder:%p \n", g_pCMHolder)); delete g_pCMHolder; g_pCMHolder = 0; } DeleteOInetSession(0); if (g_hSession) { // BUGBUG: do not close the session handle - check with RFirth InternetCloseHandle(g_hSession); g_hSession = NULL; } TlsFree(gTlsIndex); } UrlMkDebugOut((DEB_DLL,"DLL_PROCESS_DETACH: done\n")); break; } fResult = TRUE; ret: #if DBG==1 || defined(PERFTAGS) if (dwReason == DLL_THREAD_ATTACH || dwReason == DLL_THREAD_DETACH) PerfDbgLog(tagUrlDll, NULL, "-TlsDllMain"); #endif return fResult; }