Skip to content

Initializers and finalizers

IllidanS4 edited this page Sep 10, 2019 · 1 revision

When writing modular systems, it is sometimes necessary to use a piece of code for construction of objects when the script is loaded and for cleanup of objects when the script is unloaded. While there are techniques such as lazy loading or amx_guard that solve some of the problems, sometimes there is no other choice.

Definition

An initializer is any public function whose name starts with _pp@on_init@. Similarly, a finalizer is any public function whose name starts with _pp@on_exit@. There are no restrictions placed on the rest of the name. As long as the name is unique, a module (within a single script) can define any number of initializers and finalizers and they will be called when appropriate:

forward _pp@on_init@module1();
public _pp@on_init@module1()
{
    print("Module loaded!");
}

forward _pp@on_exit@module1();
public _pp@on_exit@module1()
{
    print("Module unloaded!");
}

If PP_SYNTAX_ON_INIT or PP_SYNTAX_ON_EXIT is defined, a macro can be used to simplify the definitions of these functions:

pawn_on_init[module1]
{
    print("Module loaded!");
}

pawn_on_exit[module1]
{
    print("Module unloaded!");
}

Notice that module1 does not have to have a meaning on its own; it just serves as a suffix for the functions to be unique.

Callback order

An initializer is called before any other non-initializer function in the code, even before main. Conversely, a finalizer is called after all other functions before the script is unloaded and the corresponding AMX instance is destroyed.

Specifically, an initializer is called as soon as both of these conditions are true:

  • All native functions used by the script are registered.
  • All plugins that were loaded before PawnPlus have already initialized the script.

The first condition is necessary, as the code couldn't be executed before the functions are registered. The second condition allows the user to fix potential issues with other plugins that expect AmxLoad to be called before any native function, by placing PawnPlus after the conflicting plugins in the loading order.

To satisfy these conditions, the actual function that calls the initializers is either the hooked amx_Register called from any place, or AmxLoad in PawnPlus.

The actual order in which initializers are called is left to the AMX implementation to decide, they are simply called in whatever order they appear in. For the standard implementation of the AMX and the compiler, the public functions are lexicographically ordered, so adding 0s at the beginning of the name will make it be called earlier than other initializers.

Finalizers are called as part of the AMX cleanup procedure (amx_Cleanup), right before all plugins are informed via AmxUnload that the AMX is destroyed (because at this point, the AMX is already dead). They are called in the exact opposite order to initializers (from the top index to 0) so if two initializers create a dependency from the order they were called in (which shouldn't be caused in proper code), the corresponding finalizers (with the same unique part of their name) will not break the dependency.

Clone this wiki locally