mfc controls

24
1 More on threads Konstantin Bukin CSE791 – Advanced Windows Programming Summer 2001

Upload: tarnoscribd

Post on 13-Nov-2014

61 views

Category:

Documents


8 download

DESCRIPTION

creating mfc controls

TRANSCRIPT

Page 1: MFC Controls

1

More on threads

Konstantin Bukin CSE791 – Advanced Windows

ProgrammingSummer 2001

Page 2: MFC Controls

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)

Page 3: MFC Controls

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

Page 4: MFC Controls

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

Page 5: MFC Controls

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

Page 6: MFC Controls

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

Page 7: MFC Controls

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

Page 8: MFC Controls

8

A time line for Mom and Junior threads

Page 9: MFC Controls

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

}

Page 10: MFC Controls

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

}

Page 11: MFC Controls

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

Page 12: MFC Controls

12

Usage

Foo f;

runInThread(f, &Foo::member, args);

Page 13: MFC Controls

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;

}

Page 14: MFC Controls

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.

Page 15: MFC Controls

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

}

Page 16: MFC Controls

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

}}};

Page 17: MFC Controls

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

Page 18: MFC Controls

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

Page 19: MFC Controls

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;

Page 20: MFC Controls

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.

Page 21: MFC Controls

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

Page 22: MFC Controls

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

}

Page 23: MFC Controls

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

Page 24: MFC Controls

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.