从_tiddata看CRT的线程不安全函数

//_tiddata的定义. CRT SRC\mtdll.h

struct _tiddata {

        unsigned long   _tid;       /* thread ID */

        unsigned long   _thandle;   /* thread handle */

        int     _terrno;            /* errno value */

        unsigned long   _tdoserrno; /* _doserrno value */

        unsigned int    _fpds;      /* Floating Point data segment */

        unsigned long   _holdrand;  /* rand() seed value */

        char *      _token;         /* ptr to strtok() token */

#ifdef _WIN32

        wchar_t *   _wtoken;        /* ptr to wcstok() token */

#endif  /* _WIN32 */

        unsigned char * _mtoken;    /* ptr to _mbstok() token */

        /* following pointers get malloc'd at runtime */

        char *      _errmsg;        /* ptr to strerror()/_strerror() buff */

        char *      _namebuf0;      /* ptr to tmpnam() buffer */

#ifdef _WIN32

        wchar_t *   _wnamebuf0;     /* ptr to _wtmpnam() buffer */

#endif  /* _WIN32 */

        char *      _namebuf1;      /* ptr to tmpfile() buffer */

#ifdef _WIN32

        wchar_t *   _wnamebuf1;     /* ptr to _wtmpfile() buffer */

#endif  /* _WIN32 */

        char *      _asctimebuf;    /* ptr to asctime() buffer */

#ifdef _WIN32

        wchar_t *   _wasctimebuf;   /* ptr to _wasctime() buffer */

#endif  /* _WIN32 */

        void *      _gmtimebuf;     /* ptr to gmtime() structure */

        char *      _cvtbuf;        /* ptr to ecvt()/fcvt buffer */

        /* following fields are needed by _beginthread code */

        void *      _initaddr;      /* initial user thread address */

        void *      _initarg;       /* initial user thread argument */

        /* following three fields are needed to support signal handling and

         * runtime errors */

        void *      _pxcptacttab;   /* ptr to exception-action table */

        void *      _tpxcptinfoptrs; /* ptr to exception info pointers */

        int         _tfpecode;      /* float point exception code */

        /* following field is needed by NLG routines */

        unsigned long   _NLG_dwCode;

        /*

         * Per-Thread data needed by C++ Exception Handling

         */

        void *      _terminate;     /* terminate() routine */

        void *      _unexpected;    /* unexpected() routine */

        void *      _translator;    /* S.E. translator */

        void *      _curexception;  /* current exception */

        void *      _curcontext;    /* current exception context */

#if defined (_M_MRX000)

        void *      _pFrameInfoChain;

        void *      _pUnwindContext;

        void *      _pExitContext;

        int         _MipsPtdDelta;

        int         _MipsPtdEpsilon;

#elif defined (_M_PPC)

        void *      _pExitContext;

        void *      _pUnwindContext;

        void *      _pFrameInfoChain;

        int         _FrameInfo[6];

#endif  /* defined (_M_PPC) */

        };

typedef struct _tiddata * _ptiddata;

 

//_getptd _freeptd 的相关实现. CRT SRC\tidtable.c

/***

*tidtable.c - Access thread data table

*

*       Copyright (c) 1989-1997, 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

*

*******************************************************************************/

#if defined (_MT)

# if defined(_NTSUBSET_)

#include <nt.h>

#include <ntrtl.h>

#include <nturtl.h>

#include <ntstatus.h>

#include <ntos.h>

#include <fsrtl.h>

# endif /* _NTSUBSET_ */

#include <cruntime.h>

#include <oscalls.h>

#include <internal.h>

#include <mtdll.h>

#include <memory.h>

#include <msdos.h>

#include <rterr.h>

#include <stdlib.h>

#include <stddef.h>

#include <dbgint.h>

unsigned long __tlsindex = 0xffffffff;

/****

*_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:

*       <NONE>

*Exit:

*       returns on success

*       calls _amsg_exit on failure

*

*Uses:

*       <any registers may be modified at init time>

*

*Exceptions:

*

*******************************************************************************/

int __cdecl _mtinit (

        void

        )

{

        _ptiddata ptd;

        /*

         * Initialize the mthread lock data base

         */

        _mtinitlocks();

        /*

         * Allocate a TLS index to maintain pointers to per-thread data

         */

        if ( (__tlsindex = TlsAlloc()) == 0xffffffff )

            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) )

            return FALSE;       /* fail to load DLL */

        /*

         * Initialize the per-thread data

         */

        _initptd(ptd);

        ptd->_tid = GetCurrentThreadId();

        ptd->_thandle = (unsigned long)(-1L);

        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:

*       <NONE>

*Exit:

*       returns

*

*Uses:

*

*Exceptions:

*

*******************************************************************************/

void __cdecl _mtterm (

        void

        )

{

        /*

         * 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;

        }

}

/***

*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 _M_MRX000

        /*

         * MIPS per-thread data

         */

        ptd->_MipsPtdDelta =

        ptd->_MipsPtdEpsilon = -1L ;

#endif  /* _M_MRX000 */

}

/***

*_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;

        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 = (unsigned long)(-1L);

            }

            else

                _amsg_exit(_RT_THREAD); /* write message and die */

            }

        SetLastError(TL_LastError);

        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

        )

{

        /*

         * 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);

                _free_crt((void *)ptd);

            }

            /*

             * Zero out the one pointer to the per-thread data block

             */

            TlsSetValue(__tlsindex, (LPVOID)0);

        }

}

/***

*__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

        )

{

        return( GetCurrentThreadId() );

}

_CRTIMP unsigned long __cdecl __threadhandle(

        void

        )

{

        return( (unsigned long)GetCurrentThread() );

}

#endif  /* defined (_MT) */

 

以下来自:http://www.cnblogs.com/chinasasu/archive/2010/04/11/1709767.html,节选。

 

或许有人会说,我用CreateThread创建线程以后,也调用了C运行库函数,并且也使用ExitThread退出了,可是我的程序运行得好好的,既

没有因为CRT没有初始化而崩溃,也没有因为忘记调用_endthread而发生内存泄漏,这是为什么呢,让我们继续我们的CRT之旅。


假设我用CreateThread创建了一个线程,我调用strtok函数来进行字符串处理,这个函数肯定是需要某些额外的运行时支持的。strtok的源代码在strtok.c中。从代码可见,在多线程情况下,strtok的第一句有效代码就是_ptiddata ptd = _getptd(),它通过这个来获得当前的ptd。可是我们并没有通过_beginthread来创建ptd,那么一定是_getptd捣鬼了。打开tidtable.c,可以看到_getptd的实现,果然,它先尝试获得当前的ptd,如果不能,就重新创建一个,因此,后续的CRT调用就安全了。可是这块ptd最终又是谁释放的呢?打开dllcrt0.c,可以看到一个DllMain函数。在VC中,CRT既可以作为一个动态链接库和主程序链接,也可以作为一个静态库和主程序链接,这个在Project Setting->Code Generations里面可以选。当CRT作为DLL链接到主程序时,DllMain就是CRT DLL的入口。Windows的DllMain可以由四种原因调用:Process Attach/Process Detach/Thread Attach/Thread Detach,最后一个,也就是当线程函数退出后但是线程还没有销毁前,会在这个线程的上下文中用Thread Detach调用DllMain,这里,CRT做了一个_freeptd(NULL),也就是说,如果有ptd,就free掉。所以说,恰巧没有发生内存泄漏是因为你用的是动态链接的CRT。


于是我们得出了一个更精确的结论,如果我没有使用那些会使用_getptd的CRT函数,使用CreateThread就是安全的。

你可能感兴趣的:(Data)