Globus Common: Activation/Initialization Function Reference

Activation/Initialization Motivation

Module activation (initialization) and deactivation (shutdown) was a problem when building upon existing Globus modules. Specifically, when multiple clients of a module were brought together into a single executable, we were plagued with the question of which client should call the activation and deactivation functions for that module. Good software engineering practices would have each of the clients call these functions since no client should be aware of how the other clients are implemented. Unfortunately, this results in the functions being called multiple times, potentially violating assumptions made by the module's implementor. To address this issue, we created the Globus Activation API.

Activation/Initialization Solution

This solution was designed to have a minimal impact on the implementation of our current modules. To accomplish this, we defined the following set of requirements for a module and the client software calling its activation and deactivation functions.

(1) A module's activation function must only be called when the module is inactive. In other words, it may only be called when that module has either not been previously activated or has been explicitly been deactivated by calling it's deactivation function.

(2) A module's deactivation function must not be called when the module is still be actively used by one or more clients. (3) A module must allow itself to be reactivated after having been previously been deactivated. While we would prefer that all Globus modules directly support reactivation, we recognize that this may not always be possible. For example, GRAM Myjob may depend on a version of MPI which does not support the calling of MPI_Init() after MPI_Terminate() has been called. Obviously, we have no control over the MPI implementation and therefore have no means of correcting it in a timely fashion. Modules which are unable to directly support reactivation may be updated as follows. First, the activation function should include an "already activated" flag which is used to conditionally execute the activation code. Second, the contents of the deactivation function should be moved to a new function which can be called when the process is about to exit. (Mechanisms for registering these functions will be defined later.) (4) A function which performs deactivation of all currently active modules must call the deactivation function of those modules in an order which honors their inter-dependencies (example: GRAM must be deactivated before Nexus and DUROC before GRAM). However, these restrictions make it impossible for well engineered clients to activate and deactivate modules directly. We therefore defined a module activation / deactivation API. This API ensures that clients may activate and deactivate modules of interest without concern for what other clients may have already done. This API contains three functions: activate(), deactivate() and deactivate_all(). The activate() function invokes the specified module's activation function if that module has not already been activated. The module's activation function may in turn call activate() to activate any modules which it depends upon. By allowing activate() to be called recursively, module dependencies can be tracked, allowing the system to ensure that modules are deactivated in the correct order before process termination. The deactivate() function invokes the specified module's deactivation function, which in turn may call globus_module_deactivate() to deactivate modules it depends upon. Again, recursion is necessary to allow dependencies to be expressed.

Finally, the deactivate_all() function invokes the deactivation functions for all of the currently activate modules. Inter-dependencies, which are recorded during activation, are honored, ensuring that all clients of a module are deactivated before the module itself is deactivated.

Activation/Initialization User API

int
globus_module_activate
(
          globus_module_descriptor_t      *     module_descriptor);

globus_module_activate() calls the activation function for the specified module if that module is currently inactive. The function's return value is GLOBUS_SUCCESS if activation was successful or the module was already active.


int
globus_module_deactivate
(
          globus_module_descriptor_t       *     module_descriptor);

globus_module_deactivate() calls the deactivation functions for the specified module if this is the last client using that module. The function's return value is GLOBUS_SUCCESS if deactivation was successful. Note: the return value is only intended to be advisory information for the user.


int
globus_module_deactivate_all
(
          void);

globus_module_deactivate_all() calls the deactivation function for each active module currently registered with the activation / deactivation module. The function's return value is GLOBUS_SUCCESS if all modules were successfully deactivated and GLOBUS_FAILURE if one or modules failed to deactivate.

Activation/Initialization Implementor's API

typedef globus_bool_t(*globus_module_activation_func_t)(void); typedef globus_bool_t (*globus_module_deactivation_func_t)(void); typedef void (*globus_module_atexit_func_t)(void); typedef struct {
          char * module_name;
          globus_module_activation_func_t activation_func;     
          globus_module_deactivation_func_t deactivation_func;
          globus_module_atexit_func_t atexit_func; }

globus_module_descriptor_t; Each module within the Globus system is responsible for providing a reference to a defined module descriptor. This is typically accomplished by adding the following two lines to the module's main header file extern globus_module_descriptor_t globus_i_<module_name>_module;
#define GLOBUS_<module_name>_MODULE (&globus_i_<module_name>_module) and then inserting the variable definition in the module's main source file. The module_name field is completely optional. It may contain either a pointer to a string which textually names the module, or GLOBUS_NULL. The activation_func field must contain either a pointer to the module's activation function or GLOBUS_NULL. A value of GLOBUS_NULL should be used when the module does not require any explicit activation or deactivation. If activation_func is set to GLOBUS_NULL, then the remaining fields in the descriptor will be ignored. The deactivation_func field may contain either a pointer to the module's deactivation function or GLOBUS_NULL.

The atexit_func field may contain either a pointer to a function which might be called when the process is about to exit, or GLOBUS_NULL. Note: the atexit function will only be called if the underlying system supports this functionality (atexit() or on_exit()).