From 21ed05048fc7157bd6dcbd9c6b30ddbb7747ea62 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 12 Mar 2007 20:40:30 +0000 Subject: [PATCH] dynamic native providers can now be unloaded safely fixed an api naming typo :( --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40611 --- core/systems/PluginSys.cpp | 190 +++++++++++++++++++++++++++++---- core/systems/PluginSys.h | 22 ++-- public/sourcepawn/sp_vm_api.h | 2 +- sourcepawn/jit/x86/jit_x86.cpp | 2 +- sourcepawn/jit/x86/jit_x86.h | 2 +- 5 files changed, 188 insertions(+), 30 deletions(-) diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index 6864afba..ddb5eb73 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -41,6 +41,7 @@ CPlugin::CPlugin(const char *file) m_handle = 0; m_ident = NULL; m_pProps = sm_trie_create(); + m_FakeNativesMissing = false; } CPlugin::~CPlugin() @@ -219,8 +220,15 @@ bool CPlugin::FinishMyCompile(char *error, size_t maxlength) void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...) { + PluginStatus old_status = m_status; m_status = status; + if (old_status == Plugin_Running) + { + /* Tell everyone we're now paused */ + g_PluginSys._SetPauseState(this, true); + } + va_list ap; va_start(ap, error_fmt); vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap); @@ -540,6 +548,47 @@ unsigned int CPlugin::GetLangFileByIndex(unsigned int index) const return m_PhraseFiles.at(index); } +void CPlugin::DependencyDropped(CPlugin *pOwner) +{ + if (!m_ctx.ctx) + { + return; + } + + IPluginContext *pContext = pOwner->GetBaseContext(); + List::iterator iter; + FakeNative *pNative; + sp_native_t *native; + uint32_t idx; + unsigned int unbound = 0; + + for (iter = pOwner->m_fakeNatives.begin(); + iter != pOwner->m_fakeNatives.end(); + iter++) + { + pNative = (*iter); + /* Find this native! */ + if (m_ctx.base->FindNativeByName(pNative->name.c_str(), &idx) != SP_ERROR_NONE) + { + continue; + } + /* Unbind it */ + m_ctx.base->GetNativeByIndex(idx, &native); + native->pfn = NULL; + native->status = SP_NATIVE_UNBOUND; + unbound++; + } + + /* :IDEA: in the future, add native trapping? */ + if (unbound) + { + m_FakeNativesMissing = true; + SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename()); + } + + m_dependsOn.remove(pOwner); +} + /******************* * PLUGIN ITERATOR * *******************/ @@ -995,6 +1044,24 @@ bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxleng /* Tell this plugin to finish initializing itself */ pPlugin->Call_OnPluginStart(); + /* Now, if we have fake natives, go through all plugins that might need rebinding */ + if (pPlugin->m_fakeNatives.size()) + { + List::iterator pl_iter; + CPlugin *pOther; + for (pl_iter = m_plugins.begin(); + pl_iter != m_plugins.end(); + pl_iter++) + { + pOther = (*pl_iter); + if (pOther->GetStatus() == Plugin_Error + && pOther->m_FakeNativesMissing) + { + TryRefreshNatives(pOther); + } + } + } + return true; } @@ -1015,6 +1082,40 @@ void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin) } } +void CPluginManager::TryRefreshNatives(CPlugin *pPlugin) +{ + assert(pPlugin->GetBaseContext() != NULL); + + AddFakeNativesToPlugin(pPlugin); + + /* Find any unbound natives + * Right now, these are not allowed + */ + IPluginContext *pContext = pPlugin->GetBaseContext(); + uint32_t num = pContext->GetNativesNum(); + sp_native_t *native; + for (unsigned int i=0; iGetNativeByIndex(i, &native) != SP_ERROR_NONE) + { + break; + } + if (native->status == SP_NATIVE_UNBOUND) + { + pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name); + return; + } + } + + /* If we got here, all natives are okay again! */ + pPlugin->m_status = Plugin_Running; + if ((pPlugin->m_ctx.ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED) + { + pPlugin->m_ctx.ctx->flags &= ~SPFLAG_PLUGIN_PAUSED; + _SetPauseState(pPlugin, false); + } +} + void CPluginManager::AddFakeNativesToPlugin(CPlugin *pPlugin) { IPluginContext *pContext = pPlugin->GetBaseContext(); @@ -1036,7 +1137,19 @@ void CPluginManager::AddFakeNativesToPlugin(CPlugin *pPlugin) native.func = pNative->func; if (pContext->BindNative(&native) == SP_ERROR_NONE) { - /* :TODO: add to dependency list */ + /* Add us as a dependency, but we're careful not to do this circularly! */ + if (pNative->ctx != pContext) + { + CPlugin *pOther = GetPluginByCtx(ctx); + if (pOther->m_dependents.find(pPlugin) == pOther->m_dependents.end()) + { + pOther->m_dependents.push_back(pPlugin); + } + if (pPlugin->m_dependsOn.find(pOther) == pPlugin->m_dependsOn.end()) + { + pPlugin->m_dependsOn.push_back(pOther); + } + } } } } @@ -1051,6 +1164,44 @@ bool CPluginManager::UnloadPlugin(IPlugin *plugin) return false; } + /* Remove us from the lookup table and linked list */ + m_plugins.remove(pPlugin); + sm_trie_delete(m_LoadLookup, pPlugin->m_filename); + + /* Go through all dependent plugins and tell them this plugin is now gone */ + List::iterator pl_iter; + CPlugin *pOther; + for (pl_iter = pPlugin->m_dependents.begin(); + pl_iter != pPlugin->m_dependents.end(); + pl_iter++) + { + pOther = (*pl_iter); + pOther->DependencyDropped(pPlugin); + } + + /* Tell everyone we depend on that we no longer exist */ + for (pl_iter = pPlugin->m_dependsOn.begin(); + pl_iter != pPlugin->m_dependsOn.end(); + pl_iter++) + { + pOther = (*pl_iter); + pOther->m_dependents.remove(pPlugin); + } + + /* Remove all of our native functions */ + List::iterator fn_iter; + FakeNative *pNative; + for (fn_iter = pPlugin->m_fakeNatives.begin(); + fn_iter != pPlugin->m_fakeNatives.end(); + fn_iter++) + { + pNative = (*fn_iter); + m_Natives.remove(pNative); + sm_trie_delete(m_pNativeLookup, pNative->name.c_str()); + g_pVM->DestroyFakeNative(pNative->func); + delete pNative; + } + List::iterator iter; IPluginsListener *pListener; @@ -1072,10 +1223,6 @@ bool CPluginManager::UnloadPlugin(IPlugin *plugin) pListener = (*iter); pListener->OnPluginDestroyed(pPlugin); } - - /* Remove us from the lookup table and linked list */ - m_plugins.remove(pPlugin); - sm_trie_delete(m_LoadLookup, pPlugin->m_filename); /* Tell the plugin to delete itself */ delete pPlugin; @@ -1570,15 +1717,12 @@ void CPluginManager::OnRootConsoleCommand(const char *command, unsigned int argc { if (IS_STR_FILLED(info->name)) { - g_RootMenu.ConsolePrint(" Title: %s", info->name); - } - if (IS_STR_FILLED(info->version)) - { - g_RootMenu.ConsolePrint(" Version: %s", info->version); - } - if (IS_STR_FILLED(info->url)) - { - g_RootMenu.ConsolePrint(" URL: %s", info->url); + if (IS_STR_FILLED(info->description)) + { + g_RootMenu.ConsolePrint(" Title: %s (%s)", info->name, info->description); + } else { + g_RootMenu.ConsolePrint(" Title: %s", info->name); + } } if (IS_STR_FILLED(info->author)) { @@ -1588,12 +1732,17 @@ void CPluginManager::OnRootConsoleCommand(const char *command, unsigned int argc { g_RootMenu.ConsolePrint(" Version: %s", info->version); } - if (IS_STR_FILLED(info->description)) + if (IS_STR_FILLED(info->url)) { - g_RootMenu.ConsolePrint(" Description: %s", info->description); - } - g_RootMenu.ConsolePrint(" Debugging: %s", pl->IsDebugging() ? "yes" : "no"); - g_RootMenu.ConsolePrint(" Paused: %s", pl->GetStatus() == Plugin_Running ? "no" : "yes"); + g_RootMenu.ConsolePrint(" URL: %s", info->url); + } + if (pl->GetStatus() == Plugin_Error) + { + g_RootMenu.ConsolePrint(" Error: %s", pl->m_errormsg); + } else { + g_RootMenu.ConsolePrint(" Debugging: %s", pl->IsDebugging() ? "yes" : "no"); + g_RootMenu.ConsolePrint(" Running: %s", pl->GetStatus() == Plugin_Running ? "yes" : "no"); + } } else { g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg); g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")", @@ -1726,5 +1875,8 @@ bool CPluginManager::AddFakeNative(IPluginFunction *pFunction, const char *name, m_Natives.push_back(pNative); sm_trie_insert(m_pNativeLookup, name,pNative); + CPlugin *pPlugin = GetPluginByCtx(pNative->ctx->GetContext()); + pPlugin->m_fakeNatives.push_back(pNative); + return true; } diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index 1eb2cec9..f9bda46a 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -92,6 +92,14 @@ struct ContextPair IVirtualMachine *vm; }; +struct FakeNative +{ + IPluginContext *ctx; + IPluginFunction *call; + String name; + SPVM_NATIVE_FUNC func; +}; + enum LoadRes { LoadRes_Successful, @@ -216,6 +224,7 @@ public: protected: void UpdateInfo(); void SetTimeStamp(time_t t); + void DependencyDropped(CPlugin *pOwner); private: ContextPair m_ctx; PluginType m_type; @@ -230,15 +239,11 @@ private: Handle_t m_handle; bool m_WasRunning; CVector m_PhraseFiles; + List m_dependents; + List m_dependsOn; + List m_fakeNatives; Trie *m_pProps; -}; - -struct FakeNative -{ - IPluginContext *ctx; - IPluginFunction *call; - String name; - SPVM_NATIVE_FUNC func; + bool m_FakeNativesMissing; }; class CPluginManager : @@ -393,6 +398,7 @@ public: bool AddFakeNative(IPluginFunction *pFunction, const char *name, SPVM_FAKENATIVE_FUNC func); private: void AddFakeNativesToPlugin(CPlugin *pPlugin); + void TryRefreshNatives(CPlugin *pOther); private: List m_listeners; List m_plugins; diff --git a/public/sourcepawn/sp_vm_api.h b/public/sourcepawn/sp_vm_api.h index 832de0b1..531eef4d 100644 --- a/public/sourcepawn/sp_vm_api.h +++ b/public/sourcepawn/sp_vm_api.h @@ -836,7 +836,7 @@ namespace SourcePawn * @param function Pointer to the fake native created by CreateFakeNative. * @noreturn */ - virtual void DestroyFakenative(SPVM_NATIVE_FUNC func) =0; + virtual void DestroyFakeNative(SPVM_NATIVE_FUNC func) =0; }; }; diff --git a/sourcepawn/jit/x86/jit_x86.cpp b/sourcepawn/jit/x86/jit_x86.cpp index b4b272c9..df505752 100644 --- a/sourcepawn/jit/x86/jit_x86.cpp +++ b/sourcepawn/jit/x86/jit_x86.cpp @@ -2229,7 +2229,7 @@ rewind: return (SPVM_NATIVE_FUNC)jw.outbase; } -void JITX86::DestroyFakenative(SPVM_NATIVE_FUNC func) +void JITX86::DestroyFakeNative(SPVM_NATIVE_FUNC func) { engine->ExecFree((void *)func); } diff --git a/sourcepawn/jit/x86/jit_x86.h b/sourcepawn/jit/x86/jit_x86.h index 02801438..33b4f1af 100644 --- a/sourcepawn/jit/x86/jit_x86.h +++ b/sourcepawn/jit/x86/jit_x86.h @@ -99,7 +99,7 @@ public: const char *GetVersionString(); const char *GetCPUOptimizations(); SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData); - void DestroyFakenative(SPVM_NATIVE_FUNC func); + void DestroyFakeNative(SPVM_NATIVE_FUNC func); }; cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params);