Difference between revisions of "Handles (SourceMod Scripting)"

From AlliedModders Wiki
Jump to: navigation, search
m (Cloning Handles)
(Directories)
Line 115: Line 115:
 
{{HandleType|Directory|Yes|Yes|Core|files.inc}}
 
{{HandleType|Directory|Yes|Yes|Core|files.inc}}
 
Directory Handles are used for opening and enumerating the contents of a directory (folder) on the filesystem.
 
Directory Handles are used for opening and enumerating the contents of a directory (folder) on the filesystem.
 +
 +
==Plugins==
 +
{{HandleType|Plugin|No|No|Core|sourcemod.inc}}
 +
Plugin Handles are used for the unique identification of a Plugin.  They are owned by Core and cannot be cloned or closed by any other plugin or extension.  However, plugins can use Handles for certain natives which enumerate or retrieve information on plugins. 
 +
 +
Plugin Handles should not be cached globally, as plugins can become unloaded, and the Handle will be invalid.

Revision as of 12:47, 2 January 2007

Handles are a special data type used in SourceMod Scripting. They represent a single internal and unique object. For example, there are "File" Handles for files, and "Menu" Handles for menus, but they are both encapsulated under the "Handle" tag.

Handles are more than "pointers" from C or C++ because they have a reference count. This means they the number of copies floating around in memory is tracked. The actual internal object is not destroyed until each copy is also destroyed.

Since SourcePawn does not have Garbage Collection, Handles are a special way of intelligently allowing plugins and SourceMod to manage their own memory.

For more information on using Handles in the SourceMod API, see Handles (SourceMod API).

Opening Handles

Opening a Handle is usually done implicitly by a native function. For example, OpenDirectory opens a new Handle which is used in other Directory natives. This associated a type with the Handle. Trying to use a Directory Handle with any other non-Directory native will cause an error (HandleError_Type).

new Handle:hndl = OpenDirectory("addons/sourcemod/configs");

Closing Handles

Basic operation

Closing a Handle means seeing if the internal object can be removed from memory. For example, if a plugin creates an SQL connection, closing the Handle means closing and destroying the connection. This is almost always done using the CloseHandle() native function.

When to close

When a plugin unloads, all of its Handles are automatically destroyed. This does not mean, however, that closing Handles is optional. Consider the following code:

stock bool:IsFileInFolder(const String:path[], const String:file[])
{
	new String:path[PLATFORM_MAX_PATH];
	new FileType:type;
 
	new Handle:dir = OpenDirectory(path);
	if (dir == INVALID_HANDLE)
	{
		return false;
	}
 
	while (ReadDirEntry(dir, path, sizeof(path), type))
	{
		if (type == FileType_File && StrEqual(path, file))
		{
			return true;
		}
	}
 
	CloseHandle(dir);
 
	return false;
}

This function searches a directory for a single file. If this function were to omit the call to CloseHandle, it would leak memory every time it was called, and eventually the script would fill up with a large number of lingering Handles that were left open.

So, when do you close Handles? There are generally two rules of thumb to go by:

  • If the native uses terminology such as "Opens a [...]" or "Creates a [...]" or gives explicit directions for closing.
  • If the Handle represents a temporary object or piece of information, and you will no longer be using it.

Most of the time, you will be closing Handles.

When you can't close

There are a few Handle types that cannot be closed. The major one is the Handle which identifies a plugin. Plugins cannot unload themselves, and thus destroying their own Handles is not allowed.

Furthermore, a script cannot close a Handle that is owned by another plugin. This is for protection against API mistakes and accidental errors. For example, if a Handle is shared between two plugins, one accidental free could invalidate a Handle unexpectedly. To allow the sharing of Handles, see Cloning Handles.

Reference counters

Closing Handles does not always destroy the internal data. This is the case when Handles are cloned. Each clone adds a reference count to the Handle, and closing each clone removes one reference count. The original object is only actually destroyed once the Handle has no more reference counters.

Cloning Handles

As briefly described earlier, there is a problem when scripts try to share Handles. Imagine this scenario:

  • Plugin 'A' creates a Handle.
  • Plugin 'B' received the Handle.
  • Plugin 'A' is unloaded, and the Handle is destroyed.
  • Plugin 'B' tries to use the Handle.

To prevent this, the CloneHandle native is provided. This function returns a new Handle that is a "copy" of the original. While their values won't be equal, they contain the same internal data. The data itself will not be destroyed until each copy and the original are closed with CloseHandle. It does not matter what order they are freed in, however, each Handle can only be freed once.

There are two ways of cloning. A plugin can either clone a Handle itself, and become the owner, or it can explicitly set a new owner. The difference is one of API design. Example of the former:

new Handle:g_hSQL;
/**
 * The other plugin calling this function must pass his ID in,
 * but doesn't have to call CloneHandle()
 */
public Handle:GetGlobalSQL(Handle:otherPlugin)
{
   return CloneHandle(g_hSQL, otherPlugin);
}
 
/**
 * This code would appear in the other plugin
 */
new Handle:sql = GetGlobalSQL(myself);
/* ... */
CloseHandle(sql);

Example of the latter:

new Handle:g_hSQL;
/**
 * The calling plugin must call CloneHandle() himself.
 */
public Handle:GetGlobalSQL()
{
   return g_hSQL;
}
 
/**
 * This code would appear in the other plugin
 */
new Handle:sql = GetGlobalSQL();
if (sql != INVALID_HANDLE)
{
   sql = CloneHandle(sql);
}
CloseHandle(sql);

Handle Types

The following is a list of the known Handle types provided by SourceMod and some documentation on each.

Directories

Type: Directory
Closeable: Yes
Cloneable: Yes
API: Core
Found in: files.inc

Directory Handles are used for opening and enumerating the contents of a directory (folder) on the filesystem.

Plugins

Type: Plugin
Closeable: No
Cloneable: No
API: Core
Found in: sourcemod.inc

Plugin Handles are used for the unique identification of a Plugin. They are owned by Core and cannot be cloned or closed by any other plugin or extension. However, plugins can use Handles for certain natives which enumerate or retrieve information on plugins.

Plugin Handles should not be cached globally, as plugins can become unloaded, and the Handle will be invalid.