core/systems/HandleSys.cpp
author David Anderson <dvander@alliedmods.net>
Fri Aug 15 05:22:26 2008 +0000 (2008-08-15)
changeset 2226 d10b38524be3
parent 2193 db7c6362f3f2
child 2289 44045ead61cd
permissions -rw-r--r--
Landed sourcepawn-1.2. The big changes:

1) JIT compilation/optimization now occurs per-function, and only when functions are first used. We're now officially a whole-method JIT rather than an AOT compiler (albiet, still a simple JIT). This has two implications: Functions are now much better abstracted internally, and loading a plugin is now much less expensive. If a function contains calls to other functions, THOSE functions are only compiled when they're invoked as well.

2) I've removed debug mode. We always show full backtraces now, as there was a very cheap way to implement this which really cleaned up everything. This is great for a number of reasons -- there's less code, the JIT is better designed, we don't need to relocate debug tables, and best of all we no longer have to tell users to enable debug mode at their own expense.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "HandleSys.h"
    33 #include "ShareSys.h"
    34 #include "PluginSys.h"
    35 #include "ExtensionSys.h"
    36 #include "Logger.h"
    37 #include <assert.h>
    38 #include <string.h>
    39 #include "sm_stringutil.h"
    40 
    41 HandleSystem g_HandleSys;
    42 
    43 QHandle *ignore_handle;
    44 
    45 inline HandleType_t TypeParent(HandleType_t type)
    46 {
    47 	return (type & ~HANDLESYS_SUBTYPE_MASK);
    48 }
    49 
    50 inline HandleError IdentityHandle(IdentityToken_t *token, unsigned int *index)
    51 {
    52 	return g_HandleSys.GetHandle(token->ident, g_ShareSys.GetIdentRoot(), &ignore_handle, index);
    53 }
    54 
    55 HandleSystem::HandleSystem()
    56 {
    57 	m_Handles = new QHandle[HANDLESYS_MAX_HANDLES + 1];
    58 	memset(m_Handles, 0, sizeof(QHandle) * (HANDLESYS_MAX_HANDLES + 1));
    59 
    60 	m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE];
    61 	memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE);
    62 
    63 	m_TypeLookup = sm_trie_create();
    64 	m_strtab = new BaseStringTable(512);
    65 
    66 	m_TypeTail = 0;
    67 }
    68 
    69 HandleSystem::~HandleSystem()
    70 {
    71 	delete [] m_Handles;
    72 	delete [] m_Types;
    73 	sm_trie_destroy(m_TypeLookup);
    74 	delete m_strtab;
    75 }
    76 
    77 
    78 HandleType_t HandleSystem::CreateType(const char *name, 
    79 									  IHandleTypeDispatch *dispatch, 
    80 									  HandleType_t parent, 
    81 									  const TypeAccess *typeAccess, 
    82 									  const HandleAccess *hndlAccess, 
    83 									  IdentityToken_t *ident,
    84 									  HandleError *err)
    85 {
    86 	if (!dispatch)
    87 	{
    88 		if (err)
    89 		{
    90 			*err = HandleError_Parameter;
    91 		}
    92 		return 0;
    93 	}
    94 
    95 	if (typeAccess && typeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
    96 	{
    97 		if (err)
    98 		{
    99 			*err = HandleError_Version;
   100 		}
   101 		return 0;
   102 	}
   103 
   104 	if (hndlAccess && hndlAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
   105 	{
   106 		if (err)
   107 		{
   108 			*err = HandleError_Version;
   109 		}
   110 		return 0;
   111 	}
   112 
   113 	bool isChild = false;
   114 
   115 	if (parent != 0)
   116 	{
   117 		isChild = true;
   118 		if (parent & HANDLESYS_SUBTYPE_MASK)
   119 		{
   120 			if (err)
   121 			{
   122 				*err = HandleError_NoInherit;
   123 			}
   124 			return 0;
   125 		}
   126 		if (parent >= HANDLESYS_TYPEARRAY_SIZE
   127 			|| m_Types[parent].dispatch == NULL)
   128 		{
   129 			if (err)
   130 			{
   131 				*err = HandleError_Parameter;
   132 			}
   133 			return 0;
   134 		}
   135 		if (m_Types[parent].typeSec.access[HTypeAccess_Inherit] == false
   136 			&& (m_Types[parent].typeSec.ident != ident))
   137 		{
   138 			if (err)
   139 			{
   140 				*err = HandleError_Access;
   141 			}
   142 			return 0;
   143 		}
   144 	}
   145 
   146 	if (name && name[0] != '\0')
   147 	{
   148 		if (sm_trie_retrieve(m_TypeLookup, name, NULL))
   149 		{
   150 			if (err)
   151 			{
   152 				*err = HandleError_Parameter;
   153 			}
   154 			return 0;
   155 		}
   156 	}
   157 
   158 	unsigned int index;
   159 
   160 	if (isChild)
   161 	{
   162 		QHandleType *pParent = &m_Types[parent];
   163 		if (pParent->children >= HANDLESYS_MAX_SUBTYPES)
   164 		{
   165 			if (err)
   166 			{
   167 				*err = HandleError_Limit;
   168 			}
   169 			return 0;
   170 		}
   171 		index = 0;
   172 		for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
   173 		{
   174 			if (m_Types[parent + i].dispatch == NULL)
   175 			{
   176 				index = parent + i;
   177 				break;
   178 			}
   179 		}
   180 		if (!index)
   181 		{
   182 			if (err)
   183 			{
   184 				*err = HandleError_Limit;
   185 			}
   186 			return 0;
   187 		}
   188 		pParent->children++;
   189 	} else {
   190 		if (m_FreeTypes == 0)
   191 		{
   192 			/* Reserve another index */
   193 			if (m_TypeTail >= HANDLESYS_TYPEARRAY_SIZE)
   194 			{
   195 				if (err)
   196 				{
   197 					*err = HandleError_Limit;
   198 				}
   199 				return 0;
   200 			} else {
   201 				m_TypeTail += (HANDLESYS_MAX_SUBTYPES + 1);
   202 				index = m_TypeTail;
   203 			}
   204 		} else {
   205 			/* The "free array" is compacted into the normal array for easiness */
   206 			index = m_Types[m_FreeTypes--].freeID;
   207 		}
   208 	}
   209 
   210 	QHandleType *pType = &m_Types[index];
   211 	
   212 	pType->dispatch = dispatch;
   213 	if (name && name[0] != '\0')
   214 	{
   215 		pType->nameIdx = m_strtab->AddString(name);
   216 		sm_trie_insert(m_TypeLookup, name, (void *)pType);
   217 	} else {
   218 		pType->nameIdx = -1;
   219 	}
   220 
   221 	pType->opened = 0;
   222 
   223 	if (typeAccess)
   224 	{
   225 		pType->typeSec = *typeAccess;
   226 	} else {
   227 		InitAccessDefaults(&pType->typeSec, NULL);
   228 		pType->typeSec.ident = ident;
   229 	}
   230 
   231 	if (hndlAccess)
   232 	{
   233 		pType->hndlSec = *hndlAccess;
   234 	} else {
   235 		InitAccessDefaults(NULL, &pType->hndlSec);
   236 	}
   237 
   238 	if (!isChild)
   239 	{
   240 		pType->children = 0;
   241 	}
   242 
   243 	return index;
   244 }
   245 
   246 bool HandleSystem::FindHandleType(const char *name, HandleType_t *type)
   247 {
   248 	QHandleType *_type;
   249 
   250 	if (!sm_trie_retrieve(m_TypeLookup, name, (void **)&_type))
   251 	{
   252 		return false;
   253 	}
   254 
   255 	unsigned int offset = _type - m_Types;
   256 
   257 	if (type)
   258 	{
   259 		*type = offset;
   260 	}
   261 
   262 	return true;
   263 }
   264 
   265 HandleError HandleSystem::TryAllocHandle(unsigned int *handle)
   266 {
   267 	if (m_FreeHandles == 0)
   268 	{
   269 		if (m_HandleTail >= HANDLESYS_MAX_HANDLES)
   270 		{
   271 			return HandleError_Limit;;
   272 		}
   273 		*handle = ++m_HandleTail;
   274 	}
   275 	else
   276 	{
   277 		*handle = m_Handles[m_FreeHandles--].freeID;
   278 	}
   279 
   280 	return HandleError_None;
   281 }
   282 
   283 HandleError HandleSystem::MakePrimHandle(HandleType_t type, 
   284 						   QHandle **in_pHandle, 
   285 						   unsigned int *in_index, 
   286 						   Handle_t *in_handle,
   287 						   IdentityToken_t *owner,
   288 						   bool identity)
   289 {
   290 	HandleError err;
   291 	unsigned int owner_index = 0;
   292 
   293 	if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None))
   294 	{
   295 		return HandleError_Identity;
   296 	}
   297 
   298 	unsigned int handle;
   299 	if ((err = TryAllocHandle(&handle)) != HandleError_None)
   300 	{
   301 		if (!TryAndFreeSomeHandles()
   302 			|| (err = TryAllocHandle(&handle)) != HandleError_None)
   303 		{
   304 			return err;
   305 		}
   306 	}
   307 
   308 	QHandle *pHandle = &m_Handles[handle];
   309 	
   310 	assert(pHandle->set == false);
   311 
   312 	if (++m_HSerial >= HANDLESYS_MAX_SERIALS)
   313 	{
   314 		m_HSerial = 1;
   315 	}
   316 
   317 	/* Set essential information */
   318 	pHandle->set = identity ? HandleSet_Identity : HandleSet_Used;
   319 	pHandle->refcount = 1;
   320 	pHandle->type = type;
   321 	pHandle->serial = m_HSerial;
   322 	pHandle->owner = owner;
   323 	pHandle->ch_next = 0;
   324 	pHandle->access_special = false;
   325 	pHandle->is_destroying = false;
   326 
   327 	/* Create the hash value */
   328 	Handle_t hash = pHandle->serial;
   329 	hash <<= 16;
   330 	hash |= handle;
   331 
   332 	/* Add a reference count to the type */
   333 	m_Types[type].opened++;
   334 
   335 	/* Output */
   336 	*in_pHandle = pHandle;
   337 	*in_index = handle;
   338 	*in_handle = hash;
   339 
   340 	/* Decode the identity token 
   341 	 * For now, we don't allow nested ownership
   342 	 */
   343 	if (owner && !identity)
   344 	{
   345 		QHandle *pIdentity = &m_Handles[owner_index];
   346 		if (pIdentity->ch_prev == 0)
   347 		{
   348 			pIdentity->ch_prev = handle;
   349 			pIdentity->ch_next = handle;
   350 			pHandle->ch_prev = 0;
   351 		}
   352 		else
   353 		{
   354 			/* Link previous node to us (forward) */
   355 			m_Handles[pIdentity->ch_next].ch_next = handle;
   356 			/* Link us to previous node (backwards) */
   357 			pHandle->ch_prev = pIdentity->ch_next;
   358 			/* Set new tail */
   359 			pIdentity->ch_next = handle;
   360 		}
   361 		pIdentity->refcount++;
   362 	}
   363 	else
   364 	{
   365 		pHandle->ch_prev = 0;
   366 	}
   367 
   368 	return HandleError_None;
   369 }
   370 
   371 void HandleSystem::SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken)
   372 {
   373 	if (!type
   374 		|| type >= HANDLESYS_TYPEARRAY_SIZE
   375 		|| m_Types[type].dispatch == NULL)
   376 	{
   377 		return;
   378 	}
   379 
   380 	m_Types[type].typeSec.ident = pToken;
   381 }
   382 
   383 Handle_t HandleSystem::CreateHandleInt(HandleType_t type, 
   384 									   void *object, 
   385 									   const HandleSecurity *pSec,
   386 									   HandleError *err, 
   387 									   const HandleAccess *pAccess,
   388 									   bool identity)
   389 {
   390 	IdentityToken_t *ident;
   391 	IdentityToken_t *owner;
   392 
   393 	if (pSec)
   394 	{
   395 		ident = pSec->pIdentity;
   396 		owner = pSec->pOwner;
   397 	} else {
   398 		ident = NULL;
   399 		owner = NULL;
   400 	}
   401 
   402 	if (!type 
   403 		|| type >= HANDLESYS_TYPEARRAY_SIZE
   404 		|| m_Types[type].dispatch == NULL)
   405 	{
   406 		if (err)
   407 		{
   408 			*err = HandleError_Parameter;
   409 		}
   410 		return 0;
   411 	}
   412 
   413 	/* Check to see if we're allowed to create this handle type */
   414 	QHandleType *pType = &m_Types[type];
   415 	if (!pType->typeSec.access[HTypeAccess_Create]
   416 	&& (!pType->typeSec.ident
   417 		|| pType->typeSec.ident != ident))
   418 	{
   419 		if (err)
   420 		{
   421 			*err = HandleError_Access;
   422 		}
   423 		return 0;
   424 	}
   425 
   426 	unsigned int index;
   427 	Handle_t handle;
   428 	QHandle *pHandle;
   429 	HandleError _err;
   430 
   431 	if ((_err=MakePrimHandle(type, &pHandle, &index, &handle, owner, identity)) != HandleError_None)
   432 	{
   433 		if (err)
   434 		{
   435 			*err = _err;
   436 		}
   437 		return 0;
   438 	}
   439 
   440 	if (pAccess)
   441 	{
   442 		pHandle->access_special = true;
   443 		pHandle->sec = *pAccess;
   444 	}
   445 
   446 	pHandle->object = object;
   447 	pHandle->clone = 0;
   448 
   449 	return handle;
   450 }
   451 
   452 Handle_t HandleSystem::CreateHandleEx(HandleType_t type, void *object, const HandleSecurity *pSec, const HandleAccess *pAccess, HandleError *err)
   453 {
   454 	return CreateHandleInt(type, object, pSec, err, pAccess, false);
   455 }
   456 
   457 Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err)
   458 {
   459 	HandleSecurity sec;
   460 
   461 	sec.pIdentity = ident;
   462 	sec.pOwner = owner;
   463 
   464 	return CreateHandleEx(type, object, &sec, NULL, err);
   465 }
   466 
   467 bool HandleSystem::TypeCheck(HandleType_t intype, HandleType_t outtype)
   468 {
   469 	/* Check the type inheritance */
   470 	if (intype & HANDLESYS_SUBTYPE_MASK)
   471 	{
   472 		if (intype != outtype
   473 			&& (TypeParent(intype) != TypeParent(outtype)))
   474 		{
   475 			return false;
   476 		}
   477 	} else {
   478 		if (intype != outtype)
   479 		{
   480 			return false;
   481 		}
   482 	}
   483 
   484 	return true;
   485 }
   486 
   487 HandleError HandleSystem::GetHandle(Handle_t handle,
   488 									IdentityToken_t *ident, 
   489 									QHandle **in_pHandle, 
   490 									unsigned int *in_index,
   491 									bool ignoreFree)
   492 {
   493 	unsigned int serial = (handle >> 16);
   494 	unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
   495 
   496 	if (index == 0 || index == 0xFFFF)
   497 	{
   498 		return HandleError_Index;
   499 	}
   500 
   501 	QHandle *pHandle = &m_Handles[index];
   502 
   503 	if (!pHandle->set
   504 		|| (pHandle->set == HandleSet_Freed && !ignoreFree))
   505 	{
   506 		return HandleError_Freed;
   507 	} else if (pHandle->set == HandleSet_Identity
   508 			   && ident != g_ShareSys.GetIdentRoot())
   509 	{
   510 		/* Only IdentityHandle() can read this! */
   511 		return HandleError_Identity;
   512 	}
   513 	if (pHandle->serial != serial)
   514 	{
   515 		return HandleError_Changed;
   516 	}
   517 
   518 	*in_pHandle = pHandle;
   519 	*in_index = index;
   520 
   521 	return HandleError_None;
   522 }
   523 
   524 bool HandleSystem::CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity)
   525 {
   526 	QHandleType *pType = &m_Types[pHandle->type];
   527 	unsigned int access;
   528 
   529 	if (pHandle->access_special)
   530 	{
   531 		access = pHandle->sec.access[right];
   532 	} else {
   533 		access = pType->hndlSec.access[right];
   534 	}
   535 
   536 	/* Check if the type's identity matches */
   537 	if (access & HANDLE_RESTRICT_IDENTITY)
   538 	{
   539 		IdentityToken_t *owner = pType->typeSec.ident;
   540 		if (!owner
   541 			|| (!pSecurity || pSecurity->pIdentity != owner))
   542 		{
   543 			return false;
   544 		}
   545 	}
   546 
   547 	/* Check if the owner is allowed */
   548 	if (access & HANDLE_RESTRICT_OWNER)
   549 	{
   550 		IdentityToken_t *owner = pHandle->owner;
   551 		if (owner
   552 			&& (!pSecurity || pSecurity->pOwner != owner))
   553 		{
   554 			return false;
   555 		}
   556 	}
   557 
   558 	return true;
   559 }
   560 
   561 HandleError HandleSystem::CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner)
   562 {
   563 	/* Get a new Handle ID */
   564 	unsigned int new_index;
   565 	QHandle *pNewHandle;
   566 	Handle_t new_handle;
   567 	HandleError err;
   568 
   569 	if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, newOwner)) != HandleError_None)
   570 	{
   571 		return err;
   572 	}
   573 
   574 	/* Assign permissions from parent */
   575 	if (pHandle->access_special)
   576 	{
   577 		pNewHandle->access_special = true;
   578 		pNewHandle->sec = pHandle->sec;
   579 	}
   580 
   581 	pNewHandle->clone = index;
   582 	pHandle->refcount++;
   583 
   584 	*newhandle = new_handle;
   585 
   586 	return HandleError_None;
   587 }
   588 
   589 HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t *newOwner, const HandleSecurity *pSecurity)
   590 {
   591 	HandleError err;
   592 	QHandle *pHandle;
   593 	unsigned int index;
   594 	IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
   595 
   596 	if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
   597 	{
   598 		return err;
   599 	}
   600 
   601 	/* Identities cannot be cloned */
   602 	if (pHandle->set == HandleSet_Identity)
   603 	{
   604 		return HandleError_Identity;
   605 	}
   606 
   607 	/* Check if the handle can be cloned */
   608 	if (!CheckAccess(pHandle, HandleAccess_Clone, pSecurity))
   609 	{
   610 		return HandleError_Access;
   611 	}
   612 
   613 	/* Make sure we're not cloning a clone */
   614 	if (pHandle->clone)
   615 	{
   616 		QHandle *pParent = &m_Handles[pHandle->clone];
   617 		return CloneHandle(pParent, pHandle->clone, newhandle, newOwner);
   618 	}
   619 
   620 	return CloneHandle(pHandle, index, newhandle, newOwner);
   621 }
   622 
   623 HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
   624 {
   625 	QHandleType *pType = &m_Types[pHandle->type];
   626 
   627 	if (pHandle->clone)
   628 	{
   629 		/* If we're a clone, decrease the parent reference count */
   630 		QHandle *pMaster;
   631 		unsigned int master;
   632 
   633 		/* Note that if we ever have per-handle security, we would need to re-check
   634 		* the access on this Handle. */
   635 		master = pHandle->clone;
   636 		pMaster = &m_Handles[master];
   637 
   638 		/* Release the clone now */
   639 		ReleasePrimHandle(index);
   640 
   641 		/* Decrement the master's reference count */
   642 		if (--pMaster->refcount == 0)
   643 		{
   644 			/* Type should be the same but do this anyway... */
   645 			pType = &m_Types[pMaster->type];
   646 			pMaster->is_destroying = true;
   647 			pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object);
   648 			ReleasePrimHandle(master);
   649 		}
   650 	} else if (pHandle->set == HandleSet_Identity) {
   651 		/* If we're an identity, skip all this stuff!
   652 		 * NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR
   653 		 */
   654 		ReleasePrimHandle(index);
   655 	} else {
   656 		/* Decrement, free if necessary */
   657 		if (--pHandle->refcount == 0)
   658 		{
   659 			pHandle->is_destroying = true;
   660 			pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object);
   661 			ReleasePrimHandle(index);
   662 		} else {
   663 			/* We must be cloned, so mark ourselves as freed */
   664 			pHandle->set = HandleSet_Freed;
   665 			/* Now, unlink us, so we're not being tracked by the owner */
   666 			if (pHandle->owner)
   667 			{
   668 				UnlinkHandleFromOwner(pHandle, index);
   669 			}
   670 		}
   671 	}
   672 
   673 	return HandleError_None;
   674 }
   675 
   676 HandleError HandleSystem::FreeHandle(Handle_t handle, const HandleSecurity *pSecurity)
   677 {
   678 	unsigned int index;
   679 	QHandle *pHandle;
   680 	HandleError err;
   681 	IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
   682 
   683 	if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
   684 	{
   685 		return err;
   686 	}
   687 
   688 	if (!CheckAccess(pHandle, HandleAccess_Delete, pSecurity))
   689 	{
   690 		return HandleError_Access;
   691 	}
   692 
   693 	if (pHandle->is_destroying)
   694 	{
   695 		/* Someone tried to free this recursively.  
   696 		 * We'll just ignore this safely.
   697 		 */
   698 		return HandleError_None;
   699 	}
   700 
   701 	return FreeHandle(pHandle, index);
   702 }
   703 
   704 HandleError HandleSystem::ReadHandle(Handle_t handle, HandleType_t type, const HandleSecurity *pSecurity, void **object)
   705 {
   706 	unsigned int index;
   707 	QHandle *pHandle;
   708 	HandleError err;
   709 	IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL;
   710 
   711 	if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None)
   712 	{
   713 		return err;
   714 	}
   715 
   716 	if (!CheckAccess(pHandle, HandleAccess_Read, pSecurity))
   717 	{
   718 		return HandleError_Access;
   719 	}
   720 
   721 	/* Check the type inheritance */
   722 	if (pHandle->type & HANDLESYS_SUBTYPE_MASK)
   723 	{
   724 		if (pHandle->type != type
   725 			&& (TypeParent(pHandle->type) != TypeParent(type)))
   726 		{
   727 			return HandleError_Type;
   728 		}
   729 	} else if (type) {
   730 		if (pHandle->type != type)
   731 		{
   732 			return HandleError_Type;
   733 		}
   734 	}
   735 
   736 	if (object)
   737 	{
   738 		/* if we're a clone, the rules change - object is ONLY in our reference */
   739 		if (pHandle->clone)
   740 		{
   741 			pHandle = &m_Handles[pHandle->clone];
   742 		}
   743 		*object = pHandle->object;
   744 	}
   745 
   746 	return HandleError_None;
   747 }
   748 
   749 void HandleSystem::UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index)
   750 {
   751 	/* Unlink us if necessary */
   752 	unsigned int ident_index;
   753 	if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None)
   754 	{
   755 		/* Uh-oh! */
   756 		assert(pHandle->owner == 0);
   757 		return;
   758 	}
   759 	/* Note that since 0 is an invalid handle, if any of these links are 0,
   760 	* the data can still be set.
   761 	*/
   762 	QHandle *pIdentity = &m_Handles[ident_index];
   763 
   764 	/* Unlink case: We're the head AND tail node */
   765 	if (index == pIdentity->ch_prev && index == pIdentity->ch_next)
   766 	{
   767 		pIdentity->ch_prev = 0;
   768 		pIdentity->ch_next = 0;
   769 	}
   770 	/* Unlink case: We're the head node */
   771 	else if (index == pIdentity->ch_prev) {
   772 		/* Link us to the next in the chain */
   773 		pIdentity->ch_prev = pHandle->ch_next;
   774 		/* Patch up the previous link */
   775 		m_Handles[pHandle->ch_next].ch_prev = 0;
   776 	}
   777 	/* Unlink case: We're the tail node */
   778 	else if (index == pIdentity->ch_next) {
   779 		/* Link us to the previous in the chain */
   780 		pIdentity->ch_next = pHandle->ch_prev;
   781 		/* Patch up the next link */
   782 		m_Handles[pHandle->ch_prev].ch_next = 0;
   783 	}
   784 	/* Unlink case: We're in the middle! */
   785 	else {
   786 		/* Patch the forward reference */
   787 		m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev;
   788 		/* Patch the backward reference */
   789 		m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next;
   790 	}
   791 
   792 	/* Lastly, decrease the reference count */
   793 	pIdentity->refcount--;
   794 }
   795 
   796 void HandleSystem::ReleasePrimHandle(unsigned int index)
   797 {
   798 	QHandle *pHandle = &m_Handles[index];
   799 	HandleSet set = pHandle->set;
   800 
   801 	if (pHandle->owner && (set != HandleSet_Identity))
   802 	{
   803 		UnlinkHandleFromOwner(pHandle, index);
   804 	}
   805 
   806 	/* Were we an identity ourself? */
   807 	QHandle *pLocal;
   808 	if (set == HandleSet_Identity)
   809 	{
   810 		/* Extra work to do.  We need to find everything connected to this identity and release it. */
   811 		unsigned int ch_index;
   812 #if defined _DEBUG
   813 		unsigned int old_index = 0;
   814 #endif
   815 		while ((ch_index = pHandle->ch_next) != 0)
   816 		{
   817 			pLocal = &m_Handles[ch_index];
   818 #if defined _DEBUG
   819 			assert(old_index != ch_index);
   820 			assert(pLocal->set == HandleSet_Used);
   821 			old_index = ch_index;
   822 #endif
   823 			FreeHandle(pLocal, ch_index);
   824 		}
   825 	}
   826 
   827 	pHandle->set = HandleSet_None;
   828 	m_Types[pHandle->type].opened--;
   829 	m_Handles[++m_FreeHandles].freeID = index;
   830 }
   831 
   832 bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident)
   833 {
   834 	if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE)
   835 	{
   836 		return false;
   837 	}
   838 
   839 	QHandleType *pType = &m_Types[type];
   840 
   841 	if (pType->typeSec.ident
   842 		&& pType->typeSec.ident != ident)
   843 	{
   844 		return false;
   845 	}
   846 
   847 	if (pType->dispatch == NULL)
   848 	{
   849 		return false;
   850 	}
   851 
   852 	/* Remove children if we have to */
   853 	if (!(type & HANDLESYS_SUBTYPE_MASK))
   854 	{
   855 		QHandleType *childType;
   856 		for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
   857 		{
   858 			childType = &m_Types[type + i];
   859 			if (childType->dispatch)
   860 			{
   861 				RemoveType(type + i, childType->typeSec.ident);
   862 			}
   863 		}
   864 		/* Link us into the free chain */
   865 		m_Types[++m_FreeTypes].freeID = type;
   866 	}
   867 
   868 	/* Invalidate the type now */
   869 	IHandleTypeDispatch *dispatch = pType->dispatch;
   870 	pType->dispatch = NULL;
   871 
   872 	/* Make sure nothing is using this type. */
   873 	if (pType->opened)
   874 	{
   875 		QHandle *pHandle;
   876 		for (unsigned int i=1; i<=m_HandleTail; i++)
   877 		{
   878 			pHandle = &m_Handles[i];
   879 			if (!pHandle->set || pHandle->type != type)
   880 			{
   881 				continue;
   882 			}
   883 			if (pHandle->clone)
   884 			{
   885 				/* Get parent */
   886 				QHandle *pOther = &m_Handles[pHandle->clone];
   887 				if (--pOther->refcount == 0)
   888 				{
   889 					/* Free! */
   890 					dispatch->OnHandleDestroy(type, pOther->object);
   891 					ReleasePrimHandle(pHandle->clone);
   892 				}
   893 				/* Unlink ourselves since we don't have a reference count */
   894 				ReleasePrimHandle(i);
   895 			} else {
   896 				/* If it's not a clone, we still have to check the reference count.
   897 				 * Either way, we'll be destroyed eventually because the handle types do not change.
   898 				 */
   899 				if (--pHandle->refcount == 0)
   900 				{
   901 					/* Free! */
   902 					dispatch->OnHandleDestroy(type, pHandle->object);
   903 					ReleasePrimHandle(i);
   904 				}
   905 			}
   906 			if (pType->opened == 0)
   907 			{
   908 				break;
   909 			}
   910 		}
   911 	}
   912 
   913 	return true;
   914 }
   915 
   916 bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess)
   917 {
   918 	if (pTypeAccess)
   919 	{
   920 		if (pTypeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
   921 		{
   922 			return false;
   923 		}
   924 		pTypeAccess->access[HTypeAccess_Create] = false;
   925 		pTypeAccess->access[HTypeAccess_Inherit] = false;
   926 		pTypeAccess->ident = NULL;
   927 	}
   928 
   929 	if (pHandleAccess)
   930 	{
   931 		if (pHandleAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION)
   932 		{
   933 			return false;
   934 		}
   935 
   936 		pHandleAccess->access[HandleAccess_Clone] = 0;
   937 		pHandleAccess->access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER;
   938 		pHandleAccess->access[HandleAccess_Read] = HANDLE_RESTRICT_IDENTITY;
   939 	}
   940 
   941 	return true;
   942 }
   943 
   944 bool HandleSystem::TryAndFreeSomeHandles()
   945 {
   946 	IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator();
   947 	IPlugin *highest_owner = NULL;
   948 	unsigned int highest_handle_count = 0;
   949 
   950 	/* Search all plugins */
   951 	while (pl_iter->MorePlugins())
   952 	{
   953 		IPlugin *plugin = pl_iter->GetPlugin();
   954 		IdentityToken_t *identity = plugin->GetIdentity();
   955 		unsigned int handle_count = 0;
   956 
   957 		if (identity == NULL)
   958 		{
   959 			continue;
   960 		}
   961 
   962 		/* Search all handles */
   963 		for (unsigned int i = 1; i <= m_HandleTail; i++)
   964 		{
   965 			if (m_Handles[i].set != HandleSet_Used)
   966 			{
   967 				continue;
   968 			}
   969 			if (m_Handles[i].owner == identity)
   970 			{
   971 				handle_count++;
   972 			}
   973 		}
   974 
   975 		if (handle_count > highest_handle_count)
   976 		{
   977 			highest_owner = plugin;
   978 			highest_handle_count = handle_count;
   979 		}
   980 
   981 		pl_iter->NextPlugin();
   982 	}
   983 
   984 	if (highest_owner == NULL || highest_handle_count == 0)
   985 	{
   986 		return false;
   987 	}
   988 
   989 	g_Logger.LogFatal("[SM] MEMORY LEAK DETECTED IN PLUGIN (file \"%s\")", highest_owner->GetFilename());
   990 	g_Logger.LogFatal("[SM] Reloading plugin to free %d handles.", highest_handle_count);
   991 	g_Logger.LogFatal("[SM] Contact the author(s) of this plugin to correct this error.", highest_handle_count);
   992 
   993 	highest_owner->GetBaseContext()->ThrowNativeErrorEx(SP_ERROR_MEMACCESS, "Memory leak");
   994 
   995 	return g_PluginSys.UnloadPlugin(highest_owner);
   996 }
   997 
   998 void HandleSystem::Dump(HANDLE_REPORTER rep)
   999 {
  1000 	unsigned int total_size = 0;
  1001 	rep("%-10.10s\t%-20.20s\t%-20.20s\t%-10.10s", "Handle", "Owner", "Type", "Memory");
  1002 	rep("--------------------------------------------------------------------------");
  1003 	for (unsigned int i = 1; i <= m_HandleTail; i++)
  1004 	{
  1005 		if (m_Handles[i].set != HandleSet_Used)
  1006 		{
  1007 			continue;
  1008 		}
  1009 		/* Get the index */
  1010 		unsigned int index = (m_Handles[i].serial << 16) | i;
  1011 		/* Determine the owner */
  1012 		const char *owner = "UNKNOWN";
  1013 		if (m_Handles[i].owner)
  1014 		{
  1015 			IdentityToken_t *pOwner = m_Handles[i].owner;
  1016 			if (pOwner == g_pCoreIdent)
  1017 			{
  1018 				owner = "CORE";
  1019 			}
  1020 			else if (pOwner == g_PluginSys.GetIdentity())
  1021 			{
  1022 				owner = "PLUGINSYS";
  1023 			}
  1024 			else
  1025 			{
  1026 				CExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner);
  1027 				if (ext)
  1028 				{
  1029 					owner = ext->GetFilename();
  1030 				}
  1031 				else
  1032 				{
  1033 					CPlugin *pPlugin = g_PluginSys.GetPluginFromIdentity(pOwner);
  1034 					if (pPlugin)
  1035 					{
  1036 						owner = pPlugin->GetFilename();
  1037 					}
  1038 				}
  1039 			}
  1040 		}
  1041 		else
  1042 		{
  1043 			owner = "NONE";
  1044 		}
  1045 		const char *type = "ANON";
  1046 		QHandleType *pType = &m_Types[m_Handles[i].type];
  1047 		unsigned int size = 0;
  1048 		if (pType->nameIdx != -1)
  1049 		{
  1050 			type = m_strtab->GetString(pType->nameIdx);
  1051 		}
  1052 		if (pType->dispatch->GetDispatchVersion() < HANDLESYS_MEMUSAGE_MIN_VERSION
  1053 			|| !pType->dispatch->GetHandleApproxSize(m_Handles[i].type, m_Handles[i].object, &size))
  1054 		{
  1055 			rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, "-1");
  1056 		}
  1057 		else
  1058 		{
  1059 			char buffer[32];
  1060 			UTIL_Format(buffer, sizeof(buffer), "%d", size);
  1061 			rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, buffer);
  1062 			total_size += size;
  1063 		}
  1064 	}
  1065 	rep("-- Approximately %d bytes of memory are in use by Handles.\n", total_size);
  1066 }
  1067