From 67f2d89c164c7adccdee065da1caac319d528b50 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Fri, 29 Jun 2012 16:37:08 +0000 Subject: - yet another crash fix on exit; - events speed-up - quick ICQ exit git-svn-id: http://svn.miranda-ng.org/main/trunk@687 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/Mir_core/mir_core.def | 2 + plugins/Mir_core/mir_core_10.vcxproj | 15 ++ plugins/Mir_core/mir_core_10.vcxproj.filters | 3 + plugins/Mir_core/miranda.cpp | 319 +---------------------- plugins/Mir_core/miranda.h | 4 + plugins/Mir_core/modules.cpp | 146 ++++++----- plugins/Mir_core/threads.cpp | 372 +++++++++++++++++++++++++++ 7 files changed, 477 insertions(+), 384 deletions(-) create mode 100644 plugins/Mir_core/threads.cpp (limited to 'plugins') diff --git a/plugins/Mir_core/mir_core.def b/plugins/Mir_core/mir_core.def index 7d998c0d14..e0cefde13b 100644 --- a/plugins/Mir_core/mir_core.def +++ b/plugins/Mir_core/mir_core.def @@ -124,3 +124,5 @@ db_set_utf @120 db_set_w @121 db_set_ws @122 UnloadCoreModule @123 +Thread_SetName @124 + diff --git a/plugins/Mir_core/mir_core_10.vcxproj b/plugins/Mir_core/mir_core_10.vcxproj index 68b4adf4d8..6600013e1a 100644 --- a/plugins/Mir_core/mir_core_10.vcxproj +++ b/plugins/Mir_core/mir_core_10.vcxproj @@ -43,6 +43,7 @@ + @@ -124,6 +125,13 @@ $(IntDir)$(TargetName).lib + + + + + + copy /Y $(IntDir)$(TargetName).lib $(SolutionDir)\lib + @@ -185,6 +193,13 @@ $(IntDir)$(TargetName).lib + + + + + + copy /Y $(IntDir)$(TargetName).lib $(SolutionDir)\lib + diff --git a/plugins/Mir_core/mir_core_10.vcxproj.filters b/plugins/Mir_core/mir_core_10.vcxproj.filters index 1aca9fda03..27b6a42604 100644 --- a/plugins/Mir_core/mir_core_10.vcxproj.filters +++ b/plugins/Mir_core/mir_core_10.vcxproj.filters @@ -46,6 +46,9 @@ Source Files + + Source Files + diff --git a/plugins/Mir_core/miranda.cpp b/plugins/Mir_core/miranda.cpp index 910b204292..b059e11cb7 100644 --- a/plugins/Mir_core/miranda.cpp +++ b/plugins/Mir_core/miranda.cpp @@ -29,326 +29,10 @@ HWND hAPCWindow = NULL; int InitPathUtils(void); void (*RecalculateTime)(void); -HANDLE hStackMutex, hThreadQueueEmpty; int hLangpack = 0; HINSTANCE hInst = 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 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; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// 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; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -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) 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); - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) Thread_Wait(void) -{ - // acquire the list and wake up any alertable threads - if ( MirandaWaitForMutex(hStackMutex)) { - int j; - for (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; -} +HANDLE hStackMutex, hThreadQueueEmpty; ///////////////////////////////////////////////////////////////////////////////////////// // module init @@ -378,6 +62,7 @@ static void LoadCoreModule(void) hAPCWindow = CreateWindowEx(0, _T("STATIC"), NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); SetWindowLongPtr(hAPCWindow, GWLP_WNDPROC, (LONG_PTR)APCWndProc); hStackMutex = CreateMutex(NULL, FALSE, NULL); + hThreadQueueEmpty = CreateEvent(NULL, TRUE, TRUE, NULL); #ifdef WIN64 HMODULE mirInst = GetModuleHandleA("miranda64.exe"); diff --git a/plugins/Mir_core/miranda.h b/plugins/Mir_core/miranda.h index 65140260c1..71c0804fd1 100644 --- a/plugins/Mir_core/miranda.h +++ b/plugins/Mir_core/miranda.h @@ -43,6 +43,7 @@ int InitPathUtils(void); extern HINSTANCE hInst; extern HWND hAPCWindow; +extern HANDLE hStackMutex, hThreadQueueEmpty; /**** modules.cpp **********************************************************************/ @@ -68,6 +69,8 @@ struct THookSubscriber }; }; +#define HOOK_SECRET_SIGNATURE 0xDEADBABA + struct THook { char name[ MAXMODULELABELLENGTH ]; @@ -75,6 +78,7 @@ struct THook int subscriberCount; THookSubscriber* subscriber; MIRANDAHOOK pfnHook; + DWORD secretSignature; CRITICAL_SECTION csHook; }; diff --git a/plugins/Mir_core/modules.cpp b/plugins/Mir_core/modules.cpp index f451a70f60..bd8636ccb8 100644 --- a/plugins/Mir_core/modules.cpp +++ b/plugins/Mir_core/modules.cpp @@ -109,17 +109,18 @@ MIR_CORE_DLL(HANDLE) CreateHookableEvent(const char *name) return NULL; } - THook* ret = (THook*)mir_alloc(sizeof(THook)); - strncpy(ret->name, name, sizeof(ret->name)); ret->name[ MAXMODULELABELLENGTH-1 ] = 0; - ret->id = hookId++; - ret->subscriberCount = 0; - ret->subscriber = NULL; - ret->pfnHook = NULL; - InitializeCriticalSection(&ret->csHook); - hooks.insert(ret); + THook* newItem = (THook*)mir_alloc(sizeof(THook)); + strncpy(newItem->name, name, sizeof(newItem->name)); newItem->name[ MAXMODULELABELLENGTH-1 ] = 0; + newItem->id = hookId++; + newItem->subscriberCount = 0; + newItem->subscriber = NULL; + newItem->pfnHook = NULL; + newItem->secretSignature = HOOK_SECRET_SIGNATURE; + InitializeCriticalSection(&newItem->csHook); + hooks.insert(newItem); LeaveCriticalSection(&csHooks); - return (HANDLE)ret; + return (HANDLE)newItem; } MIR_CORE_DLL(int) DestroyHookableEvent(HANDLE hEvent) @@ -135,6 +136,7 @@ MIR_CORE_DLL(int) DestroyHookableEvent(HANDLE hEvent) } THook* p = hooks[idx]; + p->secretSignature = 0; if (p->subscriberCount) { mir_free(p->subscriber); p->subscriber = NULL; @@ -223,38 +225,44 @@ MIR_CORE_DLL(int) CallHookSubscribers(HANDLE hEvent, WPARAM wParam, LPARAM lPara return returnVal; } -static int checkHook(HANDLE hHook) +static bool checkHook(HANDLE hHook) { - if (hHook == NULL) - return -1; - - EnterCriticalSection(&csHooks); - if (pLastHook != hHook || !pLastHook) { - if (hooks.getIndex((THook*)hHook) == -1) { - LeaveCriticalSection(&csHooks); - return -1; - } - pLastHook = (THook*)hHook; + THook* p = (THook*)hHook; + if (p == NULL) + return false; + + bool ret; + __try + { + if (p->secretSignature != HOOK_SECRET_SIGNATURE) + ret = false; + else if (p->subscriberCount == 0) + ret = false; + else + ret = true; } - LeaveCriticalSection(&csHooks); - return 0; + __except(EXCEPTION_EXECUTE_HANDLER) + { + ret = false; + } + + return ret; } static void CALLBACK HookToMainAPCFunc(ULONG_PTR dwParam) { THookToMainThreadItem* item = (THookToMainThreadItem*)dwParam; - - if (checkHook(item->hook) == -1) - item->result = -1; - else - item->result = CallHookSubscribers(item->hook, item->wParam, item->lParam); + item->result = CallHookSubscribers(item->hook, item->wParam, item->lParam); SetEvent(item->hDoneEvent); } MIR_CORE_DLL(int) NotifyEventHooks(HANDLE hEvent, WPARAM wParam, LPARAM lParam) { + if ( !checkHook(hEvent)) + return -1; + if ( GetCurrentThreadId() == mainThreadId) - return (checkHook(hEvent) == -1) ? -1 : CallHookSubscribers(hEvent, wParam, lParam); + return CallHookSubscribers(hEvent, wParam, lParam); mir_ptr item; item->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -267,11 +275,8 @@ MIR_CORE_DLL(int) NotifyEventHooks(HANDLE hEvent, WPARAM wParam, LPARAM lParam) static HANDLE HookEventInt(int type, const char* name, MIRANDAHOOK hookProc, void* object, LPARAM lParam) { - int idx; - THook* p; - HANDLE ret; - EnterCriticalSection(&csHooks); + int idx; if ((idx = hooks.getIndex((THook*)name)) == -1) { #ifdef _DEBUG OutputDebugStringA("Attempt to hook: \t"); @@ -282,7 +287,7 @@ static HANDLE HookEventInt(int type, const char* name, MIRANDAHOOK hookProc, voi return NULL; } - p = hooks[ idx ]; + THook* p = hooks[ idx ]; p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount+1)); p->subscriber[ p->subscriberCount ].type = type; p->subscriber[ p->subscriberCount ].pfnHook = hookProc; @@ -291,7 +296,7 @@ static HANDLE HookEventInt(int type, const char* name, MIRANDAHOOK hookProc, voi p->subscriber[ p->subscriberCount ].hOwner = GetInstByAddress(hookProc); p->subscriberCount++; - ret = (HANDLE)((p->id << 16) | p->subscriberCount); + HANDLE ret = (HANDLE)((p->id << 16) | p->subscriberCount); LeaveCriticalSection(&csHooks); return ret; } @@ -318,11 +323,9 @@ MIR_CORE_DLL(HANDLE) HookEventObjParam(const char* name, MIRANDAHOOKOBJPARAM hoo MIR_CORE_DLL(HANDLE) HookEventMessage(const char* name, HWND hwnd, UINT message) { - int idx; - THook* p; - HANDLE ret; - EnterCriticalSection(&csHooks); + + int idx; if ((idx = hooks.getIndex((THook*)name)) == -1) { #ifdef _DEBUG MessageBoxA(NULL, "Attempt to hook non-existant event", name, MB_OK); @@ -331,34 +334,34 @@ MIR_CORE_DLL(HANDLE) HookEventMessage(const char* name, HWND hwnd, UINT message) return NULL; } - p = hooks[ idx ]; + THook* p = hooks[ idx ]; p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount+1)); p->subscriber[ p->subscriberCount ].type = 5; p->subscriber[ p->subscriberCount ].hwnd = hwnd; p->subscriber[ p->subscriberCount ].message = message; p->subscriberCount++; - ret = (HANDLE)((p->id << 16) | p->subscriberCount); + HANDLE ret = (HANDLE)((p->id << 16) | p->subscriberCount); LeaveCriticalSection(&csHooks); return ret; } MIR_CORE_DLL(int) UnhookEvent(HANDLE hHook) { - int i; - THook* p = NULL; + if (hHook == NULL) + return 0; int hookId = (int)hHook >> 16; int subscriberId = ((int)hHook & 0xFFFF) - 1; - if (hHook == NULL) return 0; - EnterCriticalSection(&csHooks); - for (i = 0; i < hooks.getCount(); i++) { + + THook* p = NULL; + for (int i = 0; i < hooks.getCount(); i++) if (hooks[i]->id == hookId) { p = hooks[i]; break; - } } + } if (p == NULL) { LeaveCriticalSection(&csHooks); @@ -385,35 +388,36 @@ MIR_CORE_DLL(int) UnhookEvent(HANDLE hHook) MIR_CORE_DLL(void) KillModuleEventHooks(HINSTANCE hInst) { - int i, j; - EnterCriticalSection(&csHooks); - for (i = hooks.getCount()-1; i >= 0; i--) { + + for (int i = hooks.getCount()-1; i >= 0; i--) { if (hooks[i]->subscriberCount == 0) continue; - for (j = hooks[i]->subscriberCount-1; j >= 0; j--) { - if (hooks[i]->subscriber[j].hOwner == hInst) { - char szModuleName[ MAX_PATH ]; - GetModuleFileNameA(hooks[i]->subscriber[j].hOwner, szModuleName, sizeof(szModuleName)); - UnhookEvent((HANDLE)((hooks[i]->id << 16) + j + 1)); - if (hooks[i]->subscriberCount == 0) - break; - } } } + for (int j = hooks[i]->subscriberCount-1; j >= 0; j--) { + if (hooks[i]->subscriber[j].hOwner != hInst) + continue; + + char szModuleName[ MAX_PATH ]; + GetModuleFileNameA(hooks[i]->subscriber[j].hOwner, szModuleName, sizeof(szModuleName)); + UnhookEvent((HANDLE)((hooks[i]->id << 16) + j + 1)); + if (hooks[i]->subscriberCount == 0) + break; + } + } LeaveCriticalSection(&csHooks); } MIR_CORE_DLL(void) KillObjectEventHooks(void* pObject) { - int i, j; - EnterCriticalSection(&csHooks); - for (i = hooks.getCount()-1; i >= 0; i--) { + + for (int i = hooks.getCount()-1; i >= 0; i--) { if (hooks[i]->subscriberCount == 0) continue; - for (j = hooks[i]->subscriberCount-1; j >= 0; j--) { + for (int j = hooks[i]->subscriberCount-1; j >= 0; j--) { if (hooks[i]->subscriber[j].object == pObject) { UnhookEvent((HANDLE)((hooks[i]->id << 16) + j + 1)); if (hooks[i]->subscriberCount == 0) @@ -551,12 +555,14 @@ static void CALLBACK CallServiceToMainAPCFunc(ULONG_PTR dwParam) MIR_CORE_DLL(INT_PTR) CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam) { - if (name == NULL) return CALLSERVICE_NOTFOUND; + if (name == NULL) + return CALLSERVICE_NOTFOUND; + // the service is looked up within the main thread, since the time it takes // for the APC queue to clear the service being called maybe removed. // even thou it may exists before the call, the critsec can't be locked between calls. if (GetCurrentThreadId() == mainThreadId) - return CallService(name, wParam, lParam); + return CallService(name, wParam, lParam); mir_ptr item; item->wParam = wParam; @@ -576,12 +582,14 @@ MIR_CORE_DLL(int) CallFunctionAsync(void (__stdcall *func)(void *), void *arg) MIR_CORE_DLL(void) KillModuleServices(HINSTANCE hInst) { EnterCriticalSection(&csServices); + for (int i = services.getCount()-1; i >= 0; i--) { if (services[i]->hOwner == hInst) { char szModuleName[ MAX_PATH ]; GetModuleFileNameA(services[i]->hOwner, szModuleName, sizeof(szModuleName)); DestroyServiceFunction((HANDLE)services[i]->nameHash); - } } + } + } LeaveCriticalSection(&csServices); } @@ -589,6 +597,7 @@ MIR_CORE_DLL(void) KillModuleServices(HINSTANCE hInst) MIR_CORE_DLL(void) KillObjectServices(void* pObject) { EnterCriticalSection(&csServices); + for (int i = services.getCount()-1; i >= 0; i--) if (services[i]->object == pObject) DestroyServiceFunction((HANDLE)services[i]->nameHash); @@ -613,23 +622,26 @@ int InitialiseModularEngine(void) void DestroyModularEngine(void) { EnterCriticalSection(&csHooks); + for (int i=0; i < hooks.getCount(); i++) { THook* p = hooks[i]; - if (p->subscriberCount) + if (p->subscriberCount) mir_free(p->subscriber); DeleteCriticalSection(&p->csHook); mir_free(p); } hooks.destroy(); + LeaveCriticalSection(&csHooks); DeleteCriticalSection(&csHooks); EnterCriticalSection(&csServices); + for (int j=0; j < services.getCount(); j++) mir_free(services[j]); - services.destroy(); + LeaveCriticalSection(&csServices); - DeleteCriticalSection(&csServices); + DeleteCriticalSection(&csServices); CloseHandle(hMainThread); } diff --git a/plugins/Mir_core/threads.cpp b/plugins/Mir_core/threads.cpp new file mode 100644 index 0000000000..68fb526f5d --- /dev/null +++ b/plugins/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 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(DWORD dwThreadID, const char *szThreadName) +{ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +} -- cgit v1.2.3