Fix various problems with unloading ClientPrefs and SourceMod (bug 5874, r=ds).

--HG--
extra : rebase_source : 0a35f8380d651ca65fac9dd402c5cd3625e3105c
This commit is contained in:
David Anderson 2013-08-22 14:05:44 -07:00
parent d79b41e4a7
commit 94d33a4ef1
17 changed files with 195 additions and 183 deletions

View File

@ -775,25 +775,21 @@ CExtension *CExtensionManager::FindByOrder(unsigned int num)
bool CExtensionManager::UnloadExtension(IExtension *_pExt)
{
if (!_pExt)
{
return false;
}
CExtension *pExt = (CExtension *)_pExt;
if (m_Libs.find(pExt) == m_Libs.end())
{
return false;
}
/* Tell it to unload */
if (pExt->IsLoaded())
{
IExtensionInterface *pAPI = pExt->GetAPI();
pAPI->OnExtensionUnload();
}
pExt->GetAPI()->OnExtensionUnload();
/* First remove us from internal lists */
// Remove us from internal lists. Note that because we do this, it's
// possible that our extension could be added back if another plugin
// tries to load during this process. If we ever find this to happen,
// we can just block plugin loading.
g_ShareSys.RemoveInterfaces(_pExt);
m_Libs.remove(pExt);
@ -827,13 +823,9 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt)
{
pDep = (*c_iter);
if ((pAPI=pDep->GetAPI()) == NULL)
{
continue;
}
if (pDep == pExt)
{
continue;
}
/* Now, get its dependency list */
bool dropped = false;
List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
@ -862,15 +854,11 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt)
while (i_iter != pDep->m_ChildDeps.end())
{
if ((*i_iter).owner == pExt)
{
i_iter = pDep->m_ChildDeps.erase(i_iter);
}
else
{
i_iter++;
}
}
}
/* Unbind our natives from Core */
pExt->DropEverything();
@ -887,6 +875,11 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt)
}
}
// Everything has been informed that we're unloading, so give the
// extension one last notification.
if (pExt->IsLoaded() && pExt->GetAPI()->GetExtensionVersion() >= 7)
pExt->GetAPI()->OnDependenciesDropped();
pExt->Unload();
delete pExt;

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 sw=4 :
* vim: set ts=4 sw=4 tw=99 noet:
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -147,6 +147,7 @@ static void logic_init(const sm_core_t* core, sm_logic_t* _logic)
gamehelpers = core->gamehelpers;
g_pSourcePawn = *core->spe1;
g_pSourcePawn2 = *core->spe2;
SMGlobalClass::head = core->listeners;
g_ShareSys.Initialize();
g_pCoreIdent = g_ShareSys.CreateCoreIdentity();

View File

@ -49,7 +49,7 @@ using namespace SourceHook;
* Add 1 to the RHS of this expression to bump the intercom file
* This is to prevent mismatching core/logic binaries
*/
#define SM_LOGIC_MAGIC (0x0F47C0DE - 22)
#define SM_LOGIC_MAGIC (0x0F47C0DE - 23)
#if defined SM_LOGIC
class IVEngineServer
@ -270,6 +270,7 @@ struct sm_core_t
void * serverFactory;
void * engineFactory;
void * matchmakingDSFactory;
SMGlobalClass * listeners;
};
struct sm_logic_t

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 sw=4 :
* vim: set ts=4 sw=4 tw=99 noet:
* =============================================================================
* SourceMod
* Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved.
@ -406,6 +406,7 @@ void InitLogicBridge()
core_bridge.engineFactory = (void *)g_SMAPI->GetEngineFactory(false);
core_bridge.serverFactory = (void *)g_SMAPI->GetServerFactory(false);
core_bridge.listeners = SMGlobalClass::head;
ILibrary *mmlib;
char path[PLATFORM_MAX_PATH];

View File

