与forkthread,forkthread_r对应还有另外一套forkthreadex,forkthreadex_r
不同之处在于前者调用_beginthread来创建线程,后者调用_beginthreadex创建线程,可指定的参数更多一些
基本的思想都是一样的,都是为了当进程正常退出的时候能够安全的结束线程,
下面只分析forkthread和forkthread_r.
/*
Purpose:
A safe version of _beginthread()
Description:
A new thread is created and the source thread is paused until
internal code to call MS_SYSTEM_THREAD_PUSH is made in the context
if the new thread.
The source thread is then released and then the user supplied
code is called, when that function returns -- MS_SYSTEM_THREAD_POP
is called and then the thread returns.
This insures that Miranda will not exit whilst new threads
are trying to be born; and the unwind wait stack will unsure
that Miranda will wait for all created threads to return as well.
Caveats:
The function must be reimplemented across MT plugins, since thread
creation depends on CRT which can not be shared.
*/
struct THREAD_WAIT_ENTRY {
DWORD dwThreadId; // valid if hThread isn't signalled
HANDLE hThread;
HINSTANCE hOwner;
};
struct THREAD_WAIT_ENTRY *WaitingThreads=NULL; //这个变量用于登记所有正在工作的线程
//进程结束的时候需要等待所有这些线程结束
int WaitingThreadsCount=0; //正在运行的工作线程的总数
struct FORK_ARG {
HANDLE hEvent;
pThreadFunc threadcode;
pThreadFuncEx threadcodeex;
void *arg;
};
/
// forkthread - starts a new thread
void __cdecl forkthread_r(void * arg)
{
struct FORK_ARG * fa = (struct FORK_ARG *) arg;
void (*callercode)(void*)=fa->threadcode;
void * cookie=fa->arg;
CallService(MS_SYSTEM_THREAD_PUSH,0,0);
SetEvent(fa->hEvent);
__try {
callercode(cookie);
} __finally {
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
CallService(MS_SYSTEM_THREAD_POP,0,0);
}
return;
}
unsigned long forkthread (
void (__cdecl *threadcode)(void*),
unsigned long stacksize,
void *arg
)
{
unsigned long rc;
struct FORK_ARG fa;
fa.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
fa.threadcode=threadcode;
fa.arg=arg;
rc=_beginthread(forkthread_r,stacksize,&fa);
if ((unsigned long)-1L != rc)
WaitForSingleObject(fa.hEvent,INFINITE);
CloseHandle(fa.hEvent);
return rc;
}
static int ForkThreadService(WPARAM wParam, LPARAM lParam)
{
return (int)forkthread(( pThreadFunc )wParam, 0, ( void* )lParam );
}
//CallService(MS_SYSTEM_THREAD_PUSH,0,0)都做了什么?
//该函数向WaitingThreads里添加一项,表示有一个线程正在工作,同时设置hThreadQueueEmpty为无信号态,即不空
//一般会在线程开始运行的时候调用该函数进行登记
int UnwindThreadPush(WPARAM wParam,LPARAM lParam)
{
ResetEvent(hThreadQueueEmpty); // thread list is not empty
if (WaitForSingleObject(hStackMutex,INFINITE)==WAIT_OBJECT_0)
{
HANDLE hThread=0;
DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),&hThread,THREAD_SET_CONTEXT,FALSE,0);
WaitingThreads=mir_realloc(WaitingThreads,sizeof(struct THREAD_WAIT_ENTRY)*(WaitingThreadsCount+1));
WaitingThreads[WaitingThreadsCount].hThread=hThread;
WaitingThreads[WaitingThreadsCount].dwThreadId=GetCurrentThreadId();
WaitingThreads[WaitingThreadsCount].hOwner=GetInstByAddress( GetCurrentThreadEntryPoint() );
WaitingThreadsCount++;
#ifdef _DEBUG
{
char szBuf[64];
mir_snprintf(szBuf,SIZEOF(szBuf),"*** pushing thread (%x)/n",GetCurrentThreadId());
OutputDebugStringA(szBuf);
}
#endif
ReleaseMutex(hStackMutex);
} //if
return 0;
}
//CallService(MS_SYSTEM_THREAD_POP,0,0)都做了什么?
//该函数从WaitingThreads移除一项,假如WaitingThreads为空,设置hThreadQueueEmpty为信号态
//一般在线程结束以后调用该函数将自己从WaitingThreads移除
int UnwindThreadPop(WPARAM wParam,LPARAM lParam)
{
if (WaitForSingleObject(hStackMutex,INFINITE)==WAIT_OBJECT_0)
{
DWORD dwThreadId=GetCurrentThreadId();
int j;
for (j=0;j<WaitingThreadsCount;j++)
{
if (WaitingThreads[j].dwThreadId == dwThreadId)
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
CloseHandle(WaitingThreads[j].hThread);
WaitingThreadsCount--;
if (j<WaitingThreadsCount)
memmove(&WaitingThreads[j],&WaitingThreads[j+1],(WaitingThreadsCount-j) * sizeof(struct THREAD_WAIT_ENTRY));
if (!WaitingThreadsCount)
{
mir_free(WaitingThreads);
WaitingThreads=NULL;
WaitingThreadsCount=0;
ReleaseMutex(hStackMutex);
SetEvent(hThreadQueueEmpty); // thread list is empty now
return 0;
}
else
{
WaitingThreads=mir_realloc(WaitingThreads,sizeof(struct THREAD_WAIT_ENTRY)*WaitingThreadsCount);
} //if
ReleaseMutex(hStackMutex);
return 0;
} //if
} //for
ReleaseMutex(hStackMutex);
} //if
return 1;
}
//Miranda退出的时候将检查所有的线程是否结束,否则就等待,
//直到所有的线程都正常结束以后,进程才结束
static int MirandaWaitForMutex(HANDLE hEvent)
{
for (;;) {
// will get WAIT_IO_COMPLETE for QueueUserAPC() which isnt a result
DWORD rc=MsgWaitForMultipleObjectsExWorkaround(1, &hEvent, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
if ( rc == WAIT_OBJECT_0 + 1 ) {
MSG msg;
while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) {
if ( IsDialogMessage(msg.hwnd, &msg) ) continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} else if ( rc==WAIT_OBJECT_0 ) {
// got object
return 1;
} else if ( rc==WAIT_ABANDONED_0 || rc == WAIT_FAILED ) return 0;
}
}
static void UnwindThreadWait(void)
{
// acquire the list and wake up any alertable threads
if ( MirandaWaitForMutex(hStackMutex) )
{
int j;
for (j=0;j<WaitingThreadsCount;j++)
{
//DummyAPCFunc 实际上什么都不做,只是用来唤醒该线程
//QueueUserAPC 向处于告警状态的线程投递一个APC, APC进入队列以后,
//线程将会被唤醒执行APC,同时等待函数返回WAIT_IO_COMPLETION。
QueueUserAPC(DummyAPCFunc,WaitingThreads[j].hThread, 0);
}
ReleaseMutex(hStackMutex);
}
// give all unclosed threads 5 seconds to close
// 如果5s以后线程还没有结束,就强力杀掉线程
SetTimer( NULL, 0, 5000, KillAllThreads );
// wait until the thread list is empty
MirandaWaitForMutex(hThreadQueueEmpty);
}
//强力杀掉所有线程
VOID CALLBACK KillAllThreads(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
if ( MirandaWaitForMutex( hStackMutex ))
{
int j;
for ( j=0; j < WaitingThreadsCount; j++ )
{
char szModuleName[ MAX_PATH ];
GetModuleFileNameA( WaitingThreads[j].hOwner, szModuleName, sizeof(szModuleName));
Netlib_Logf( NULL, "Thread %08x was abnormally terminated because module '%s' didn't released it",
WaitingThreads[j].hThread, szModuleName );
TerminateThread( WaitingThreads[j].hThread, 9999 );
}
if ( WaitingThreadsCount )
{
mir_free(WaitingThreads);
WaitingThreads=NULL;
WaitingThreadsCount=0;
}
ReleaseMutex(hStackMutex);
SetEvent(hThreadQueueEmpty);
}
}