Analysis of AfxGetThreadState and _afx_thread_state

Source: Internet
Author: User
Tags assert bool

In the analysis of MFC's source code often see afxgetthreadstate this function, which is used to return some information of the current thread, its code is mainly in the Afxstat_.h afxtls_.h and their corresponding afxstate.cpp And afxstl.cpp, the definition of the Afxgettheradstate function is in afxstate.cpp and is defined as follows:

_afx_thread_state* AFXAPI afxgetthreadstate ()
{
	_afx_thread_state *pstate =_afxthreadstate.getdata ();  _afxthreadstate is a global template class object

	ensure (pState! = NULL); 
	return pState;
}

_afx_thread_state is MFC's class declaration on thread state in Afxstat_.h, and _afxthreadstate is a global template object located in Afxstate.cpp, first look at the declaration of this class:

Class Afx_novtable CNoTrackObject
{public
:
	void* PASCAL operator new (size_t nSize);
	void PASCAL operator Delete (void*);

#if defined (_DEBUG) &&!defined (_AFX_NO_DEBUG_CRT)
	void* PASCAL operator new (size_t nSize, LPCSTR, int); C12/>void PASCAL operator Delete (void* pobject, LPCSTR, int);
#endif
    Virtual ~cnotrackobject () {};
};


Class _afx_thread_state:public CNoTrackObject//virtual base class {public: _afx_thread_state ();

	Virtual ~_afx_thread_state ();            Override for M_pmodulestate in _afx_app_state afx_module_state* m_pmodulestate; Afx_module_state an important class that holds the information for the module in which the current thread executes the code.

	Afx_module_state* m_pprevmodulestate;    Memory Safety Pool for temp maps void* M_psafetypoolbuffer;

	Current buffer//thread Local exception context Afx_exception_context M_exceptioncontext;   CWND Create, Gray dialog hook, and other hook data cwnd* m_pwndinit;      The CWnd class uses this pointer to mark a pointer to the class corresponding to the window to be hook before creating each window, see Afxhookwindowcreate cwnd* m_palternatewndinit;
	Special case Commdlg hooking common dialog box hooks the tag pointer DWORD M_dwpropstyle;          DWORD M_dwpropexstyle;
	Property page Toggle Type save HWND m_hwndinit;          Hhook M_hhookoldcbtfilter;

	Save the handler for the CBT hook function, _afxcbtfilterhook the window procedure to AfxWndProc hhook M_hhookoldmsgfilter after the window is created;                   Message pump for Run MSG m_msgcur; Current Message CPoint m_ptcursorlast;                Last mouse position UINT m_nmsglast; Last mouse message//Three messages loop for CWinApp #ifdef _DEBUG int m_ndisablepumpcount;              Diagnostic trap to detect illegal re-entrancy #endif//Other CWnd modal data MSG m_lastsentmsg;         See Cwnd::windowproc HWND M_htrackingwindow;
	See Cwnd::trackpopupmenu HMENU M_htrackingmenu;    TCHAR M_sztempclassname[_afx_temp_class_name_size];    See AfxRegisterWndClass HWND M_hlockoutnotifywindow;                                         See CWnd::OnCommand BOOL M_binmsgfilter;          The above provides support for message routing for each MFC window class for each thread and//other framework modal data cview* M_proutingview;
	See Ccmdtarget::getroutingview cpushroutingview* M_ppushroutingview;     Cframewnd* m_pRoutingFrame;

	See Ccmdtarget::getroutingframe cpushroutingframe* M_ppushroutingframe;

	mfc/db thread-local data BOOL m_bwaitfordatasource;       OLE control thread-local data cwnd* M_pwndpark; "ParkiNg space "window long m_nctrlref;       Reference count on parking window BOOL m_bneedterm; TRUE if OleUninitialize needs to be called};

This class should be more like a struct, and does not define any functional interface functions for the class, primarily to provide global variable support for the message routing mechanism of MFC, where two afx_module_state pointers are important when writing MFC DLL files.

Looking back at _afxthreadstate, this global object, which is defined in Afxstate.cpp

Thread_local (_afx_thread_state, _afxthreadstate)

THREAD_LOCAL Macro for

#define Thread_local (class_name, ident_name) \
	afx_comdat cthreadlocal<class_name> ident_name;

Here _afxthreadstate is an instance object of the Cthreadlocal<_afx_thread_state> template class.