@ -129,6 +129,8 @@ ConfigResult SourceModBase::OnSourceModConfigChanged(const char *key,
return ConfigResult_Ignore;
}
static bool sSourceModInitialized = false;
bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late)
{
const char *gamepath = g_SMAPI->GetBaseDir();
@ -240,6 +242,8 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late
g_pSourcePawn2->SetDebugListener(logicore.debugger);
sSourceModInitialized = true;
/* Hook this now so we can detect startup without calling StartSourceMod() */
SH_ADD_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SourceModBase::LevelInit), false);
@ -474,24 +478,34 @@ size_t SourceModBase::BuildPath(PathType type, char *buffer, size_t maxlength, c
}
void SourceModBase::CloseSourceMod()
{
if (!sSourceModInitialized)
return;
SH_REMOVE_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SourceModBase::LevelInit), false);
if (g_Loaded)
{
/* Force a level end */
LevelShutdown();
ShutdownServices();
}
/* Rest In Peace */
ShutdownLogicBridge();
ShutdownJIT();
}
void SourceModBase::ShutdownServices()
{
/* Unload plugins */
scripts->Shutdown();
/* Unload extensions */
extsys->Shutdown();
SH_REMOVE_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SourceModBase::LevelInit), false);
if (g_Loaded)
{
if (g_pOnMapEnd)
{
g_Forwards.ReleaseForward(g_pOnMapEnd);
}
/* Notify! */
SMGlobalClass *pBase = SMGlobalClass::head;
@ -535,11 +549,6 @@ void SourceModBase::CloseSourceMod()
SH_REMOVE_HOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::GameFrame), false);
}
/* Rest In Peace */
ShutdownLogicBridge();
ShutdownJIT();
}
void SourceModBase::LogMessage(IExtension *pExt, const char *format, ...)
{
IExtensionInterface *pAPI = pExt->GetAPI();

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 :
* vim: set ts=4 sw=4 tw=99 noet:
* =============================================================================
* SourceMod
* Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved.
@ -136,6 +136,8 @@ public: // ISourceMod
int GetPluginId();
int GetShApiVersion();
bool IsMapRunning();
private:
void ShutdownServices();
private:
CStack<CDataPack *> m_freepacks;
char m_SMBaseDir[PLATFORM_MAX_PATH];

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 :
* vim: set ts=4 sw=4 tw=99 noet:
* =============================================================================
* SourceMod Client Preferences Extension
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -55,35 +55,12 @@ void CookieManager::Unload()
for (int i = playerhelpers->GetMaxClients()+1; --i > 0;)
{
if (connected[i])
{
OnClientDisconnecting(i);
}
}
/* Find all cookies and delete them */
Cookie *current;
for (SourceHook::List<Cookie *>::iterator _iter = cookieList.begin(); _iter != cookieList.end(); _iter++)
{
current = (Cookie *)*_iter;
if (current == NULL)
{
continue;
}
g_ClientPrefs.cookieMutex->Lock();
if (current->usedInQuery)
{
current->shouldDelete = true;
g_ClientPrefs.cookieMutex->Unlock();
}
else
{
g_ClientPrefs.cookieMutex->Unlock();
delete current;
}
}
delete (*_iter);
cookieList.clear();
}
@ -93,9 +70,7 @@ Cookie *CookieManager::FindCookie(const char *name)
Cookie **pCookie = cookieTrie.retrieve(name);
if (pCookie == NULL)
{
return NULL;
}
return *pCookie;
}
@ -116,7 +91,6 @@ Cookie *CookieManager::CreateCookie(const char *name, const char *description, C
/* First time cookie - Create from scratch */
pCookie = new Cookie(name, description, access);
pCookie->usedInQuery++;
/* Attempt to insert cookie into the db and get its ID num */
TQueryOp *op = new TQueryOp(Query_InsertCookie, pCookie);

View File

@ -75,34 +75,23 @@ struct Cookie
dbid = -1;
for (int i=0; i<=MAXCLIENTS; i++)
{
data[i] = NULL;
}
shouldDelete = false;
usedInQuery = 0;
}
~Cookie()
{
for (int i=0; i<=MAXCLIENTS; i++)
{
if (data[i] != NULL)
{
delete data[i];
}
}
}
char name[MAX_NAME_LENGTH+1];
char description[MAX_DESC_LENGTH+1];
int dbid;
CookieData *data[MAXCLIENTS+1];
CookieAccess access;
/* Reference counting stuff */
bool shouldDelete;
int usedInQuery;
};
class CookieManager : public IClientListener, public IPluginsListener

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 :
* vim: set ts=4 sw=4 tw=99 noet:
* =============================================================================
* SourceMod Client Preferences Extension
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -50,9 +50,6 @@ DbDriver g_DriverType;
bool ClientPrefs::SDK_OnLoad(char *error, size_t maxlength, bool late)
{
queryMutex = threader->MakeMutex();
cookieMutex = threader->MakeMutex();
DBInfo = dbi->FindDatabaseConf("clientprefs");
if (DBInfo == NULL)
@ -155,13 +152,15 @@ void ClientPrefs::NotifyInterfaceDrop(SMInterface *pInterface)
}
}
void ClientPrefs::SDK_OnUnload()
void ClientPrefs::SDK_OnDependenciesDropped()
{
// At this point, we're guaranteed that DBI has flushed the worker thread
// for us, so no cookies should have outstanding queries.
g_CookieManager.Unload();
handlesys->RemoveType(g_CookieType, myself->GetIdentity());
handlesys->RemoveType(g_CookieIterator, myself->GetIdentity());
g_CookieManager.Unload();
if (Database != NULL)
{
Database->Close();
@ -199,9 +198,6 @@ void ClientPrefs::SDK_OnUnload()
plsys->RemovePluginsListener(&g_CookieManager);
playerhelpers->RemoveClientListener(&g_CookieManager);
queryMutex->DestroyThis();
cookieMutex->DestroyThis();
}
void ClientPrefs::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
@ -312,7 +308,11 @@ void ClientPrefs::DatabaseConnect()
databaseLoading = false;
// Need a new scope because of the goto above.
{
ke::AutoLock lock(&queryLock);
this->ProcessQueryCache();
}
return;
fatal_fail:
@ -323,23 +323,17 @@ fatal_fail:
bool ClientPrefs::AddQueryToQueue(TQueryOp *query)
{
queryMutex->Lock();
{
ke::AutoLock lock(&queryLock);
if (Database == NULL)
{
cachedQueries.push_back(query);
queryMutex->Unlock();
return false;
}
if (!cachedQueries.empty())
{
queryMutex->Unlock();
this->ProcessQueryCache();
}
else
{
queryMutex->Unlock();
}
query->SetDatabase(Database);
dbi->AddToThreadQueue(query, PrioQueue_Normal);
@ -348,12 +342,11 @@ bool ClientPrefs::AddQueryToQueue( TQueryOp *query )
void ClientPrefs::ProcessQueryCache()
{
if (Database == NULL)
{
return;
}
queryLock.AssertCurrentThreadOwns();
if (Database == NULL)
return;
queryMutex->Lock();
TQueryOp *op;
for (SourceHook::List<TQueryOp *>::iterator iter = cachedQueries.begin(); iter != cachedQueries.end(); iter++)
{
@ -363,7 +356,6 @@ void ClientPrefs::ProcessQueryCache()
}
cachedQueries.clear();
queryMutex->Unlock();
}
size_t IsAuthIdConnected(char *authID)
@ -417,8 +409,7 @@ void ClientPrefs::CatchLateLoadClients()
void ClientPrefs::ClearQueryCache(int serial)
{
queryMutex->Lock();
ke::AutoLock lock(&queryLock);
for (SourceHook::List<TQueryOp *>::iterator iter = cachedQueries.begin(); iter != cachedQueries.end();)
{
TQueryOp *op = *iter;
@ -432,7 +423,6 @@ void ClientPrefs::ClearQueryCache(int serial)
iter++;
}
}
queryMutex->Unlock();
}
bool Translate(char *buffer,
@ -530,7 +520,5 @@ ClientPrefs::ClientPrefs()
phrases = NULL;
DBInfo = NULL;
cookieMutex = NULL;
queryMutex = NULL;
identity = NULL;
}

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 :
* vim: set ts=4 sw=4 tw=99 noet:
* =============================================================================
* SourceMod Client Preferences Extension
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -39,6 +39,8 @@
#include "smsdk_ext.h"
#include "sh_list.h"
#include <ke_thread_utils.h>
char * UTIL_strncpy(char * destination, const char * source, size_t num);
#include "cookie.h"
@ -74,10 +76,7 @@ public:
*/
virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
/**
* @brief This is called right before the extension is unloaded.
*/
virtual void SDK_OnUnload();
virtual void SDK_OnDependenciesDropped();
/**
* @brief This is called once all known extensions have been loaded.
@ -162,7 +161,7 @@ public:
private:
SourceHook::List<TQueryOp *> cachedQueries;
IMutex *queryMutex;
ke::Mutex queryLock;
IdentityToken_t *identity;
};

View File

@ -303,23 +303,6 @@ int TQueryOp::PullQuerySerial()
ParamData::~ParamData()
{
if (cookie)
{
g_ClientPrefs.cookieMutex->Lock();
cookie->usedInQuery--;
if (cookie->shouldDelete && cookie->usedInQuery <= 0)
{
g_ClientPrefs.cookieMutex->Unlock();
delete cookie;
cookie = NULL;
}
else
{
g_ClientPrefs.cookieMutex->Unlock();
}
}
if (data)
{
/* Data is only ever passed in a client disconnect query and always needs to be deleted */

View File

@ -67,7 +67,7 @@
//#define SMEXT_ENABLE_MEMUTILS
#define SMEXT_ENABLE_GAMEHELPERS
//#define SMEXT_ENABLE_TIMERSYS
#define SMEXT_ENABLE_THREADER
//#define SMEXT_ENABLE_THREADER
//#define SMEXT_ENABLE_LIBSYS
#define SMEXT_ENABLE_MENUS
//#define SMEXT_ENABLE_ADTFACTORY

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 :
* vim: set ts=4 sw=4 tw=99 noet:
* =============================================================================
* SourceMod Base Extension Code
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -97,6 +97,12 @@ IUserMessages *usermsgs = NULL;
#if defined SMEXT_ENABLE_TRANSLATOR
ITranslator *translator = NULL;
#endif
#if defined SMEXT_ENABLE_NINVOKE
INativeInterface *ninvoke = NULL;
#endif
#if defined SMEXT_ENABLE_ROOTCONSOLEMENU
IRootConsole *rootconsole = NULL;
#endif
/** Exports the main interface */
PLATFORM_EXTERN_C IExtensionInterface *GetSMExtAPI()
@ -185,6 +191,12 @@ bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error,
#if defined SMEXT_ENABLE_TRANSLATOR
SM_GET_IFACE(TRANSLATOR, translator);
#endif
#if defined SMEXT_ENABLE_NINVOKE
SM_GET_IFACE(NINVOKE, ninvoke);
#endif
#if defined SMEXT_ENABLE_ROOTCONSOLEMENU
SM_GET_IFACE(ROOTCONSOLE, rootconsole);
#endif
if (SDK_OnLoad(error, maxlength, late))
{
@ -227,6 +239,11 @@ void SDKExtension::OnExtensionUnload()
SDK_OnUnload();
}
void SDKExtension::OnDependenciesDropped()
{
SDK_OnDependenciesDropped();
}
const char *SDKExtension::GetExtensionAuthor()
{
return SMEXT_CONF_AUTHOR;
@ -279,6 +296,10 @@ void SDKExtension::SDK_OnAllLoaded()
{
}
void SDKExtension::SDK_OnDependenciesDropped()
{
}
#if defined SMEXT_CONF_METAMOD
PluginId g_PLID = 0; /**< Metamod plugin ID */

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 :
* vim: set ts=4 sw=4 tw=99 noet:
* =============================================================================
* SourceMod Base Extension Code
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -91,6 +91,12 @@
#if defined SMEXT_ENABLE_TRANSLATOR
#include <ITranslator.h>
#endif
#if defined SMEXT_ENABLE_NINVOKE
#include <INativeInvoker.h>
#endif
#if defined SMEXT_ENABLE_ROOTCONSOLEMENU
#include <IRootConsoleMenu.h>
#endif
#if defined SMEXT_CONF_METAMOD
#include <ISmmPlugin.h>
@ -121,7 +127,7 @@ public:
virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
/**
* @brief This is called right before the extension is unloaded.
* @brief This is called once the extension unloading process begins.
*/
virtual void SDK_OnUnload();
@ -135,6 +141,12 @@ public:
*/
virtual void SDK_OnPauseChange(bool paused);
/**
* @brief Called after SDK_OnUnload, once all dependencies have been
* removed, and the extension is about to be removed from memory.
*/
virtual void SDK_OnDependenciesDropped();
#if defined SMEXT_CONF_METAMOD
/**
* @brief Called when Metamod is attached, before the extension version is called.
@ -197,6 +209,9 @@ public: //IExtensionInterface
virtual const char *GetExtensionDescription();
/** Returns date string */
virtual const char *GetExtensionDateString();
/** Called after OnExtensionUnload, once dependencies have been dropped. */
virtual void OnDependenciesDropped();
#if defined SMEXT_CONF_METAMOD
public: //ISmmPlugin
/** Called when the extension is attached to Metamod. */
@ -289,6 +304,12 @@ extern IUserMessages *usermsgs;
#if defined SMEXT_ENABLE_TRANSLATOR
extern ITranslator *translator;
#endif
#if defined SMEXT_ENABLE_NINVOKE
extern INativeInterface *ninvoke;
#endif
#if defined SMEXT_ENABLE_ROOTCONSOLEMENU
extern IRootConsole *rootconsole;
#endif
#if defined SMEXT_CONF_METAMOD
PLUGIN_GLOBALVARS();

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 :
* vim: set ts=4 sw=4 tw=99 noet:
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -134,8 +134,9 @@ namespace SourceMod
* itself is not versioned.
*
* V6 - added TestFeature() to IShareSys.
* V7 - added OnDependenciesDropped() to IExtensionInterface.
*/
#define SMINTERFACE_EXTENSIONAPI_VERSION 6
#define SMINTERFACE_EXTENSIONAPI_VERSION 7
/**
* @brief The interface an extension must expose.
@ -313,6 +314,17 @@ namespace SourceMod
virtual void OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
{
}
/**
* @brief Called once all dependencies have been unloaded. This is
* called AFTER OnExtensionUnload(), but before the extension library
* has been unloaded. It can be used as an alternate unload hook for
* cases where having no dependent plugins would make shutdown much
* simplier.
*/
virtual void OnDependenciesDropped()
{
}
};
/**

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 :
* vim: set ts=4 sw=4 tw=99 noet:
* =============================================================================
* SourceMod Base Extension Code
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -239,6 +239,11 @@ void SDKExtension::OnExtensionUnload()
SDK_OnUnload();
}
void SDKExtension::OnDependenciesDropped()
{
SDK_OnDependenciesDropped();
}
const char *SDKExtension::GetExtensionAuthor()
{
return SMEXT_CONF_AUTHOR;
@ -291,6 +296,10 @@ void SDKExtension::SDK_OnAllLoaded()
{
}
void SDKExtension::SDK_OnDependenciesDropped()
{
}
#if defined SMEXT_CONF_METAMOD
PluginId g_PLID = 0; /**< Metamod plugin ID */

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 :
* vim: set ts=4 sw=4 tw=99 noet:
* =============================================================================
* SourceMod Base Extension Code
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -127,7 +127,7 @@ public:
virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
/**
* @brief This is called right before the extension is unloaded.
* @brief This is called once the extension unloading process begins.
*/
virtual void SDK_OnUnload();
@ -141,6 +141,12 @@ public:
*/
virtual void SDK_OnPauseChange(bool paused);
/**
* @brief Called after SDK_OnUnload, once all dependencies have been
* removed, and the extension is about to be removed from memory.
*/
virtual void SDK_OnDependenciesDropped();
#if defined SMEXT_CONF_METAMOD
/**
* @brief Called when Metamod is attached, before the extension version is called.
@ -203,6 +209,9 @@ public: //IExtensionInterface
virtual const char *GetExtensionDescription();
/** Returns date string */
virtual const char *GetExtensionDateString();
/** Called after OnExtensionUnload, once dependencies have been dropped. */
virtual void OnDependenciesDropped();
#if defined SMEXT_CONF_METAMOD
public: //ISmmPlugin
/** Called when the extension is attached to Metamod. */