zoukankan      html  css  js  c++  java
  • 从_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就是安全的。

  • 相关阅读:
    简爱 灵魂所在
    charles抓取http/https
    Class.forName()用法
    ArrayList源码剖析
    java中的多线程
    分布式负载均衡缓冲系统,如何快速定位到是那个服务器
    maven依赖jar包时版本冲突的解决
    简单工厂模式设计(java反射机制改进)
    Fiddler 抓包工具使用详解
    Fiddler 使用
  • 原文地址:https://www.cnblogs.com/qinfengxiaoyue/p/3061800.html
Copyright © 2011-2022 走看看