From efd3a8ab5fa7e03f5431598e66498b2552f81641 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 11 Jan 2007 07:29:09 +0000 Subject: [PATCH] Handle system can now unload identities safely. when an identity is removed, all handles owned by it are removed in a chain. --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40288 --- core/msvc8/sourcemod_mm.vcproj | 12 +- core/sm_srvcmds.cpp | 2 + core/smn_filesystem.cpp | 14 -- core/systems/HandleSys.cpp | 280 +++++++++++++++++++-------------- core/systems/HandleSys.h | 26 +-- core/systems/ShareSys.cpp | 4 +- 6 files changed, 188 insertions(+), 150 deletions(-) diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index e66833adf..11b7fc163 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -203,10 +203,6 @@ RelativePath="..\sm_autonatives.cpp" > - - @@ -396,6 +392,10 @@ + + @@ -408,10 +408,6 @@ RelativePath="..\interfaces\ILibrarySys.h" > - - diff --git a/core/sm_srvcmds.cpp b/core/sm_srvcmds.cpp index 3c7d756a2..0b88e3ee6 100644 --- a/core/sm_srvcmds.cpp +++ b/core/sm_srvcmds.cpp @@ -30,6 +30,8 @@ inline const char *StatusToStr(PluginStatus st) return "Uncompiled"; case Plugin_BadLoad: return "Bad Load"; + case Plugin_Failed: + return "Failed"; default: assert(false); return "-"; diff --git a/core/smn_filesystem.cpp b/core/smn_filesystem.cpp index 7a135c69f..bc777175e 100644 --- a/core/smn_filesystem.cpp +++ b/core/smn_filesystem.cpp @@ -428,26 +428,12 @@ static cell_t sm_WriteFileLine(IPluginContext *pContext, const cell_t *params) return 1; } -//:TODO: DEBUG CODE HERE -cell_t PrintStuff(IPluginContext *pContext, const cell_t *params) -{ - char *stuff; - pContext->LocalToString(params[1], &stuff); - - FILE *fp = fopen("c:\\debug.txt", "at"); - fprintf(fp, "%s\n", stuff); - fclose(fp); - - return 0; -} - static FileNatives s_FileNatives; REGISTER_NATIVES(filesystem) { {"OpenDirectory", sm_OpenDirectory}, {"ReadDirEntry", sm_ReadDirEntry}, - {"PrintStuff", PrintStuff},//:TODO: remove this when no longer needed {"OpenFile", sm_OpenFile}, {"DeleteFile", sm_DeleteFile}, {"ReadFileLine", sm_ReadFileLine}, diff --git a/core/systems/HandleSys.cpp b/core/systems/HandleSys.cpp index 4c25ca7ba..36ee9a573 100644 --- a/core/systems/HandleSys.cpp +++ b/core/systems/HandleSys.cpp @@ -231,7 +231,8 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type, QHandle **in_pHandle, unsigned int *in_index, Handle_t *in_handle, - IdentityToken_t *owner) + IdentityToken_t *owner, + bool identity) { unsigned int owner_index = 0; @@ -262,7 +263,7 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type, } /* Set essential information */ - pHandle->set = HandleSet_Used;; + pHandle->set = identity ? HandleSet_Identity : HandleSet_Used; pHandle->refcount = 1; pHandle->type = type; pHandle->serial = m_HSerial; @@ -282,8 +283,10 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type, *in_index = handle; *in_handle = hash; - /* Decode the identity token */ - if (owner) + /* Decode the identity token + * For now, we don't allow nested ownership + */ + if (owner && !identity) { QHandle *pIdentity = &m_Handles[owner_index]; if (pIdentity->ch_prev == 0) @@ -319,7 +322,7 @@ void HandleSystem::SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pTok m_Types[type].typeSec.ident = pToken; } -Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err) +Handle_t HandleSystem::CreateHandleEx(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err, bool identity) { if (!type || type >= HANDLESYS_TYPEARRAY_SIZE @@ -335,8 +338,8 @@ Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityTok /* Check to see if we're allowed to create this handle type */ QHandleType *pType = &m_Types[type]; if (!pType->typeSec.access[HTypeAccess_Create] - && (!pType->typeSec.ident - || pType->typeSec.ident != ident)) + && (!pType->typeSec.ident + || pType->typeSec.ident != ident)) { if (err) { @@ -350,7 +353,7 @@ Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityTok QHandle *pHandle; HandleError _err; - if ((_err=MakePrimHandle(type, &pHandle, &index, &handle, owner)) != HandleError_None) + if ((_err=MakePrimHandle(type, &pHandle, &index, &handle, owner, identity)) != HandleError_None) { if (err) { @@ -365,6 +368,11 @@ Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityTok return handle; } +Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err) +{ + return CreateHandleEx(type, object, owner, ident, err, false); +} + bool HandleSystem::TypeCheck(HandleType_t intype, HandleType_t outtype) { /* Check the type inheritance */ @@ -453,6 +461,27 @@ bool HandleSystem::CheckAccess(QHandle *pHandle, HandleAccessRight right, const return true; } +HandleError HandleSystem::CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner) +{ + /* Get a new Handle ID */ + unsigned int new_index; + QHandle *pNewHandle; + Handle_t new_handle; + HandleError err; + + if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, newOwner)) != HandleError_None) + { + return err; + } + + pNewHandle->clone = index; + pHandle->refcount++; + + *newhandle = new_handle; + + return HandleError_None; +} + HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t *newOwner, const HandleSecurity *pSecurity) { HandleError err; @@ -477,20 +506,63 @@ HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, Iden return HandleError_Access; } - /* Get a new Handle ID */ - unsigned int new_index; - QHandle *pNewHandle; - Handle_t new_handle; - - if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, newOwner)) != HandleError_None) + /* Make sure we're not cloning a clone */ + if (pHandle->clone) { - return err; + QHandle *pParent = &m_Handles[pHandle->clone]; + return CloneHandle(pParent, pHandle->clone, newhandle, newOwner); } - pNewHandle->clone = index; - pHandle->refcount++; + return CloneHandle(pHandle, index, newhandle, newOwner); +} - *newhandle = new_handle; +HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index) +{ + QHandleType *pType = &m_Types[pHandle->type]; + + if (pHandle->clone) + { + /* If we're a clone, decrease the parent reference count */ + QHandle *pMaster; + unsigned int master; + + /* Note that if we ever have per-handle security, we would need to re-check + * the access on this Handle. */ + master = pHandle->clone; + pMaster = &m_Handles[master]; + + /* Release the clone now */ + ReleasePrimHandle(index); + + /* Decrement the master's reference count */ + if (--pMaster->refcount == 0) + { + /* Type should be the same but do this anyway... */ + pType = &m_Types[pMaster->type]; + pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object); + ReleasePrimHandle(master); + } + } else if (pHandle->set == HandleSet_Identity) { + /* If we're an identity, skip all this stuff! + * NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR + */ + ReleasePrimHandle(index); + } else { + /* Decrement, free if necessary */ + if (--pHandle->refcount == 0) + { + pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object); + ReleasePrimHandle(index); + } else { + /* We must be cloned, so mark ourselves as freed */ + pHandle->set = HandleSet_Freed; + /* Now, unlink us, so we're not being tracked by the owner */ + if (pHandle->owner) + { + UnlinkHandleFromOwner(pHandle, index); + } + } + } return HandleError_None; } @@ -512,44 +584,7 @@ HandleError HandleSystem::FreeHandle(Handle_t handle, const HandleSecurity *pSec return HandleError_Access; } - QHandleType *pType = &m_Types[pHandle->type]; - - bool dofree = false; - if (pHandle->clone) - { - /* If we're a clone, decrease the parent reference count */ - QHandle *pMaster; - unsigned int master; - - /* Note that if we ever have per-handle security, we would need to re-check - * the access on this Handle. */ - master = pHandle->clone; - pMaster = &m_Handles[master]; - - /* Release the clone now */ - ReleasePrimHandle(index); - - /* Decrement the master's reference count */ - if (--pMaster->refcount == 0) - { - /* Type should be the same but do this anyway... */ - pType = &m_Types[pMaster->type]; - pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object); - ReleasePrimHandle(master); - } - } else { - /* Decrement, free if necessary */ - if (--pHandle->refcount == 0) - { - pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object); - ReleasePrimHandle(index); - } else { - /* We must be cloned, so mark ourselves as freed */ - pHandle->set = HandleSet_Freed; - } - } - - return HandleError_None; + return FreeHandle(pHandle, index); } HandleError HandleSystem::ReadHandle(Handle_t handle, HandleType_t type, const HandleSecurity *pSecurity, void **object) @@ -597,60 +632,84 @@ HandleError HandleSystem::ReadHandle(Handle_t handle, HandleType_t type, const H return HandleError_None; } +void HandleSystem::UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index) +{ + /* Unlink us if necessary */ + unsigned int ident_index; + if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None) + { + /* Uh-oh! */ + assert(pHandle->owner == 0); + return; + } + /* Note that since 0 is an invalid handle, if any of these links are 0, + * the data can still be set. + */ + QHandle *pIdentity = &m_Handles[ident_index]; + + /* Unlink case: We're the head AND tail node */ + if (index == pIdentity->ch_prev && index == pIdentity->ch_next) + { + pIdentity->ch_prev = 0; + pIdentity->ch_next = 0; + } + /* Unlink case: We're the head node */ + else if (index == pIdentity->ch_prev) { + /* Link us to the next in the chain */ + pIdentity->ch_prev = pHandle->ch_next; + /* Patch up the previous link */ + m_Handles[pHandle->ch_next].ch_prev = 0; + } + /* Unlink case: We're the tail node */ + else if (index == pIdentity->ch_next) { + /* Link us to the previous in the chain */ + pIdentity->ch_next = pHandle->ch_prev; + /* Patch up the next link */ + m_Handles[pHandle->ch_prev].ch_next = 0; + } + /* Unlink case: We're in the middle! */ + else { + /* Patch the forward reference */ + m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev; + /* Patch the backward reference */ + m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next; + } + + /* Lastly, decrease the reference count */ + pIdentity->refcount--; +} + void HandleSystem::ReleasePrimHandle(unsigned int index) { QHandle *pHandle = &m_Handles[index]; - + HandleSet set = pHandle->set; + + if (pHandle->owner && (set != HandleSet_Identity)) + { + UnlinkHandleFromOwner(pHandle, index); + } + + /* Were we an identity ourself? */ + QHandle *pLocal; + if (set == HandleSet_Identity) + { + /* Extra work to do. We need to find everything connected to this identity and release it. */ + unsigned int ch_index, old_index = 0; + while ((ch_index = pHandle->ch_next) != 0) + { + pLocal = &m_Handles[ch_index]; +#if defined _DEBUG + assert(old_index != ch_index); + assert(pLocal->set == HandleSet_Used); + old_index = ch_index; +#endif + FreeHandle(pLocal, ch_index); + } + } + pHandle->set = HandleSet_None; m_Types[pHandle->type].opened--; m_Handles[++m_FreeHandles].freeID = index; - - /* Unlink us if necessary */ - if (pHandle->owner) - { - unsigned int ident_index; - if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None) - { - /* Uh-oh! */ - assert(pHandle->owner == 0); - return; - } - /* Note that since 0 is an invalid handle, if any of these links are 0, - * the data can still be set. - */ - QHandle *pIdentity = &m_Handles[ident_index]; - - /* Unlink case: We're the head AND tail node */ - if (index == pIdentity->ch_prev && index == pIdentity->ch_next) - { - pIdentity->ch_prev = 0; - pIdentity->ch_next = 0; - } - /* Unlink case: We're the head node */ - else if (index == pIdentity->ch_prev) { - /* Link us to the next in the chain */ - pIdentity->ch_prev = pHandle->ch_next; - /* Patch up the previous link */ - m_Handles[pHandle->ch_next].ch_prev = 0; - } - /* Unlink case: We're the tail node */ - else if (index == pIdentity->ch_next) { - /* Link us to the previous in the chain */ - pIdentity->ch_next = pHandle->ch_prev; - /* Patch up the next link */ - m_Handles[pHandle->ch_prev].ch_next = 0; - } - /* Unlink case: We're in the middle! */ - else { - /* Patch the forward reference */ - m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev; - /* Patch the backward reference */ - m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next; - } - - /* Lastly, decrease the reference count */ - pIdentity->refcount--; - } } bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident) @@ -737,19 +796,6 @@ bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident) return true; } -void HandleSystem::MarkHandleAsIdentity(Handle_t handle) -{ - QHandle *pHandle; - unsigned int index; - - if (GetHandle(handle, g_ShareSys.GetIdentRoot(), &pHandle, &index) != HandleError_None) - { - return; - } - - pHandle->set = HandleSet_Identity; -} - bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess) { if (pTypeAccess) diff --git a/core/systems/HandleSys.h b/core/systems/HandleSys.h index 55c65dc44..1b651ba81 100644 --- a/core/systems/HandleSys.h +++ b/core/systems/HandleSys.h @@ -55,8 +55,8 @@ struct QHandle unsigned int freeID; /* ID of a free handle in the free handle chain */ /* Indexes into the handle array for owner membership. * For identity roots, these are treated as the head/tail. */ - unsigned int ch_prev; /* chained previous handle or HEAD */ - unsigned int ch_next; /* chained next handle or TAIL */ + unsigned int ch_prev; /* chained previous handle */ + unsigned int ch_next; /* chained next handle */ }; struct QHandleType @@ -97,6 +97,7 @@ public: //IHandleSystem IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err); + HandleError FreeHandle(Handle_t handle, const HandleSecurity *pSecurity); HandleError CloneHandle(Handle_t handle, @@ -130,26 +131,33 @@ protected: QHandle **pHandle, unsigned int *index, HandleType_t *handle, - IdentityToken_t *owner); + IdentityToken_t *owner, + bool identity=false); /** * Frees a primitive handle. Does no object freeing, only reference count, bookkeepping, * and linked list maintenance. + * If used on an Identity handle, destroys all Handles under that identity. */ void ReleasePrimHandle(unsigned int index); /** - * Sets the security owner of a type + * Sets the security owner of a type. */ void SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken); - /** - * Marks a handle as an identity. - * This prevents it from being tampered with by outside stuff + /** + * Helper function to check access rights. */ - void MarkHandleAsIdentity(Handle_t handle); - bool CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity); + + /** + * Some wrappers for internal functions, so we can pass indexes instead of encoded handles. + */ + HandleError FreeHandle(QHandle *pHandle, unsigned int index); + void UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index); + HandleError CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner); + Handle_t CreateHandleEx(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err, bool identity); private: QHandle *m_Handles; QHandleType *m_Types; diff --git a/core/systems/ShareSys.cpp b/core/systems/ShareSys.cpp index 784a69db3..ef10e5054 100644 --- a/core/systems/ShareSys.cpp +++ b/core/systems/ShareSys.cpp @@ -73,7 +73,7 @@ IdentityType_t ShareSystem::CreateIdentType(const char *name) void ShareSystem::OnHandleDestroy(HandleType_t type, void *object) { - /* We don't care here */ + /* THIS WILL NEVER BE CALLED FOR ANYTHING WITH THE IDENTITY TYPE */ } IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type) @@ -85,7 +85,7 @@ IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type) /* :TODO: Cache? */ IdentityToken_t *pToken = new IdentityToken_t; - pToken->ident = g_HandleSys.CreateHandle(type, NULL, GetIdentRoot(), GetIdentRoot(), NULL); + pToken->ident = g_HandleSys.CreateHandleEx(type, NULL, GetIdentRoot(), GetIdentRoot(), NULL, true); return pToken; }