博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从_tiddata看CRT的线程不安全函数
阅读量:7000 次
发布时间:2019-06-27

本文共 10265 字,大约阅读时间需要 34 分钟。

 
//_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 
#include 
#include 
#include 
#include 
#include 
 
# endif /* _NTSUBSET_ */
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
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:
*       
*Exit:
*       returns on success
*       calls _amsg_exit on failure
*
*Uses:
*       
*
*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:
*       
*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) */
 

 

以下来自:,节选。

 

或许有人会说,我用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就是安全的。

转载于:https://www.cnblogs.com/qinfengxiaoyue/archive/2013/05/05/3061800.html

你可能感兴趣的文章
oracle控制台OEM无法启动
查看>>
haproxy负载均衡
查看>>
clink 让cmd像ubuntu gnome-terminal一样
查看>>
初识Java模板引擎Beetl之简单示例
查看>>
Oracle UNDO表空间的管理
查看>>
canal.deployer-1.1.0版本,当监听到数据库变动时,server端报异常,docker单核引起的问题...
查看>>
JAVA并发编程:干掉 Synchronized
查看>>
JAVA .class 文件防止反编译
查看>>
iOS-<UITabBarControllerDelegate> 代理不执行
查看>>
easyui实现datagrid列标题拖动
查看>>
CentOS 6.5系统安装配置LAMP(Apache+PHP5+MySQL)服务器环境
查看>>
在Websphere上修改项目的web.xml中的配置后不起作用
查看>>
JAVA 数据计算、取整、+1、四舍五入
查看>>
wshell修改了upload功能,増加显示图片功能
查看>>
ERP中标准成本的差异分析控制
查看>>
linux 中断的上半部和下半部
查看>>
单例模式的七种写法
查看>>
好用到吐血!APP设计利器Sketch
查看>>
Android TensorFlow环境搭建
查看>>
【细品架构1/100】架构之缘起
查看>>