/*** *tidtable.c - Access thread data table * * Copyright (c) 1989-2001, Microsoft Corporation. All rights reserved. * *Purpose: * This module contains the following routines for multi-thread * data support: * * _mtinit = Initialize the mthread data * _getptd = get the pointer to the per-thread data structure for * the current thread * _freeptd = free up a per-thread data structure and its * subordinate structures * __threadid = return thread ID for the current thread * __threadhandle = return pseudo-handle for the current thread * *Revision History: * 05-04-90 JCR Translated from ASM to C for portable 32-bit OS/2 * 06-04-90 GJF Changed error message interface. * 07-02-90 GJF Changed __threadid() for DCR 1024/2012. * 08-08-90 GJF Removed 32 from API names. * 10-08-90 GJF New-style function declarators. * 10-09-90 GJF Thread ids are of type unsigned long! Also, fixed a * bug in __threadid(). * 10-22-90 GJF Another bug in __threadid(). * 12-04-90 SRW Changed to include instead of * 12-06-90 SRW Added _CRUISER_ and _WIN32 conditionals. * 05-31-91 GJF Win32 version [_WIN32_]. * 07-18-91 GJF Fixed many silly errors [_WIN32_]. * 09-29-91 GJF Conditionally added _getptd_lk/_getptd1_lk so that * DEBUG version of mlock doesn't infinitely recurse * the first time _THREADDATA_LOCK is asserted [_WIN32_]. * 01-30-92 GJF Must init. _pxcptacttab field to _XcptActTab. * 02-25-92 GJF Initialize _holdrand field to 1. * 02-13-93 GJF Revised to use TLS API. Also, purged Cruiser support. * 03-26-93 GJF Initialize ptd->_holdrand to 1L (see thread.c). * 04-06-93 SKS Replace _CRTAPI* with __cdecl * 04-13-93 SKS Add _mtterm to do multi-thread termination * Set freed __tlsindex to -1 again to prevent mis-use * 04-26-93 SKS _mtinit now returns 0 or 1, no longer calls _amsg_exit * 12-13-93 SKS Add _freeptd(), which frees up the per-thread data * maintained by the C run-time library. * 04-12-94 GJF Made definition of __tlsindex conditional on ndef * DLL_FOR_WIN32S. Also, replaced MTHREAD with _MT. * 01-10-95 CFW Debug CRT allocs. * 04-12-95 DAK Added NT kernel support for C++ exceptions * 04-18-95 SKS Add 5 MIPS per-thread variables. * 04-25-95 DAK More Kernel EH support * 05-02-95 SKS add _initptd() to do initialization of per-thread data * 05-24-95 CFW Add _defnewh. * 06-12-95 JWM _getptd() now preserves LastError. * 01-17-97 GJF _freeptd() must free the thread's copy of the * exception-action table. * 09-26-97 BWT Fix NTSUBSET * 02-03-98 GJF Changes for Win64: use uintptr_t type for anything with * a HANDLE value. * 04-27-98 GJF Added support for per-thread mbc information. * 07-28-98 JWM Initialize __pceh (new per-thread data for comerr support). * 09-03-98 GJF Added support for per-thread locale information. * 12-04-98 JWM Pulled all comerr support. * 12-08-98 GJF In _freeptd, fixed several errors in cleaning up the * threadlocinfo. * 12-18-98 GJF Fixed one more error in _freeptd. * 01-18-99 GJF Take care not to free up __ptlocinfo when a thread * exits. * 03-16-99 GJF threadlocinfo incorporates more reference counters * 04-24-99 PML Added __lconv_intl_refcount * 04-28-99 PML Wrap __declspec(allocate()) in _CRTALLOC macro. * 11-03-99 RDL Win64 _NTSUBSET_ warning fix. * 06-08-00 PML No need to keep per-thread mbcinfo in circular linked * list. Also, don't free mbcinfo if it's also the global * info (vs7#118174). * 02-20-01 PML vs7#172586 Avoid _RT_LOCK by preallocating all locks * that will be required, and returning failure back on * inability to allocate a lock. * 06-12-01 BWT ntbug: 414059 - cleanup from mtinit failure * *******************************************************************************/ #if defined(_MT) || defined(_NTSUBSET_) # if defined(_NTSUBSET_) #include #include #include #include #include #include # endif /* _NTSUBSET_ */ #include #include #include #include #include #include #include #include #include #include #include #ifdef _MT extern pthreadmbcinfo __ptmbcinfo; extern threadlocinfo __initiallocinfo; extern pthreadlocinfo __ptlocinfo; void __cdecl __freetlocinfo(pthreadlocinfo); #endif unsigned long __tlsindex = 0xffffffff; #if defined(_NTSUBSET_) #define BEGIN_NONPAGED_CODE code_seg(".text") #define END_NONPAGED_CODE code_seg("PAGE") static _ptiddata pTheKernelExceptionContextPool; static KSPIN_LOCK TheKernelExceptionContextPoolLock; /**** *_initKernelExceptionContextPool() - creates kernel-mode exception context pool * *Purpose: * (1) Initializes spin-lock used to serialize access to pool * *Entry: * *Exit: * * * *******************************************************************************/ #pragma BEGIN_NONPAGED_CODE void __cdecl _initKernelExceptionContextPool(void) { KeInitializeSpinLock( &TheKernelExceptionContextPoolLock ); } #pragma END_NONPAGED_CODE /**** * Tell the linker to insert a call to this function into the CRT runtime * startup. * * The segment .CRT$XCA is the first data segment bounding the array * of CRT startup functions. We therefore insert our function between * the first (.CRT$XCA) and the last (.CRT$XCZ). * *******************************************************************************/ #pragma data_seg(".CRT$XCAA") typedef void (__cdecl *PFStartup)(void); _CRTALLOC(".CRT$XCAA") PFStartup _KernelECPool = _initKernelExceptionContextPool; #pragma data_seg(".CRT$XCZ") #pragma data_seg() /**** *_emptyKernelExceptionContextPool() - empties kernel-mode exception context pool * *Purpose: * (1) Deletes all entries in the pool * *Entry: * *Exit: * * * *******************************************************************************/ #pragma BEGIN_NONPAGED_CODE void _emptyKernelExceptionContextPool() { while ( pTheKernelExceptionContextPool != 0 ) { _ptiddata ptd = pTheKernelExceptionContextPool; pTheKernelExceptionContextPool = pTheKernelExceptionContextPool->_next; ExFreePool( ptd ); } } #pragma END_NONPAGED_CODE /**** *_newKernelTid() - Creates, initializes and sets the current thread's * OfsChain to _tiddata; * *Purpose: * (1) Creates a new kernel-mode _tiddata * (2) Initializes fields in new _tiddata * (3) Sets current thread's OfsChain to new _tiddata * * NOTES: * (1) Can not fail; the memory allocation will cause a fatal system * trap if memory can not be allocated * *Entry: * *Exit: * _ptiddata * *******************************************************************************/ #pragma BEGIN_NONPAGED_CODE static _ptiddata _newKernelTid(void) { /* * Pop an entry from the pool of _ptiddata's */ _ptiddata ptd; PSINGLE_LIST_ENTRY entry; entry = ExInterlockedPopEntryList((PSINGLE_LIST_ENTRY) & pTheKernelExceptionContextPool, & TheKernelExceptionContextPoolLock ); /* * If none are available, create a new one. */ if ( 0 == entry ) { ptd = FsRtlAllocatePoolWithTag( NonPagedPoolMustSucceed, sizeof(struct _tiddata), ' dtp' ); } else { ptd = CONTAINING_RECORD( entry, struct _tiddata, _next ); } ptd->_tid = (unsigned long) (uintptr_t) PsGetCurrentThread()->Cid.UniqueThread; ptd->_terminate = 0; ptd->_unexpected = 0; ptd->_translator = SystemExceptionTranslator; ptd->_curexception = 0; ptd->_curcontext = 0; PsGetCurrentThread()->OfsChain = ptd; return ptd; } #pragma END_NONPAGED_CODE /**** *_freeKernelTid() - appends the _tiddata to the pool * *Entry: * _ptiddata *Exit: * *******************************************************************************/ #pragma BEGIN_NONPAGED_CODE static void _freeKernelTid(_ptiddata ptd) { /* * Return the entry to the kernel pool */ ExInterlockedPushEntryList( (PSINGLE_LIST_ENTRY) &pTheKernelExceptionContextPool, (PSINGLE_LIST_ENTRY) &ptd->_next, &TheKernelExceptionContextPoolLock ); } #pragma END_NONPAGED_CODE #endif /* _NTSUBSET_ */ /**** *_mtinit() - Init multi-thread data bases * *Purpose: * (1) Call _mtinitlocks to create/open all lock semaphores. * (2) Allocate a TLS index to hold pointers to per-thread data * structure. * * NOTES: * (1) Only to be called ONCE at startup * (2) Must be called BEFORE any mthread requests are made * *Entry: * *Exit: * returns FALSE on failure * *Uses: * * *Exceptions: * *******************************************************************************/ #if defined(_NTSUBSET_) #pragma BEGIN_NONPAGED_CODE #endif int __cdecl _mtinit ( void ) { _ptiddata ptd; #if defined(_NTSUBSET_) ptd = _newKernelTid(); #else /* * Initialize the mthread lock data base */ if ( !_mtinitlocks() ) { _mtterm(); return FALSE; /* fail to load DLL */ } /* * Allocate a TLS index to maintain pointers to per-thread data */ if ( (__tlsindex = TlsAlloc()) == 0xffffffff ) { _mtterm(); return FALSE; /* fail to load DLL */ } /* * Create a per-thread data structure for this (i.e., the startup) * thread. */ if ( ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) == NULL) || !TlsSetValue(__tlsindex, (LPVOID)ptd) ) { _mtterm(); return FALSE; /* fail to load DLL */ } /* * Initialize the per-thread data */ _initptd(ptd); ptd->_tid = GetCurrentThreadId(); ptd->_thandle = (uintptr_t)(-1); #endif /* _NTSUBSET_ */ return TRUE; } /**** *_mtterm() - Clean-up multi-thread data bases * *Purpose: * (1) Call _mtdeletelocks to free up all lock semaphores. * (2) Free up the TLS index used to hold pointers to * per-thread data structure. * * NOTES: * (1) Only to be called ONCE at termination * (2) Must be called AFTER all mthread requests are made * *Entry: * *Exit: * returns * *Uses: * *Exceptions: * *******************************************************************************/ void __cdecl _mtterm ( void ) { #if defined(_NTSUBSET_) _freeKernelTid( PsGetCurrentThread()->OfsChain ); PsGetCurrentThread()->OfsChain = 0; #else /* _NTSUBSET_ */ /* * Clean up the mthread lock data base */ _mtdeletelocks(); /* * Free up the TLS index * * (Set the variable __tlsindex back to the unused state (-1L).) */ if ( __tlsindex != 0xffffffff ) { TlsFree(__tlsindex); __tlsindex = 0xffffffff; } #endif /* _NTSUBSET_ */ } #if !defined(_NTSUBSET_) /*** *void _initptd(_ptiddata ptd) - initialize a per-thread data structure * *Purpose: * This routine handles all of the per-thread initialization * which is common to _beginthread, _beginthreadex, _mtinit * and _getptd. * *Entry: * pointer to a per-thread data block * *Exit: * the common fields in that block are initialized * *Exceptions: * *******************************************************************************/ void __cdecl _initptd ( _ptiddata ptd ) { ptd->_pxcptacttab = (void *)_XcptActTab; ptd->_holdrand = 1L; #ifdef ANSI_NEW_HANDLER ptd->_newh = _defnewh; #endif /* ANSI_NEW_HANDLER */ #ifdef _M_MRX000 /* * MIPS per-thread data */ ptd->_MipsPtdDelta = ptd->_MipsPtdEpsilon = -1L ; #endif } #endif /* _NTSUBSET_ */ /*** *_ptiddata _getptd(void) - get per-thread data structure for the current thread * *Purpose: * *Entry: * unsigned long tid * *Exit: * success = pointer to _tiddata structure for the thread * failure = fatal runtime exit * *Exceptions: * *******************************************************************************/ _ptiddata __cdecl _getptd ( void ) { _ptiddata ptd; DWORD TL_LastError; #if defined(_NTSUBSET_) ptd = PsGetCurrentThread()->OfsChain; if ( ptd == 0 ) { ptd = _newKernelTid(); } #else /* _NTSUBSET_ */ TL_LastError = GetLastError(); if ( (ptd = TlsGetValue(__tlsindex)) == NULL ) { /* * no per-thread data structure for this thread. try to create * one. */ if ( ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) != NULL) && TlsSetValue(__tlsindex, (LPVOID)ptd) ) { /* * Initialize of per-thread data */ _initptd(ptd); ptd->_tid = GetCurrentThreadId(); ptd->_thandle = (uintptr_t)(-1); } else _amsg_exit(_RT_THREAD); /* write message and die */ } SetLastError(TL_LastError); #endif /* _NTSUBSET_ */ return(ptd); } /*** *void _freeptd(_ptiddata) - free up a per-thread data structure * *Purpose: * Called from _endthread and from a DLL thread detach handler, * this routine frees up the per-thread buffer associated with a * thread that is going away. The tiddata structure itself is * freed, but not until its subordinate buffers are freed. * *Entry: * pointer to a per-thread data block (malloc-ed memory) * If NULL, the pointer for the current thread is fetched. * *Exit: * *Exceptions: * *******************************************************************************/ void __cdecl _freeptd ( _ptiddata ptd ) { #if defined(_NTSUBSET_) if ( 0 == ptd ) { ptd = PsGetCurrentThread()->OfsChain; } else { ASSERT( ptd == PsGetCurrentThread()->OfsChain ); } _freeKernelTid( ptd ); PsGetCurrentThread()->OfsChain = 0; #else /* _NTSUBSET_ */ pthreadmbcinfo ptmbci; pthreadlocinfo ptloci; /* * Do nothing unless per-thread data has been allocated for this module! */ if ( __tlsindex != 0xFFFFFFFF ) { /* * if parameter "ptd" is NULL, get the per-thread data pointer * Must NOT call _getptd because it will allocate one if none exists! */ if ( ! ptd ) ptd = TlsGetValue(__tlsindex ); /* * Free up the _tiddata structure & its malloc-ed buffers. */ if ( ptd ) { if(ptd->_errmsg) _free_crt((void *)ptd->_errmsg); if(ptd->_namebuf0) _free_crt((void *)ptd->_namebuf0); if(ptd->_namebuf1) _free_crt((void *)ptd->_namebuf1); if(ptd->_asctimebuf) _free_crt((void *)ptd->_asctimebuf); if(ptd->_gmtimebuf) _free_crt((void *)ptd->_gmtimebuf); if(ptd->_cvtbuf) _free_crt((void *)ptd->_cvtbuf); if (ptd->_pxcptacttab != _XcptActTab) _free_crt((void *)ptd->_pxcptacttab); _mlock(_MB_CP_LOCK); __try { if ( ((ptmbci = ptd->ptmbcinfo) != NULL) && (--(ptmbci->refcount) == 0) && (ptmbci != __ptmbcinfo) ) _free_crt(ptmbci); } __finally { _munlock(_MB_CP_LOCK); } _mlock(_SETLOCALE_LOCK); __try { if ( (ptloci = ptd->ptlocinfo) != NULL ) { (ptloci->refcount)--; if ( ptloci->lconv_intl_refcount != NULL ) (*(ptloci->lconv_intl_refcount))--; if ( ptloci->lconv_mon_refcount != NULL ) (*(ptloci->lconv_mon_refcount))--; if ( ptloci->lconv_num_refcount != NULL ) (*(ptloci->lconv_num_refcount))--; if ( ptloci->ctype1_refcount != NULL ) (*(ptloci->ctype1_refcount))--; (ptloci->lc_time_curr->refcount)--; if ( (ptloci != __ptlocinfo) && (ptloci != &__initiallocinfo) && (ptloci->refcount == 0) ) __freetlocinfo(ptloci); } } __finally { _munlock(_SETLOCALE_LOCK); } _free_crt((void *)ptd); } /* * Zero out the one pointer to the per-thread data block */ TlsSetValue(__tlsindex, (LPVOID)0); } #endif /* _NTSUBSET_ */ } #if defined(_NTSUBSET_) #pragma END_NONPAGED_CODE #endif /*** *__threadid() - Returns current thread ID *__threadhandle() - Returns "pseudo-handle" for current thread * *Purpose: * The two function are simply do-nothing wrappers for the corresponding * Win32 APIs (GetCurrentThreadId and GetCurrentThread, respectively). * *Entry: * void * *Exit: * thread ID value * *Exceptions: * *******************************************************************************/ _CRTIMP unsigned long __cdecl __threadid ( void ) { #if defined(_NTSUBSET_) return( (unsigned long) (uintptr_t) PsGetCurrentThread()->Cid.UniqueThread ); #else return( GetCurrentThreadId() ); #endif } _CRTIMP uintptr_t __cdecl __threadhandle( void ) { #if defined(_NTSUBSET_) return( (uintptr_t) PsGetCurrentThread()->Cid.UniqueThread ); #else return( (uintptr_t)GetCurrentThread() ); #endif } #endif