summaryrefslogtreecommitdiff
path: root/src/mir_core/threads.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mir_core/threads.cpp')
-rw-r--r--src/mir_core/threads.cpp372
1 files changed, 372 insertions, 0 deletions
diff --git a/src/mir_core/threads.cpp b/src/mir_core/threads.cpp
new file mode 100644
index 0000000000..90dcdaaf5d
--- /dev/null
+++ b/src/mir_core/threads.cpp
@@ -0,0 +1,372 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2012 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "commonheaders.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// APC and mutex functions
+
+static void __stdcall DummyAPCFunc(ULONG_PTR)
+{
+ /* called in the context of thread that cleared it's APC queue */
+ return;
+}
+
+static int MirandaWaitForMutex(HANDLE hEvent)
+{
+ for (;;) {
+ // will get WAIT_IO_COMPLETE for QueueUserAPC() which isnt a result
+ DWORD rc = MsgWaitForMultipleObjectsEx(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;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// exception handling
+
+static DWORD __cdecl sttDefaultFilter(DWORD, EXCEPTION_POINTERS*)
+{
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+pfnExceptionFilter pMirandaExceptFilter = sttDefaultFilter;
+
+MIR_CORE_DLL(pfnExceptionFilter) GetExceptionFilter()
+{
+ return pMirandaExceptFilter;
+}
+
+MIR_CORE_DLL(pfnExceptionFilter) SetExceptionFilter(pfnExceptionFilter pMirandaExceptFilter)
+{
+ pfnExceptionFilter oldOne = pMirandaExceptFilter;
+ if (pMirandaExceptFilter != 0)
+ pMirandaExceptFilter = pMirandaExceptFilter;
+ return oldOne;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// thread support functions
+
+struct THREAD_WAIT_ENTRY
+{
+ DWORD dwThreadId; // valid if hThread isn't signalled
+ HANDLE hThread;
+ HINSTANCE hOwner;
+ void* pObject;
+ //PVOID addr;
+};
+
+static LIST<THREAD_WAIT_ENTRY> threads(10, NumericKeySortT);
+
+struct FORK_ARG {
+ HANDLE hEvent;
+ pThreadFunc threadcode;
+ pThreadFuncEx threadcodeex;
+ void *arg, *owner;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// 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;
+ Thread_Push(( HINSTANCE)callercode);
+ SetEvent(fa->hEvent);
+ __try
+ {
+ callercode(cookie);
+ }
+ __except(pMirandaExceptFilter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ }
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ Thread_Pop();
+ return;
+}
+
+MIR_CORE_DLL(UINT_PTR) forkthread( void (__cdecl *threadcode)(void*), unsigned long stacksize, void *arg)
+{
+ UINT_PTR 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 ((UINT_PTR)-1L != rc)
+ WaitForSingleObject(fa.hEvent, INFINITE);
+
+ CloseHandle(fa.hEvent);
+ return rc;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// forkthreadex - starts a new thread with the extended info and returns the thread id
+
+unsigned __stdcall forkthreadex_r(void * arg)
+{
+ struct FORK_ARG *fa = (struct FORK_ARG *)arg;
+ pThreadFuncEx threadcode = fa->threadcodeex;
+ pThreadFuncOwner threadcodeex = (pThreadFuncOwner)fa->threadcodeex;
+ void *cookie = fa->arg;
+ void *owner = fa->owner;
+ unsigned long rc = 0;
+
+ Thread_Push((HINSTANCE)threadcode, fa->owner);
+ SetEvent(fa->hEvent);
+ __try
+ {
+ if (owner)
+ rc = threadcodeex(owner, cookie);
+ else
+ rc = threadcode(cookie);
+ }
+ __except(pMirandaExceptFilter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ }
+
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ Thread_Pop();
+ return rc;
+}
+
+MIR_CORE_DLL(UINT_PTR) forkthreadex(
+ void *sec,
+ unsigned stacksize,
+ unsigned (__stdcall *threadcode)(void*),
+ void* owner,
+ void *arg,
+ unsigned *thraddr)
+{
+ UINT_PTR rc;
+ struct FORK_ARG fa = { 0 };
+ fa.threadcodeex = threadcode;
+ fa.arg = arg;
+ fa.owner = owner;
+ fa.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ rc = _beginthreadex(sec, stacksize, forkthreadex_r, (void *)&fa, 0, thraddr);
+ if (rc)
+ WaitForSingleObject(fa.hEvent, INFINITE);
+
+ CloseHandle(fa.hEvent);
+ return rc;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(void) KillObjectThreads(void* owner)
+{
+ if (owner == NULL)
+ return;
+
+ WaitForSingleObject(hStackMutex, INFINITE);
+
+ HANDLE* threadPool = (HANDLE*)alloca(threads.getCount()*sizeof(HANDLE));
+ int threadCount = 0;
+
+ for (int j = threads.getCount(); j--;) {
+ THREAD_WAIT_ENTRY* p = threads[j];
+ if (p->pObject == owner)
+ threadPool[ threadCount++ ] = p->hThread;
+ }
+ ReleaseMutex(hStackMutex);
+
+ // is there anything to kill?
+ if (threadCount > 0) {
+ if ( WaitForMultipleObjects(threadCount, threadPool, TRUE, 5000) == WAIT_TIMEOUT) {
+ // forcibly kill all remaining threads after 5 secs
+ WaitForSingleObject(hStackMutex, INFINITE);
+ for (int j = threads.getCount()-1; j >= 0; j--) {
+ THREAD_WAIT_ENTRY* p = threads[j];
+ if (p->pObject == owner) {
+ TerminateThread(p->hThread, 9999);
+ CloseHandle(p->hThread);
+ threads.remove(j);
+ mir_free(p);
+ }
+ }
+ ReleaseMutex(hStackMutex);
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void CALLBACK KillAllThreads(HWND, UINT, UINT_PTR, DWORD)
+{
+ if ( MirandaWaitForMutex(hStackMutex)) {
+ for (int j=0; j < threads.getCount(); j++) {
+ THREAD_WAIT_ENTRY* p = threads[j];
+ char szModuleName[ MAX_PATH ];
+ GetModuleFileNameA(p->hOwner, szModuleName, sizeof(szModuleName));
+ TerminateThread(p->hThread, 9999);
+ CloseHandle(p->hThread);
+ mir_free(p);
+ }
+
+ threads.destroy();
+
+ ReleaseMutex(hStackMutex);
+ SetEvent(hThreadQueueEmpty);
+ }
+}
+
+MIR_CORE_DLL(void) Thread_Wait(void)
+{
+ // acquire the list and wake up any alertable threads
+ if ( MirandaWaitForMutex(hStackMutex)) {
+ for (int j=0; j < threads.getCount(); j++)
+ QueueUserAPC(DummyAPCFunc, threads[j]->hThread, 0);
+ ReleaseMutex(hStackMutex);
+ }
+
+ // give all unclosed threads 5 seconds to close
+ SetTimer(NULL, 0, 5000, KillAllThreads);
+
+ // wait til the thread list is empty
+ MirandaWaitForMutex(hThreadQueueEmpty);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+typedef LONG (WINAPI *pNtQIT)(HANDLE, LONG, PVOID, ULONG, PULONG);
+#define ThreadQuerySetWin32StartAddress 9
+
+static void* GetCurrentThreadEntryPoint()
+{
+ LONG ntStatus;
+ HANDLE hDupHandle, hCurrentProcess;
+ DWORD_PTR dwStartAddress;
+
+ pNtQIT NtQueryInformationThread = (pNtQIT)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "NtQueryInformationThread" );
+ if(NtQueryInformationThread == NULL) return 0;
+
+ hCurrentProcess = GetCurrentProcess();
+ if(!DuplicateHandle(hCurrentProcess, GetCurrentThread(), hCurrentProcess, &hDupHandle, THREAD_QUERY_INFORMATION, FALSE, 0)){
+ SetLastError(ERROR_ACCESS_DENIED);
+ return NULL;
+ }
+ ntStatus = NtQueryInformationThread(hDupHandle, ThreadQuerySetWin32StartAddress, &dwStartAddress, sizeof(DWORD_PTR), NULL);
+ CloseHandle(hDupHandle);
+
+ if(ntStatus != ERROR_SUCCESS) return 0;
+ return ( void* )dwStartAddress;
+}
+
+MIR_CORE_DLL(INT_PTR) Thread_Push(HINSTANCE hInst, void* pOwner)
+{
+ ResetEvent(hThreadQueueEmpty); // thread list is not empty
+ if ( WaitForSingleObject(hStackMutex, INFINITE) == WAIT_OBJECT_0) {
+ THREAD_WAIT_ENTRY* p = (THREAD_WAIT_ENTRY*)mir_calloc(sizeof(THREAD_WAIT_ENTRY));
+
+ DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &p->hThread, 0, FALSE, DUPLICATE_SAME_ACCESS);
+ p->dwThreadId = GetCurrentThreadId();
+ p->pObject = pOwner;
+ if (pluginListAddr.getIndex(hInst) != -1)
+ p->hOwner = hInst;
+ else
+ p->hOwner = GetInstByAddress(( hInst != NULL ) ? (PVOID)hInst : GetCurrentThreadEntryPoint());
+
+ threads.insert(p);
+
+ ReleaseMutex(hStackMutex);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_CORE_DLL(INT_PTR) Thread_Pop()
+{
+ if ( WaitForSingleObject(hStackMutex, INFINITE) == WAIT_OBJECT_0) {
+ DWORD dwThreadId = GetCurrentThreadId();
+ for (int j=0; j < threads.getCount(); j++) {
+ THREAD_WAIT_ENTRY* p = threads[j];
+ if (p->dwThreadId == dwThreadId) {
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ CloseHandle(p->hThread);
+ threads.remove(j);
+ mir_free(p);
+
+ if ( !threads.getCount()) {
+ threads.destroy();
+ ReleaseMutex(hStackMutex);
+ SetEvent(hThreadQueueEmpty); // thread list is empty now
+ return 0;
+ }
+
+ ReleaseMutex(hStackMutex);
+ return 0;
+ }
+ }
+ ReleaseMutex(hStackMutex);
+ }
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+const DWORD MS_VC_EXCEPTION=0x406D1388;
+
+#pragma pack(push,8)
+typedef struct tagTHREADNAME_INFO
+{
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+MIR_CORE_DLL(void) Thread_SetName(const char *szThreadName)
+{
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = GetCurrentThreadId();
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+}