From 389fa94f1b09f0b1e97d2ecc11c994a12801b898 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Mon, 23 Apr 2018 17:25:10 +0300 Subject: pack of fixes for complete dynamic reload of protocols fixes #1295 --- src/mir_app/src/miranda.h | 14 ++++++++-- src/mir_app/src/options.cpp | 17 ++++++++++-- src/mir_app/src/pluginopts.cpp | 7 ++--- src/mir_app/src/proto_accs.cpp | 39 +++++++++++++-------------- src/mir_app/src/proto_opts.cpp | 8 +++--- src/mir_core/src/threads.cpp | 61 +++++++++++++++++++++++++++--------------- 6 files changed, 93 insertions(+), 53 deletions(-) diff --git a/src/mir_app/src/miranda.h b/src/mir_app/src/miranda.h index ad3370ff48..e1674f4b06 100644 --- a/src/mir_app/src/miranda.h +++ b/src/mir_app/src/miranda.h @@ -66,6 +66,7 @@ void UninitIni(void); extern HINSTANCE g_hInst; extern DWORD hMainThreadId; extern HANDLE hOkToExitEvent, hModulesLoadedEvent, hevLoadModule, hevUnloadModule; +extern HANDLE hAccListChanged; extern wchar_t mirandabootini[MAX_PATH]; extern struct pluginEntry *plugin_crshdmp, *plugin_service, *plugin_ssl, *plugin_clist; extern bool bModulesLoadedFired; @@ -163,10 +164,19 @@ int FreeDefaultAccount(PROTO_INTERFACE* ppi); bool ActivateAccount(PROTOACCOUNT *pa, bool bIsDynamic); void EraseAccount(const char *pszProtoName); -void DeactivateAccount(PROTOACCOUNT *pa, bool bIsDynamic, bool bErase); -void UnloadAccount(PROTOACCOUNT *pa, bool bIsDynamic, bool bErase); void OpenAccountOptions(PROTOACCOUNT *pa); +///////////////////////////////////////////////////////////////////////////////////////// + +#define DAF_DYNAMIC 0x0001 +#define DAF_ERASE 0x0002 +#define DAF_FORK 0x0004 + +void DeactivateAccount(PROTOACCOUNT *pa, int flags); +void UnloadAccount(PROTOACCOUNT *pa, int flags); + +///////////////////////////////////////////////////////////////////////////////////////// + void LoadDbAccounts(void); void WriteDbAccounts(void); diff --git a/src/mir_app/src/options.cpp b/src/mir_app/src/options.cpp index c6780393f2..0cc0f87a49 100644 --- a/src/mir_app/src/options.cpp +++ b/src/mir_app/src/options.cpp @@ -371,10 +371,11 @@ class COptionsDlg : public CDlgBase { int m_currentPage; HTREEITEM m_hCurrentPage; - LIST m_arOpd, m_arDeleted; + LIST m_arOpd, m_arDeleted, m_arInserted; RECT m_rcDisplay; RECT m_rcTab; HFONT m_hBoldFont; + bool m_bInsideApply = false; wchar_t m_szFilterString[1024]; const wchar_t *m_szCaption, *m_szGroup, *m_szPage, *m_szTab; @@ -693,6 +694,7 @@ public: m_timerRebuild(this, NEW_PAGE_TIMER), m_arOpd(10), m_arDeleted(1), + m_arInserted(1), m_szCaption(pszCaption), m_szGroup(pszGroup), m_szPage(pszPage), @@ -838,6 +840,7 @@ public: } LIST arChanged(10, CompareOPD); + m_bInsideApply = true; PSHNOTIFY pshn = {}; pshn.hdr.code = PSN_APPLY; @@ -859,10 +862,17 @@ public: m_currentPage = m_arOpd.indexOf(&p); if (opd) opd->pDialog->Show(); + m_bInsideApply = false; return; } } + m_bInsideApply = false; + for (auto &it : m_arInserted) + m_arOpd.insert(it); + m_arInserted.destroy(); + + // send PSN_WIZFINISH once to last changed tab that belongs to the same group pshn.hdr.code = PSN_WIZFINISH; for (int i = 0; i < arChanged.getCount(); i++) { OptionsPageData *p = arChanged[i]; @@ -1118,7 +1128,10 @@ public: if (opd->pDialog == nullptr) // smth went wrong delete opd; else { - m_arOpd.insert(opd); + if (m_bInsideApply) + m_arInserted.insert(opd); + else + m_arOpd.insert(opd); m_timerRebuild.Start(50); } } diff --git a/src/mir_app/src/pluginopts.cpp b/src/mir_app/src/pluginopts.cpp index 8e75ac0eb4..0bddc0f6f4 100644 --- a/src/mir_app/src/pluginopts.cpp +++ b/src/mir_app/src/pluginopts.cpp @@ -189,9 +189,10 @@ static bool LoadPluginDynamically(PluginListItemData *dat) if (pd->hInst != pPlug->bpi.hInst) continue; - for (auto &pa : accounts) - if (pa->ppro == nullptr && !mir_strcmp(pa->szProtoName, pd->szName)) - ActivateAccount(pa, true); + for (auto &pa : accounts) + if (pa->ppro == nullptr && !mir_strcmp(pa->szProtoName, pd->szName) && pa->bIsEnabled) + if (ActivateAccount(pa, true)) + NotifyEventHooks(hAccListChanged, PRAC_ADDED, (LPARAM)pa); } dat->hInst = pPlug->bpi.hInst; diff --git a/src/mir_app/src/proto_accs.cpp b/src/mir_app/src/proto_accs.cpp index 141f14c706..405d691f6e 100644 --- a/src/mir_app/src/proto_accs.cpp +++ b/src/mir_app/src/proto_accs.cpp @@ -332,7 +332,7 @@ struct DeactivationThreadParam { PROTO_INTERFACE *ppro; pfnUninitProto fnUninit; - bool bIsDynamic, bErase; + int flags; }; pfnUninitProto GetProtocolDestructor(char *szProto); @@ -344,7 +344,7 @@ static int DeactivationThread(DeactivationThreadParam* param) char *szModuleName = NEWSTR_ALLOCA(p->m_szModuleName); - if (param->bIsDynamic) { + if (param->flags & DAF_DYNAMIC) { while (!p->IsReadyToExit()) SleepEx(100, TRUE); @@ -354,7 +354,7 @@ static int DeactivationThread(DeactivationThreadParam* param) KillObjectThreads(p); // waits for them before terminating KillObjectEventHooks(p); // untie an object from the outside world - if (param->bErase) + if (param->flags & DAF_ERASE) p->OnErase(); if (param->fnUninit) @@ -362,14 +362,14 @@ static int DeactivationThread(DeactivationThreadParam* param) KillObjectServices(p); - if (param->bErase) + if (param->flags & DAF_ERASE) EraseAccount(szModuleName); delete param; return 0; } -void DeactivateAccount(PROTOACCOUNT *pa, bool bIsDynamic, bool bErase) +void DeactivateAccount(PROTOACCOUNT *pa, int flags) { if (pa->hwndAccMgrUI) { DestroyWindow(pa->hwndAccMgrUI); @@ -377,10 +377,13 @@ void DeactivateAccount(PROTOACCOUNT *pa, bool bIsDynamic, bool bErase) pa->bAccMgrUIChanged = FALSE; } - pa->iIconBase = -1; + if (flags & DAF_DYNAMIC) + pa->bDynDisabled = true; + + NotifyEventHooks(hAccListChanged, PRAC_REMOVED, (LPARAM)pa); if (pa->ppro == nullptr) { - if (bErase) + if (flags & DAF_ERASE) EraseAccount(pa->szModuleName); return; } @@ -388,10 +391,9 @@ void DeactivateAccount(PROTOACCOUNT *pa, bool bIsDynamic, bool bErase) DeactivationThreadParam *param = new DeactivationThreadParam; param->ppro = pa->ppro; param->fnUninit = GetProtocolDestructor(pa->szProtoName); - param->bIsDynamic = bIsDynamic; - param->bErase = bErase; + param->flags = flags; pa->ppro = nullptr; - if (bIsDynamic) + if (flags & DAF_FORK) mir_forkthread((pThreadFunc)DeactivationThread, param); else DeactivationThread(param); @@ -405,12 +407,9 @@ void KillModuleAccounts(HINSTANCE hInst) if (pd->hInst != hInst) continue; - for (auto &pa : accounts.rev_iter()) { - if (!mir_strcmp(pa->szProtoName, pd->szName)) { - DeactivateAccount(pa, false, false); - pa->bDynDisabled = true; - } - } + for (auto &pa : accounts.rev_iter()) + if (!mir_strcmp(pa->szProtoName, pd->szName)) + DeactivateAccount(pa, DAF_DYNAMIC); } } @@ -431,9 +430,9 @@ void EraseAccount(const char *pszModuleName) ///////////////////////////////////////////////////////////////////////////////////////// -void UnloadAccount(PROTOACCOUNT *pa, bool bIsDynamic, bool bErase) +void UnloadAccount(PROTOACCOUNT *pa, int flags) { - DeactivateAccount(pa, bIsDynamic, bErase); + DeactivateAccount(pa, flags); replaceStrW(pa->tszAccountName, 0); replaceStr(pa->szProtoName, 0); @@ -442,7 +441,7 @@ void UnloadAccount(PROTOACCOUNT *pa, bool bIsDynamic, bool bErase) // szModuleName should be freed only on a program's exit. // otherwise many plugins dependand on static protocol names will crash! // do NOT fix this 'leak', please - if (!bIsDynamic) { + if (!(flags & DAF_DYNAMIC)) { mir_free(pa->szModuleName); mir_free(pa); } @@ -454,7 +453,7 @@ void UnloadAccountsModule() auto T = accounts.rev_iter(); for (auto &it : T) { - UnloadAccount(it, false, false); + UnloadAccount(it, 0); accounts.remove(T.indexOf(&it)); } accounts.destroy(); diff --git a/src/mir_app/src/proto_opts.cpp b/src/mir_app/src/proto_opts.cpp index a506e633ea..e8f76ad3f0 100644 --- a/src/mir_app/src/proto_opts.cpp +++ b/src/mir_app/src/proto_opts.cpp @@ -48,8 +48,6 @@ Alternatively, just click on the Plus sign underneath the list to set up a new I static class CAccountManagerDlg *pAccMgr = nullptr; -extern HANDLE hAccListChanged; - int UnloadPlugin(wchar_t* buf, int bufLen); MIR_APP_DLL(PROTOACCOUNT*) Proto_CreateAccount(const char *pszInternal, const char *pszBaseProto, const wchar_t *tszAccountName) @@ -462,7 +460,7 @@ public: } if (!pa->bIsEnabled) - DeactivateAccount(pa, true, false); + DeactivateAccount(pa, DAF_DYNAMIC | DAF_FORK); } WriteDbAccounts(); @@ -554,7 +552,7 @@ public: WriteDbAccounts(); NotifyEventHooks(hAccListChanged, PRAC_REMOVED, (LPARAM)pa); - UnloadAccount(pa, true, true); + UnloadAccount(pa, DAF_DYNAMIC | DAF_FORK | DAF_ERASE); Refresh(); m_accList.Enable(); @@ -875,7 +873,7 @@ void CAccountFormDlg::OnOk(CCtrlButton*) wchar_t szPlugin[MAX_PATH]; mir_snwprintf(szPlugin, L"%s.dll", _A2T(m_pa->szProtoName)); int idx = accounts.getIndex(m_pa); - UnloadAccount(m_pa, false, false); + UnloadAccount(m_pa, 0); accounts.remove(idx); if (oldProto && UnloadPlugin(szPlugin, _countof(szPlugin))) { wchar_t szNewName[MAX_PATH]; diff --git a/src/mir_core/src/threads.cpp b/src/mir_core/src/threads.cpp index a71d366585..de9474a101 100644 --- a/src/mir_core/src/threads.cpp +++ b/src/mir_core/src/threads.cpp @@ -208,12 +208,9 @@ MIR_CORE_DLL(HANDLE) mir_forkthreadowner(pThreadFuncOwner aFunc, void *owner, vo ///////////////////////////////////////////////////////////////////////////////////////// -MIR_CORE_DLL(void) KillObjectThreads(void* owner) +static void __cdecl KillObjectThreadsWorker(void* owner) { - if (owner == nullptr) - return; - - HANDLE *threadPool = (HANDLE*)alloca(threads.getCount()*sizeof(HANDLE)); + HANDLE *threadPool = (HANDLE*)alloca(threads.getCount() * sizeof(HANDLE)); int threadCount = 0; { mir_cslock lck(csThreads); @@ -224,22 +221,44 @@ MIR_CORE_DLL(void) KillObjectThreads(void* owner) } // is there anything to kill? - if (threadCount > 0) { - if (WaitForMultipleObjects(threadCount, threadPool, TRUE, 5000) == WAIT_TIMEOUT) { - // forcibly kill all remaining threads after 5 secs - mir_cslock lck(csThreads); - auto T = threads.rev_iter(); - for (auto &it : T) { - if (it->pObject == owner) { - char szModuleName[MAX_PATH]; - GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName)); - Netlib_Logf(nullptr, "Killing object thread %s:%p", szModuleName, it->dwThreadId); - TerminateThread(it->hThread, 9999); - CloseHandle(it->hThread); - mir_free(it); - threads.remove(T.indexOf(&it)); - } - } + if (threadCount == 0) + return; + + // wait'em all + if (WaitForMultipleObjects(threadCount, threadPool, TRUE, 5000) != WAIT_TIMEOUT) + return; + + // forcibly kill all remaining threads after 5 secs + mir_cslock lck(csThreads); + auto T = threads.rev_iter(); + for (auto &it : T) { + if (it->pObject == owner) { + char szModuleName[MAX_PATH]; + GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName)); + Netlib_Logf(nullptr, "Killing object thread %s:%p", szModuleName, it->dwThreadId); + TerminateThread(it->hThread, 9999); + CloseHandle(it->hThread); + mir_free(it); + threads.remove(T.indexOf(&it)); + } + } +} + +MIR_CORE_DLL(void) KillObjectThreads(void* owner) +{ + if (owner == nullptr) + return; + + DWORD dwTicks = GetTickCount() + 6000; + HANDLE hThread = mir_forkthread(KillObjectThreadsWorker, owner); + while (GetTickCount() < dwTicks) { + if (WAIT_OBJECT_0 == MsgWaitForMultipleObjectsEx(1, &hThread, 50, QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_ALERTABLE)) + break; + + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); } } } -- cgit v1.2.3