mfc controls
DESCRIPTION
creating mfc controlsTRANSCRIPT
1
More on threads
Konstantin Bukin CSE791 – Advanced Windows
ProgrammingSummer 2001
2
MFC’s thread implementation MFC Internals by G. Shepherd, S. Wingo
Executing a class member in its own thread (C/C++ Users Journal, Vol.17, No. 12, p. 57)
Singleton Creation the thread-safe way (C/C++ Users Journal, Vol.17, No. 10, p. 43)
Improving performance with thread-private heaps (C/C++ Users Journal, Vol.17, No. 9, p. 50)
3
Thread’s thread creation
CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
int nPriority, UINT nStackSize,
DWORD dwCreateFlags, LPSECURITY_ATTRIBUTES lpSecurityAttrs);
CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass,
int nPriority, UINT nStackSize,
DWORD dwCreateFlags, LPSECURITY_ATTRIBUTES lpSecurityAttrs);
4
AfxBeginThread()
CWinThread* pThread = new CWinThread(pfnThreadProc, pParam);
if (!pThread->CreateThread( dwCreateFlags | CREATE_SUSPENDED, nStackSize, lpSecurityAttrs))
{
pThread->Delete();
return NULL;
}
VERIFY(pThread->SetThreadPriority(nPriority));
if (!(dwCreateFlags & CREATE_SUSPENDED))
VERIFY(pThread->ResumeThread() != (DWORD)-1);
return pThread;
It’s all about CWinThread and it’s CreateThread()
5
CWinThread
class CWinThread : public CCmdTarget {public:
CWinThread();CWinThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam);
BOOL CreateThread(DWORD dwCreateFlags = 0, UINT nStackSize = 0,LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
virtual BOOL InitInstance();virtual int Run();virtual BOOL PreTranslateMessage(MSG* pMsg);virtual BOOL PumpMessage(); // low level message pumpvirtual BOOL OnIdle(LONG lCount); //return TRUE if more idle processingvirtual BOOL IsIdleMessage(MSG* pMsg); //checks for special messages
private:LPVOID m_pThreadParams; //generic parameters passed to starting functionAFX_THREADPROC m_pfnThreadProc;HANDLE m_hThread; // this thread's HANDLE
} Back to AfxBeginThread()
6
CWinThread::CreateThread()
Initialization of _AFX_THREAD_STARTUP startup structure m_hThread = (HANDLE)_beginthreadex(lpSecurityAttrs,
nStackSize, &_AfxThreadEntry, &startup, dwCreateFlags|
CREATE_SUSPENDED, (UINT*) &m_nThreadID);ResumeThread(), ::ResumeThread(m_hThread)::WaitForSingleObject(startup.hEvent, INFINITE)_AfxThreadEntry()Cleans up after creating child thread::SetEvent(startup.hEvent2)_AfxThreadEntry() keeps running
Pic
7
_AfxThreadEntry()
Copies and initializes thread’s specific data (a lot of them) Unblocks parent thread::WaitForSingleObject(startup.hEvent2, INFINITE) Back to CWinThread::CreateThread()
if (pThread->m_pfnThreadProc != NULL)
nResult=(*pThread->m_pfnThreadProc)(pThread->m_pThreadParams);
else if (!pThread->InitInstance())
nResult=pThread->ExitInstance();
else
pThread-> Run();
//clean up and shutdown thread
AfxEndThread(nResult);
Pic
8
A time line for Mom and Junior threads
9
CWinThread::Run()
int CWinThread::Run(){ ASSERT_VALID(this);// for tracking the idle time state
BOOL bIdle = TRUE;LONG lIdleCount = 0;// acquire and dispatch messages until a WM_QUIT message is received.for (;;){ // phase1: check to see if we can do idle work
while (bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)){ // call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))bIdle = FALSE; // assume "no idle" state
} // phase2: pump messages while availabledo { // pump message, but quit on WM_QUIT
if (!PumpMessage())return ExitInstance();
// reset "no idle" state after pumping "normal" messageif (IsIdleMessage(&m_msgCur)) {
bIdle = TRUE;lIdleCount = 0;}
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));}ASSERT(FALSE); // not reachable
}
10
CWinThread::OnIdle()
BOOL CWinThread::OnIdle(LONG lCount){
if (lCount <= 0) {
// send WM_IDLEUPDATECMDUI to the main window
// send WM_IDLEUPDATECMDUI to all frame window
else if (lCount >= 0) {
//Call AfxLockTempMaps/AfxUnlockTempMaps
//to free maps, DLLs, etc
}
return lCount < 0; //nothing more to do if lCount >= 0
}
11
Executing a class member in its own thread
Motivationclass Task {…};
class Processor {
public:
void SubmitTask(Task t);
private:
usigned ProcessTask(void *data);
std::queue<Task> m_tasks;
}
ProblemWINBASEAPI HANDLE WINAPI CreateThread(LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, WDORD dwCreationFlags, LPDWORD lpThreadID);
12
Usage
Foo f;
runInThread(f, &Foo::member, args);
13
Solution #1
template <class Object>void runInThread(Object& obj, DWORD(WINAPI Objecg::*memberFunc)(void), HANDLE*
handle=0, DWORD* id=0) {using std::runtime_error;DWORD (WINAPI *threadFunc)(void*);
//pass object “this” pointer via thread void* pointervoid *thisPointer=satic_cast<void*>(&obj);
//copy class member pointer to global function pointer*reinterpret_cast<long*>(&threadFunc)=*reinterpret_cast<long*>(&memberFunc);
DWORD threadId = -1;
HANDLE result=CreateThread(0, 0, threadFunc, thisPointer, 0, &threadId);if (result == 0) throw runtime_error(“CreateThread failed”);
if (handle) *handle=result;if (id) *id = threadId;
}
14
Problems of solution #1
The solution relies on WINAPI calling convention (__stdcall) which is not always possible. It would not work with const member functions.
The solution assumes (incorectly) that non-static member function will always revieve their this pointer as the first argument on the call stack.
Using the void* thread argument to pass the object this pointer prevents the member function from taking any parameters. It’s a big limitation.
15
Solution #2
template <class Object, class MemberFunc, class Param>class ThreadableMember {private:
Object& m_instance;MemberFunc m_member;
struct ThrdArg {Object *object;MemberFunc memberFunc;Param param;
ThrdArg(Object& o, MemberFunc m, const Param& p) :object(o), memberFunc(m), param(p) {}
};static DWORD WINAPI ThreadFunc(LPVOID v) {
std::auto_ptr<ThrdArg> arg(reinterpret_cast<ThrdArg*>(v);
return ((arg->object)->*(arg->memberFunc))(arg->param);
}
16
Solution #2 (cont.)
public:ThreadableMember(Object& instance, MemberFunc member) :
m_instance(instance), m_member(member) {}
void run(const Param& param, HANDLE* handle=0, DWORD* id=0){DWORD thrdId=-1;
ThrArg* thrdArg = new ThrArg(m_instance, m_member, param);
HANDLE result = CreateThread(0, 0, &ThreadFunc, thrdArg,0,&thrdId);
if (result != 0){if (handle) *handle=result;if (id) *id = thrdId;
} else {delete thrdArg;throw std::tuntime_error(“CreateThread failed”);
}}};
17
Usage
Foo f;
ThreadableMember<Foo, DWORD(Foo::*)(char*), char*>
m(f, &Foo::Bar1);
m.run(“alpha”); Helper function:
template <class Object, class MemberFunc, class Param>
inline void runInThread(Object& instance, MemberFunc member,
const Param& param, HANDLE* handle=0, DWORD* id=0){
ThreadableMember<Object,MemberFunc,Param> m(instance, member);
m.run(param, handle, id);
}
runInThread(f, &Foo::Bar2, class Bar2Param);
18
Issues
Take care of you data members Thread security and stack-size can
be added as a parameters to Threadable::run() and runInThread()
One more parameter (perhaps, with default value) can be added to create thread in a suspended state
19
Singleton Creation in a Thread-safe Way
class Singleton {
public:
static Singleton& instance(){
if (!m_instance)
m_instance = new Singleton;
return *m_instance;
}
protected:
Singleton();
private:
static Singleton* m_instance;
};
Singleton* Singleton::m_instance = 0;
20
Problem
Thread may be preempted by the operating system during if (!m_instance). Then another thread gets control. Since the first thread did not finished creation of the m_instance, the second thread evaluates (!m_instance) as true and creates another singleton.
21
Solutionclass Singleton {public:
static Singleton& instance(){m_key.Lock();if (!m_instance)
m_instance = new Singleton;return *m_instance;m_key.Unlock();
}protected:
Singleton();private:
static Singleton* m_instance;static CCriticalSection m_key;
};
Singleton* Singleton::m_instance = 0;CCriticalSection Singleton::m_key; Different from the code in the article
22
Heaps
Let’s compare these two functions:void SomeFunc(Foo bar){
Obj* pObj=new Obj;
pObj->Hadler(bar);
delete pObj;
}
void SomeFunc(Foo bar){
Obj obj;
obj.Hadler(bar);
}
23
Possible memory leak (can be solved with auto_ptr)
Heap is a shared resource, which has to be synchronized
The more interdependences between threads the less scalable application
24
Process heap and Private heap
When a new Win32 process starts up, OS creates a default Process heap. GetProcessHeap() returns its handle
HANDLE HeapCreate(DWORD flOptions, DWORD dwInitialSize, DWORD dwMaximumSize) creates Private heap
LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, DWORD dwButes);BOOL HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);BOOL HeapDestroy(HANDLE hHeap);
Serialization is turned on by default, but can be turned off by using a flag value of HEAP_NO_SERIALIZE
Access to the process heap in an multithreaded application must be serialized.