diff options
author | rburchell <rburchell@5417fbe8-f217-4b02-8779-1006273d7864> | 2009-02-16 22:59:59 +0000 |
---|---|---|
committer | rburchell <rburchell@5417fbe8-f217-4b02-8779-1006273d7864> | 2009-02-16 22:59:59 +0000 |
commit | ce3a04f0a879b7d3ebb120e69b87ed072dc376e2 (patch) | |
tree | 93c3555172e5eb68af1ef8c1727af78d9eb7162c | |
parent | f756f2b394fa286919e5d5c0f63d7be3824bfb34 (diff) |
Modevents, ported from insp. Typesafe (unlike the current ones), no double hashing overhead (faster), no useless memory allocations (copies of the args)
git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@2101 5417fbe8-f217-4b02-8779-1006273d7864
-rw-r--r-- | include/modules.h | 183 | ||||
-rw-r--r-- | src/modulemanager.cpp | 152 |
2 files changed, 335 insertions, 0 deletions
diff --git a/include/modules.h b/include/modules.h index 0e2ba9c7c..093bae576 100644 --- a/include/modules.h +++ b/include/modules.h @@ -41,6 +41,126 @@ #endif +/** + * This #define allows us to call a method in all + * loaded modules in a readable simple way, e.g.: + * 'FOREACH_MOD(I_OnConnect,OnConnect(user));' + */ +#define FOREACH_MOD(y,x) do { \ + std::vector<Module*>::iterator safei; \ + for (std::vector<Module*>::iterator _i = ServerInstance->Modules->EventHandlers[y].begin(); _i != ServerInstance->Modules->EventHandlers[y].end(); ) \ + { \ + safei = _i; \ + ++safei; \ + try \ + { \ + (*_i)->x ; \ + } \ + catch (CoreException& modexcept) \ + { \ + ServerInstance->Logs->Log("MODULE",DEFAULT,"Exception caught: %s",modexcept.GetReason()); \ + } \ + _i = safei; \ + } \ +} while (0); + +/** + * This #define allows us to call a method in all + * loaded modules in a readable simple way and pass + * an instance pointer to the macro. e.g.: + * 'FOREACH_MOD_I(Instance, OnConnect, OnConnect(user));' + */ +#define FOREACH_MOD_I(z,y,x) do { \ + std::vector<Module*>::iterator safei; \ + for (std::vector<Module*>::iterator _i = z->Modules->EventHandlers[y].begin(); _i != z->Modules->EventHandlers[y].end(); ) \ + { \ + safei = _i; \ + ++safei; \ + try \ + { \ + (*_i)->x ; \ + } \ + catch (CoreException& modexcept) \ + { \ + z->Logs->Log("MODULE",DEFAULT,"Exception caught: %s",modexcept.GetReason()); \ + } \ + _i = safei; \ + } \ +} while (0); + +/** + * This define is similar to the one above but returns a result in MOD_RESULT. + * The first module to return a nonzero result is the value to be accepted, + * and any modules after are ignored. + */ +#define FOREACH_RESULT(y,x) \ +do { \ + std::vector<Module*>::iterator safei; \ + MOD_RESULT = 0; \ + for (std::vector<Module*>::iterator _i = ServerInstance->Modules->EventHandlers[y].begin(); _i != ServerInstance->Modules->EventHandlers[y].end(); ) \ + { \ + safei = _i; \ + ++safei; \ + try \ + { \ + int res = (*_i)->x ; \ + if (res != 0) { \ + MOD_RESULT = res; \ + break; \ + } \ + } \ + catch (CoreException& modexcept) \ + { \ + ServerInstance->Logs->Log("MODULE",DEFAULT,"Exception caught: %s",modexcept.GetReason()); \ + } \ + _i = safei; \ + } \ +} while(0); + + +/** + * This define is similar to the one above but returns a result in MOD_RESULT. + * The first module to return a nonzero result is the value to be accepted, + * and any modules after are ignored. + */ +#define FOREACH_RESULT_I(z,y,x) \ +do { \ + std::vector<Module*>::iterator safei; \ + MOD_RESULT = 0; \ + for (std::vector<Module*>::iterator _i = z->Modules->EventHandlers[y].begin(); _i != z->Modules->EventHandlers[y].end(); ) \ + { \ + safei = _i; \ + ++safei; \ + try \ + { \ + int res = (*_i)->x ; \ + if (res != 0) { \ + MOD_RESULT = res; \ + break; \ + } \ + } \ + catch (CoreException& modexcept) \ + { \ + z->Logs->Log("MODULE",DEBUG,"Exception caught: %s",modexcept.GetReason()); \ + } \ + _i = safei; \ + } \ +} while (0); + + +/** Priority types which can be returned from Module::Prioritize() + */ +enum Priority { PRIORITY_FIRST, PRIORITY_DONTCARE, PRIORITY_LAST, PRIORITY_BEFORE, PRIORITY_AFTER }; + +/** Implementation-specific flags which may be set in Module::Implements() + */ +enum Implementation +{ + I_BEGIN, + I_END +}; + + /*************************************************************************/ #define CMD_HASH(x) (((x)[0]&31)<<5 | ((x)[1]&31)) /* Will gen a hash from a string :) */ #define MAX_CMD_HASH 1024 @@ -422,6 +542,11 @@ class CoreExport Module class CoreExport ModuleManager { public: + /** Event handler hooks. + * This needs to be public to be used by FOREACH_MOD and friends. + */ + static std::vector<Module *> EventHandlers[I_END]; + /** Load up a list of modules. * @param total_modules The number of modules to load * @param module_list The list of modules to load @@ -445,6 +570,64 @@ class CoreExport ModuleManager /** Run all pending module timer callbacks. */ static void RunCallbacks(); + + + + + + + + /** Change the priority of one event in a module. + * Each module event has a list of modules which are attached to that event type. If you wish to be called before or after other specific modules, you may use this + * method (usually within void Module::Prioritize()) to set your events priority. You may use this call in other methods too, however, this is not supported behaviour + * for a module. + * @param mod The module to change the priority of + * @param i The event to change the priority of + * @param s The state you wish to use for this event. Use one of + * PRIO_FIRST to set the event to be first called, PRIO_LAST to set it to be the last called, or PRIO_BEFORE and PRIO_AFTER + * to set it to be before or after one or more other modules. + * @param modules If PRIO_BEFORE or PRIO_AFTER is set in parameter 's', then this contains a list of one or more modules your module must be + * placed before or after. Your module will be placed before the highest priority module in this list for PRIO_BEFORE, or after the lowest + * priority module in this list for PRIO_AFTER. + * @param sz The number of modules being passed for PRIO_BEFORE and PRIO_AFTER. Defaults to 1, as most of the time you will only want to prioritize your module + * to be before or after one other module. + */ + static bool SetPriority(Module* mod, Implementation i, Priority s, Module** modules = NULL, size_t sz = 1); + + /** Change the priority of all events in a module. + * @param mod The module to set the priority of + * @param s The priority of all events in the module. + * Note that with this method, it is not possible to effectively use PRIO_BEFORE or PRIO_AFTER, you should use the more fine tuned + * SetPriority method for this, where you may specify other modules to be prioritized against. + */ + static bool SetPriority(Module* mod, Priority s); + + /** Attach an event to a module. + * You may later detatch the event with ModuleManager::Detach(). If your module is unloaded, all events are automatically detatched. + * @param i Event type to attach + * @param mod Module to attach event to + * @return True if the event was attached + */ + static bool Attach(Implementation i, Module* mod); + + /** Detatch an event from a module. + * This is not required when your module unloads, as the core will automatically detatch your module from all events it is attached to. + * @param i Event type to detach + * @param mod Module to detach event from + * @param Detach true if the event was detached + */ + static bool Detach(Implementation i, Module* mod); + + /** Detach all events from a module (used on unload) + * @param mod Module to detach from + */ + static void DetachAll(Module* mod); + + /** Attach an array of events to a module + * @param i Event types (array) to attach + * @param mod Module to attach events to + */ + static void Attach(Implementation* i, Module* mod, size_t sz); private: /** Call the module_delete function to safely delete the module * @param m the module to delete diff --git a/src/modulemanager.cpp b/src/modulemanager.cpp index 5354e4755..b80b9a8b9 100644 --- a/src/modulemanager.cpp +++ b/src/modulemanager.cpp @@ -11,6 +11,9 @@ #include "modules.h" #include "language.h" #include "version.h" +#include <algorithm> // std::find + +std::vector<Module *> ModuleManager::EventHandlers[I_END]; void ModuleManager::LoadModuleList(int total_modules, char **module_list) { @@ -292,3 +295,152 @@ void ModuleManager::DeleteModule(Module *m) alog("%s", dlerror()); } } + +bool ModuleManager::Attach(Implementation i, Module* mod) +{ + if (std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod) != EventHandlers[i].end()) + return false; + + EventHandlers[i].push_back(mod); + return true; +} + +bool ModuleManager::Detach(Implementation i, Module* mod) +{ + std::vector<Module *>::iterator x = std::find(EventHandlers[i].begin(), EventHandlers[i].end(), mod); + + if (x == EventHandlers[i].end()) + return false; + + EventHandlers[i].erase(x); + return true; +} + +void ModuleManager::Attach(Implementation* i, Module* mod, size_t sz) +{ + for (size_t n = 0; n < sz; ++n) + Attach(i[n], mod); +} + +void ModuleManager::DetachAll(Module* mod) +{ + for (size_t n = I_BEGIN + 1; n != I_END; ++n) + Detach((Implementation)n, mod); +} + +bool ModuleManager::SetPriority(Module* mod, Priority s) +{ + for (size_t n = I_BEGIN + 1; n != I_END; ++n) + SetPriority(mod, (Implementation)n, s); + + return true; +} + +bool ModuleManager::SetPriority(Module* mod, Implementation i, Priority s, Module** modules, size_t sz) +{ + /** To change the priority of a module, we first find its position in the vector, + * then we find the position of the other modules in the vector that this module + * wants to be before/after. We pick off either the first or last of these depending + * on which they want, and we make sure our module is *at least* before or after + * the first or last of this subset, depending again on the type of priority. + */ + size_t swap_pos = 0; + size_t source = 0; + bool swap = true; + bool found = false; + + /* Locate our module. This is O(n) but it only occurs on module load so we're + * not too bothered about it + */ + for (size_t x = 0; x != EventHandlers[i].size(); ++x) + { + if (EventHandlers[i][x] == mod) + { + source = x; + found = true; + break; + } + } + + /* Eh? this module doesnt exist, probably trying to set priority on an event + * theyre not attached to. + */ + if (!found) + return false; + + switch (s) + { + /* Dummy value */ + case PRIORITY_DONTCARE: + swap = false; + break; + /* Module wants to be first, sod everything else */ + case PRIORITY_FIRST: + swap_pos = 0; + break; + /* Module is submissive and wants to be last... awww. */ + case PRIORITY_LAST: + if (EventHandlers[i].empty()) + swap_pos = 0; + else + swap_pos = EventHandlers[i].size() - 1; + break; + /* Place this module after a set of other modules */ + case PRIORITY_AFTER: + { + /* Find the latest possible position */ + swap_pos = 0; + swap = false; + for (size_t x = 0; x != EventHandlers[i].size(); ++x) + { + for (size_t n = 0; n < sz; ++n) + { + if ((modules[n]) && (EventHandlers[i][x] == modules[n]) && (x >= swap_pos) && (source <= swap_pos)) + { + swap_pos = x; + swap = true; + } + } + } + } + break; + /* Place this module before a set of other modules */ + case PRIORITY_BEFORE: + { + swap_pos = EventHandlers[i].size() - 1; + swap = false; + for (size_t x = 0; x != EventHandlers[i].size(); ++x) + { + for (size_t n = 0; n < sz; ++n) + { + if ((modules[n]) && (EventHandlers[i][x] == modules[n]) && (x <= swap_pos) && (source >= swap_pos)) + { + swap = true; + swap_pos = x; + } + } + } + } + break; + } + + /* Do we need to swap? */ + if (swap && (swap_pos != source)) + { + /* Suggestion from Phoenix, "shuffle" the modules to better retain call order */ + int incrmnt = 1; + + if (source > swap_pos) + incrmnt = -1; + + for (unsigned int j = source; j != swap_pos; j += incrmnt) + { + if (( j + incrmnt > EventHandlers[i].size() - 1) || (j + incrmnt < 0)) + continue; + + std::swap(EventHandlers[i][j], EventHandlers[i][j+incrmnt]); + } + } + + return true; +} |