Template<class type>    //declaration in Afxstl_.h file
class Cthreadlocal:public Cthreadlocalobject
{
// Attributes public
:
	afx_inline type* GetData ()
	{
		type* pData = (type*) cthreadlocalobject::getdata ( &createobject);
		ensure (pData! = NULL);
		return pData;
	}
	Afx_inline type* Getdatana ()
	{
		type* pData = (type*) Cthreadlocalobject::getdatana ();
		return pData;
	}
	Afx_inline operator type* ()
	{ 
		return GetData (); 
	}
	Afx_inline type* operator-> ()
	{ 
		return GetData (); 
	}

Implementation public
:
	static cnotrackobject* AFXAPI CreateObject ()
		{return new TYPE;}
This template class is just a simple extension of its base class Cthreadlocalobject, and then look at the Cthreadlocalobject declaration,

Class Afx_novtable Cthreadlocalobject
{public
:
//Attributes
	cnotrackobject* GetData ( cnotrackobject* (afxapi* Pfncreateobject) ());
	cnotrackobject* Getdatana ();

implementation
	int m_nslot;  
	~cthreadlocalobject ();
};

In the call AfxGetThreadState ()
_afx_thread_state *pstate =_afxthreadstate.getdata ();    This cthreadlocal<_afx_thread_state> type of global object calls Cthreadlocalobject's GetData function, which
is implemented as follows:

cnotrackobject* Cthreadlocalobject::getdata (
	cnotrackobject* (afxapi* pfncreateobject) ())  
{
    ENSURE ( Pfncreateobject);

	if (M_nslot = = 0)
	{
		if (_afxthreaddata = = NULL)     //Global pointer variable pointer to an object of type Cthreadslotdata the first time it is called, the object is created in the heap to assign the pointer

		{
			_afxthreaddata = new (__afxthreaddata) Cthreadslotdata;
			ensure (_afxthreaddata! = NULL);
		}
		M_nslot = _afxthreaddata->allocslot ();  Return the requested slot number,
		ensure (M_nslot! = 0);
	}
	cnotrackobject* PValue = static_cast<cnotrackobject*> (_afxthreaddata->getthreadvalue (M_nSlot));
	The IF (PValue = = NULL)  //cthreadslotdata object holds the data in a thread-local storage mechanism, the same slot number corresponds to a different data pointer in each thread

	{
		//Allocate Zero-init Object
		PValue = (*pfncreateobject) ();  If the M_nsolt slot number gets a data pointer that is not valid, a Afx_thread_state object is created in the heap, and the Pfncreateobject function pointer corresponds to the
NEW afx_thread_state
		Set TLS data to newly created object
		_afxthreaddata->setvalue (M_nslot, pValue);  The created Afx_thread_state object is stored locally with the thread and saved.
		ASSERT (_afxthreaddata->getthreadvalue (m_nslot) = = PValue);
	}
	return pValue;



_afxthreaddata is a global object pointer, __afxthreaddata (note that an underscore is more) is a global array,

_afxthreaddata = new (__afxthreaddata) Cthreadslotdata; A Cthreadslotdata object was created in global static space

They are defined in the Afxtls.cpp:

Global _afxthreaddata used to allocate thread local indexes
BYTE __afxthreaddata[sizeof (cthreadslotdata)];
cthreadslotdata* _afxthreaddata;


Cthreadslotdata uses the mechanism of thread-local storage to hold the Afx_thread_state object for each thread to see its declaration and implementation

struct Cthreaddata:public cnotrackobject
{
	cthreaddata* pnext;//required to be member of csimplelist
	int n Count;         Current size of PData
	lpvoid* pData;      Actual thread local data (indexed by Nslot)
};

struct Cslotdata
{
	DWORD dwFlags;      Slot flags (Allocated/not allocated)
	hinstance hInst;    module which owns this slot
};


Class Cthreadslotdata {public:cthreadslotdata ();
	Operations int Allocslot ();	
	void Freeslot (int nslot);
	void SetValue (int nslot, void* pValue);
	Delete all values in Process/thread void Deletevalues (hinstance hInst, BOOL ball = FALSE);

Assign instance handle to just constructed slots void Assigninstance (HInstance hInst);   Implementation DWORD M_tlsindex;       Used to access system thread-local storage thread local storage index number int m_nalloc;       Number of slots allocated (in uints) int m_nrover;         (optimization) For quick finding of free slots int M_nmax; Size of slot table below (in bits) cslotdata* m_pslotdata;  State of each slot (allocated or not) ctypedsimplelist<cthreaddata*> m_list;

	List of cthreaddata structures critical_section M_sect; void* getthreadvalue (int nslot);
	Special version for Threads only!
	void* PASCAL operator New (size_t, void* p) {return p;} void Deletevalues (cthreaddata* pData, hinstance hInst);
 ~cthreadslotdata ();

int Cthreadslotdata::allocslot ()
{
	entercriticalsection (&m_sect);
	int nalloc = M_nalloc;
	int nslot = M_nrover;
	if (nslot >= Nalloc | | (M_pslotdata[nslot].dwflags & slot_used)) If there is not enough memory allocated or allocated to M_pslotdata

	{
		//search for first free slot, starting at beginning for
		(nslot = 1;
			Nslot < Nalloc && (M_pslotdata[nslot].dwflags & slot_used); nslot++)
			;

		If none found, need to allocate more space
		if (Nslot >= nalloc)
		{
			//realloc memory for the bit array And the slot memory
			int nnewalloc = m_nalloc+32;  Distribution granularity of
			hglobal Hslotdata;
			if (M_pslotdata = = NULL)
			{
				hslotdata = GlobalAlloc (gmem_moveable, static_cast<uint> (:: ATL:: Atlmultiplythrow (static_cast<uint> (nnewalloc),static_cast<uint> (sizeof (Cslotdata))));
                                                                                     Nnewalloc * sizeof (CSLOTDATA)
				} else {hslotdata = Globalhandle (M_pslotdata);
				GlobalUnlock (Hslotdata); Hslotdata = GlobalRealloc (Hslotdata, static_cast<uint> (:: Atl::atlmultiplythrow (Static_cast<UINT> ( Nnewalloc),static_cast<uint> (sizeof (Cslotdata))), gmem_moveable|
			Gmem_share);
				} if (hslotdata = = null) {if (m_pslotdata! = null) GlobalLock (Globalhandle (m_pslotdata));
				LeaveCriticalSection (&m_sect);
			AfxThrowMemoryException ();

			} cslotdata* Pslotdata = (cslotdata*) globallock (hslotdata);
			Always zero initialize after success memset (Pslotdata+m_nalloc, 0, (Nnewalloc-m_nalloc) *sizeof (cslotdata));
			M_nalloc = Nnewalloc;
		M_pslotdata = Pslotdata; }} ASSERT (Nslot! = 0); First slot (0) is reserved//adjust M_nmax to largest slot ever allocated if (Nslot >= m_nmax) M_nmax = nslot+

	1; ASSERT (! ( M_pslotdaTa[nslot].dwflags & slot_used));               M_pslotdata[nslot].dwflags |= slot_used;

	Set the flag for Cslotdata with slot number Nsolt to used//update m_nrover (likely place to find a free slots is next one) M_nrover = nslot+1;
	LeaveCriticalSection (&m_sect);   return nslot; Slot can be used for Freeslot, GetValue, SetValue}
void cthreadslotdata::setvalue (int nslot, void* pValue) {entercriticalsection (&m_sect);
	The critical section object prevents multi-threaded re-entry ASSERT (Nslot! = 0 && Nslot < M_nmax);
	ASSERT (M_pslotdata! = NULL);
	ASSERT (M_pslotdata[nslot].dwflags & slot_used);
	if (nslot <= 0 | | nslot >= m_nmax)//Check for retail builds.
		{leavecriticalsection (&m_sect);
	Return   } cthreaddata* PData = (cthreaddata*) tlsgetvalue (M_tlsindex); Use thread-local storage to ensure that each thread has its own cthreaddate structure in the heap 
	if (PData = = NULL | | Nslot >= pdata->ncount && pValue! = NULL) {//if pData is null and this thread ha
			s not been visited yet if (PData = = NULL) {TRY {pData = new Cthreaddata;
				} catch_all (e) {leavecriticalsection (&m_sect);
			Throw_last ();
			} End_catch_all pdata->ncount = 0;
			Pdata->pdata = NULL;

			Debug_only (pdata->pnext = NULL); M_list.
		AddHead (PData);
		}//grow to now current size void** ppvtemp; if (Pdata->pdata = = NULL)//allocates memory for an array of pdadata pointers in Cthreaddata, increases the allocation of memory by increasing the slot number ppvtemp = (void**) LocalAlloc (lmem_fixed, Static_cast<uint> (:: Atl::atlmultiplythrow (static_cast<uint> (M_nmax),static_cast<uint> (sizeof
		(LPVOID)))); else Ppvtemp = (void**) localrealloc (Pdata->pdata, static_cast<uint> (:: Atl::atlmultiplythrow (static_cast
		<UINT> (M_nmax),static_cast<uint> (sizeof (LPVOID))), lmem_moveable);
if (ppvtemp = = NULL) {leavecriticalsection (&m_sect);			AfxThrowMemoryException ();

		} pdata->pdata = Ppvtemp; Initialize the newly allocated part memset (pdata->pdata + pdata->ncount, 0, (m_nmax-pdata->ncount) * si
		Zeof (LPVOID));
		Pdata->ncount = M_nmax;
	TlsSetValue (M_tlsindex, pData);
	} ASSERT (Pdata->pdata! = NULL && Nslot < Pdata->ncount);                        if (pdata->pdata! = NULL && Nslot < pdata->ncount) Pdata->pdata[nslot] = PValue; Depending on the slot number, set the Pdadata (LPVOID type in the Cthreaddata, corresponding to the object that belongs to each thread that needs to be saved.
pointer) The object in the pointer array to which the pointer is pointing
	LeaveCriticalSection (&m_sect);
}

inline void* cthreadslotdata::getthreadvalue (int nslot)
{
	entercriticalsection (&m_sect);
	ASSERT (Nslot! = 0 && Nslot < M_nmax);
	ASSERT (M_pslotdata! = NULL);
	ASSERT (M_pslotdata[nslot].dwflags & slot_used);
	ASSERT (M_tlsindex! = (DWORD)-1);
	if (nslot <= 0 | | nslot >= m_nmax)//Check for retail builds. Slot number less than or equal to 0 or greater than existing slot number  meaningless
	{
		leavecriticalsection (&m_sect);
		return NULL;
	}

	cthreaddata* PData = (cthreaddata*) tlsgetvalue (M_tlsindex); Gets the Cthreaddata object pointer that belongs to this thread
	if (pData = = NULL | | nslot >= pdata->ncount)
	{
		leavecriticalsection ( &m_sect);
		return NULL;
	}
	void* pRetVal = pdata->pdata[nslot]; Returns the corresponding object pointer
	leavecriticalsection (&m_sect) in the pdata pointer array according to the slot number;
	return pretval;
}



The above code can see the cthreadlocal<_afx_thread_state> type of global object _afxthreadstate, in fact, encapsulates the cthreadslotdata type of the global reference _ Afxthreadslotdate

, all work is done by _afxthreadslotdate (a pointer to the Cthreadslotdata class object). This class uses local thread storage to implement each thread's own _afx_thread_state object.

This set of mechanisms seems to be very complex, but it is quite useful, in the implementation of MFC will find more than n calls.



It is convenient to use this mechanism when we are writing multi-threaded program, and want to save an object belonging to this thread for each thread. For example, we want each thread to use a separate Diystring object to hold some character information that belongs to that thread. We can define a global cthreadlocal< diystring> object g_ Threadstring then defines a global adaptation function for it, note that the thread object that is saved with this mechanism must inherit from CNoTrackObject and has a default constructor


#include "stdafx.h"
#include <iostream>
#include <string>
#include <afxwin.h>

Class Diystring:public Cnotrackobject,public std::string
{public
:
	diystring ()
	{

	}
};

Cthreadlocal<diystring>  g_threadstring;

Diystring * g_getthreadstring ()
{
	diystring * ret =0;
	ret = G_threadstring.getdata ();
	return ret;
}

DWORD WINAPI ThreadProc (lpvoid lpparameter)
{
	diystring * pStr = g_getthreadstring (); 
	diystring * pStr = g_threadstring;  This is also possible because the Cthreadlocal template class overloads the type conversion operator
	Pstr->append (Static_cast<char *> (Lpparameter));
	std::cout<<*pstr<<std::endl;
	return 0;
}

int _tmain (int argc, _tchar* argv[])
{for
	(int i=0; i<10; ++i)
	{
		char date[256] = {0};
		sprintf_s (Date, "This is%d thread been created", I);
		:: CreateThread (null,null,threadproc,date,null,null);
		Sleep (+);  The delay ensures that the thread that the data is created in date is saved to the threadlocal
	}
	System ("pause");
	return 0;
}


The operation results are as follows



This mechanism is very useful when writing multi-threaded program, but there are some drawbacks,

The first must contain AFXWIN.h settings using the MFC library, the program that uses the SDK wants to pick it up and use it to modify some of the dependencies in this four file

The first interface that frees resources when a thread exits, to free objects that have already been requested in the heap



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.