diff --git a/doc/RELEASE-NOTES b/doc/RELEASE-NOTES index 65c31211d..770a7cea8 100644 --- a/doc/RELEASE-NOTES +++ b/doc/RELEASE-NOTES @@ -286,6 +286,12 @@ It is therefore best to wait until beta1. You have been warned ;). in sendto_server(), then be sure to use %lld and cast the timestamp to 'long long' so that it is compatible with both *NIX and Windows. Example: sendnotice(sptr, "Timestamp is %lld", (long long)ts); +* EventAdd() changed the order of parameters and expects every_msec now + which specifies the time in milliseconds rather than seconds. This + allows for additional precision, or at least multiple calls per second. + The minimum allowed every_msec value is 100 at this time. + The prototype is now: EventAdd(Module *module, char *name, + vFP event, void *data, long every_msec, int count); * New HOOKTYPE_IS_HANDSHAKE_FINISHED. If a module returns 0 there, then the register_user() function will not be called and the user will not come online (yet). This is used by CAP and some other stuff. diff --git a/include/h.h b/include/h.h index f0db31ed2..eafa264ff 100644 --- a/include/h.h +++ b/include/h.h @@ -934,3 +934,4 @@ extern void _add_name_list(NameList **list, char *name); extern void _del_name_list(NameList **list, char *name); extern NameList *find_name_list(NameList *list, char *name); extern NameList *find_name_list_match(NameList *list, char *name); +extern int minimum_msec_since_last_run(struct timeval *tv_old, long minimum); diff --git a/include/modules.h b/include/modules.h index 9ce1894d2..eba569b4f 100644 --- a/include/modules.h +++ b/include/modules.h @@ -606,16 +606,17 @@ struct Module #define MOD_OPT_GLOBAL 0x0008 /* Module is required to be loaded globally (i.e. across the entire network) */ #define MOD_Dep(name, container,module) {#name, (vFP *) &container, module} -/* Event structs */ +/** Event structs */ struct Event { - Event *prev, *next; - char *name; - time_t every; - long howmany; - vFP event; - void *data; - time_t last; - Module *owner; + Event *prev; /**< Previous event (linked list) */ + Event *next; /**< Next event (linked list) */ + char *name; /**< Name of the event */ + long every_msec; /**< How often we should run this event */ + long count; /**< How many times this event should run (0 = infinite) */ + vFP event; /**< Actual function to call */ + void *data; /**< The data to pass in the function call */ + struct timeval last_run; /**< Last time this event ran */ + Module *owner; /**< To which module this event belongs */ }; #define EMOD_EVERY 0x0001 @@ -624,10 +625,11 @@ struct Event { #define EMOD_EVENT 0x0008 #define EMOD_DATA 0x0010 +/** event struct information, for EventMod() only - see Event for documentation */ struct EventInfo { int flags; - long howmany; - time_t every; + long count; + time_t every_msec; char *name; vFP event; void *data; @@ -639,14 +641,14 @@ extern MODVAR Hooktype Hooktypes[MAXCUSTOMHOOKS]; extern MODVAR Callback *Callbacks[MAXCALLBACKS], *RCallbacks[MAXCALLBACKS]; extern MODVAR ClientCapability *clicaps; -extern Event *EventAdd(Module *, char *name, long every, long howmany, vFP event, void *data); -extern Event *EventDel(Event *event); -extern Event *EventMarkDel(Event *event); -extern Event *EventFind(char *name); -extern int EventMod(Event *event, EventInfo *mods); -extern void DoEvents(void); -extern void EventStatus(Client *sptr); -extern void SetupEvents(void); +extern Event *EventAdd(Module *module, char *name, vFP event, void *data, long every_msec, int count); +extern Event *EventDel(Event *event); +extern Event *EventMarkDel(Event *event); +extern Event *EventFind(char *name); +extern int EventMod(Event *event, EventInfo *mods); +extern void DoEvents(void); +extern void EventStatus(Client *sptr); +extern void SetupEvents(void); extern void Module_Init(void); diff --git a/src/api-event.c b/src/api-event.c index a4a1c9c30..d5a6bc94d 100644 --- a/src/api-event.c +++ b/src/api-event.c @@ -29,23 +29,46 @@ MODVAR Event *events = NULL; extern EVENT(unrealdns_removeoldrecords); -Event *EventAdd(Module *module, char *name, long every, long howmany, - vFP event, void *data) +/** Add an event, a function that will run at regular intervals. + * @param module Module that this event belongs to + * @param name Name of the event + * @param event The EVENT(function) to be called + * @param data The data to be passed to the function (or just NULL) + * @param every_msec Every milliseconds the event will be called, but see notes. + * @param count After how many times we should stop calling this even (0 = infinite times) + * @returns an Event struct + * @notes UnrealIRCd will try to call the event every 'every_msec' milliseconds. + * However, in case of low traffic the minimum time is at least SOCKETLOOP_MAX_DELAY + * which is 250ms at the time of writing. Also, we reject any value below 100 msecs. + * The actual calling time will not be quicker than the specified every_msec but + * can be later, in case of high load, in very extreme cases even up to 1000 or 2000 + * msec later but that would be very unusual. Just saying, it's not a guarantee.. + */ +Event *EventAdd(Module *module, char *name, vFP event, void *data, long every_msec, int count) { Event *newevent; - if (!name || (every < 0) || (howmany < 0) || !event) + if (!name || (every_msec < 0) || (count < 0) || !event) { if (module) module->errorcode = MODERR_INVALID; return NULL; } + if (every_msec < 100) + { + ircd_log(LOG_ERROR, "[BUG] EventAdd() from module %s with suspiciously low every_msec value (%ld). " + "Note that it is in milliseconds now (1000 = 1 second)!", + module ? module->header->name : "???", + every_msec); + every_msec = 100; + } newevent = safe_alloc(sizeof(Event)); safe_strdup(newevent->name, name); - newevent->howmany = howmany; - newevent->every = every; + newevent->count = count; + newevent->every_msec = every_msec; newevent->event = event; newevent->data = data; - newevent->last = TStime(); + newevent->last_run.tv_sec = timeofday_tv.tv_sec; + newevent->last_run.tv_usec = timeofday_tv.tv_usec; newevent->owner = module; AddListItem(newevent,events); if (module) @@ -62,7 +85,7 @@ Event *EventAdd(Module *module, char *name, long every, long howmany, Event *EventMarkDel(Event *event) { - event->howmany = -1; + event->count = -1; return event; } @@ -116,9 +139,9 @@ int EventMod(Event *event, EventInfo *mods) } if (mods->flags & EMOD_EVERY) - event->every = mods->every; + event->every_msec = mods->every_msec; if (mods->flags & EMOD_HOWMANY) - event->howmany = mods->howmany; + event->count = mods->count; if (mods->flags & EMOD_NAME) safe_strdup(event->name, mods->name); if (mods->flags & EMOD_EVENT) @@ -137,16 +160,15 @@ void DoEvents(void) for (eventptr = events; eventptr; eventptr = eventptr->next) { - if (eventptr->howmany == -1) + if (eventptr->count == -1) goto freeit; - if ((eventptr->every == 0) || ((TStime() - eventptr->last) >= eventptr->every)) + if ((eventptr->every_msec == 0) || minimum_msec_since_last_run(&eventptr->last_run, eventptr->every_msec)) { - eventptr->last = TStime(); (*eventptr->event)(eventptr->data); - if (eventptr->howmany > 0) + if (eventptr->count > 0) { - eventptr->howmany--; - if (eventptr->howmany == 0) + eventptr->count--; + if (eventptr->count == 0) { freeit: temp.next = EventDel(eventptr); @@ -158,33 +180,15 @@ freeit: } } -void EventStatus(Client *sptr) -{ - Event *eventptr; - time_t now = TStime(); - - if (!events) - { - sendnotice(sptr, "*** No events"); - return; - } - for (eventptr = events; eventptr; eventptr = eventptr->next) - { - sendnotice(sptr, "*** Event %s: e/%lld h/%lld n/%lld l/%lld", - eventptr->name, (long long)eventptr->every, (long long)eventptr->howmany, - (long long)(now - eventptr->last), (long long)((eventptr->last + eventptr->every) - now)); - } -} - void SetupEvents(void) { /* Start events */ - EventAdd(NULL, "tunefile", 300, 0, save_tunefile, NULL); - EventAdd(NULL, "garbage", GARBAGE_COLLECT_EVERY, 0, garbage_collect, NULL); - EventAdd(NULL, "loop", 0, 0, loop_event, NULL); - EventAdd(NULL, "unrealdns_removeoldrecords", 15, 0, unrealdns_removeoldrecords, NULL); - EventAdd(NULL, "check_pings", 1, 0, check_pings, NULL); - EventAdd(NULL, "check_deadsockets", 1, 0, check_deadsockets, NULL); - EventAdd(NULL, "check_unknowns", 1, 0, check_unknowns, NULL); - EventAdd(NULL, "try_connections", 2, 0, try_connections, NULL); + EventAdd(NULL, "tunefile", save_tunefile, NULL, 300*1000, 0); + EventAdd(NULL, "garbage", garbage_collect, NULL, GARBAGE_COLLECT_EVERY*1000, 0); + EventAdd(NULL, "loop", loop_event, NULL, 1000, 0); + EventAdd(NULL, "unrealdns_removeoldrecords", unrealdns_removeoldrecords, NULL, 15000, 0); + EventAdd(NULL, "check_pings", check_pings, NULL, 1000, 0); + EventAdd(NULL, "check_deadsockets", check_deadsockets, NULL, 1000, 0); + EventAdd(NULL, "check_unknowns", check_unknowns, NULL, 1000, 0); + EventAdd(NULL, "try_connections", try_connections, NULL, 2000, 0); } diff --git a/src/conf.c b/src/conf.c index 4db422d90..3bb63601a 100644 --- a/src/conf.c +++ b/src/conf.c @@ -2584,16 +2584,16 @@ int config_run() long v; eInfo.flags = EMOD_EVERY; if (!THROTTLING_PERIOD) - v = 120; + v = 120000; else { v = THROTTLING_PERIOD/2; if (v > 5) - v = 5; /* accuracy, please */ + v = 5000; /* accuracy, please */ if (v < 1) - v = 1; /* duh */ + v = 1000; /* duh */ } - eInfo.every = v; + eInfo.every_msec = v; EventMod(EventFind("bucketcleaning"), &eInfo); } diff --git a/src/dns.c b/src/dns.c index 6d3f46bc3..1aacd1e8e 100644 --- a/src/dns.c +++ b/src/dns.c @@ -167,7 +167,7 @@ void init_resolver(int firsttime) } ares_set_socket_callback(resolver_channel, unrealdns_sock_create_cb, NULL); - unrealdns_timeout_hdl = EventAdd(NULL, "unrealdns_timeout", 0, 0, unrealdns_timeout, NULL); + unrealdns_timeout_hdl = EventAdd(NULL, "unrealdns_timeout", unrealdns_timeout, NULL, 500, 0); } void reinit_resolver(Client *sptr) diff --git a/src/hash.c b/src/hash.c index e5865e8d3..04477899e 100644 --- a/src/hash.c +++ b/src/hash.c @@ -898,11 +898,11 @@ void init_throttling() { v = THROTTLING_PERIOD/2; if (v > 5) - v = 5; /* accuracy, please */ + v = 5000; /* run at least every 5s */ if (v < 1) - v = 1; /* duh */ + v = 1000; /* run at max once every 1s */ } - EventAdd(NULL, "bucketcleaning", v, 0, e_clean_out_throttling_buckets, NULL); + EventAdd(NULL, "bucketcleaning", e_clean_out_throttling_buckets, NULL, v, 0); } uint64_t hash_throttling(char *ip) diff --git a/src/ircd.c b/src/ircd.c index ac5c31079..d9cfb58d0 100644 --- a/src/ircd.c +++ b/src/ircd.c @@ -735,11 +735,10 @@ void fix_timers(void) /* Reset all event timers */ for (e = events; e; e = e->next) { - if (e->last > TStime()) + if (e->last_run.tv_sec > TStime()) { - Debug((DEBUG_DEBUG, "fix_timers(): %s: e->last %ld -> %ld", - e->name, e->last, TStime()-1)); - e->last = TStime()-1; + e->last_run.tv_sec = TStime()-1; + e->last_run.tv_usec = 0; } } @@ -947,7 +946,9 @@ int InitUnrealIRCd(int argc, char *argv[]) struct rlimit corelim; #endif - timeofday = time(NULL); + gettimeofday(&timeofday_tv, NULL); + timeofday = timeofday_tv.tv_sec; + safe_strdup(configfile, CONFIGFILE); init_random(); /* needs to be done very early!! */ diff --git a/src/modules.c b/src/modules.c index faf6b584a..97e812f19 100644 --- a/src/modules.c +++ b/src/modules.c @@ -1449,7 +1449,7 @@ void special_delayed_unloading(void) { config_warn("Delaying module unloading of '%s' a few seconds...", m->header->name); m->flags |= MODFLAG_DELAYED; - EventAdd(NULL, "e_unload_module_delayed", 5, 1, e_unload_module_delayed, m->header->name); + EventAdd(NULL, "e_unload_module_delayed", e_unload_module_delayed, m->header->name, 5000, 1); } } } diff --git a/src/modules/chanmodes/floodprot.c b/src/modules/chanmodes/floodprot.c index 1d0456745..77293fdae 100644 --- a/src/modules/chanmodes/floodprot.c +++ b/src/modules/chanmodes/floodprot.c @@ -190,7 +190,7 @@ MOD_INIT() MOD_LOAD() { - EventAdd(modinfo->handle, "modef_event", 10, 0, modef_event, NULL); + EventAdd(modinfo->handle, "modef_event", modef_event, NULL, 10000, 0); floodprot_rehash_complete(); return MOD_SUCCESS; } diff --git a/src/modules/chanmodes/history.c b/src/modules/chanmodes/history.c index ddf510008..aa2d1bd47 100644 --- a/src/modules/chanmodes/history.c +++ b/src/modules/chanmodes/history.c @@ -103,7 +103,7 @@ MOD_INIT() MOD_LOAD() { - EventAdd(modinfo->handle, "history_clean", HISTORY_TIMER_EVERY, 0, history_clean, NULL); + EventAdd(modinfo->handle, "history_clean", history_clean, NULL, HISTORY_TIMER_EVERY*1000, 0); return MOD_SUCCESS; } diff --git a/src/modules/channeldb.c b/src/modules/channeldb.c index 87b8182ff..53aee6b05 100644 --- a/src/modules/channeldb.c +++ b/src/modules/channeldb.c @@ -97,7 +97,7 @@ MOD_INIT() MOD_LOAD() { - EventAdd(modinfo->handle, "channeldb_write_channeldb", CHANNELDB_SAVE_EVERY, 0, write_channeldb_evt, NULL); + EventAdd(modinfo->handle, "channeldb_write_channeldb", write_channeldb_evt, NULL, CHANNELDB_SAVE_EVERY*1000, 0); if (ModuleGetError(modinfo->handle) != MODERR_NOERROR) { config_error("A critical error occurred when loading module %s: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); diff --git a/src/modules/connthrottle.c b/src/modules/connthrottle.c index ab7d8a9ff..b44f8f1ae 100644 --- a/src/modules/connthrottle.c +++ b/src/modules/connthrottle.c @@ -111,7 +111,7 @@ MOD_INIT() MOD_LOAD() { - EventAdd(modinfo->handle, "connthrottle_evt", 1, 0, connthrottle_evt, NULL); + EventAdd(modinfo->handle, "connthrottle_evt", connthrottle_evt, NULL, 1000, 0); return MOD_SUCCESS; } diff --git a/src/modules/extbans/timedban.c b/src/modules/extbans/timedban.c index 29c9d6314..47287676c 100644 --- a/src/modules/extbans/timedban.c +++ b/src/modules/extbans/timedban.c @@ -89,7 +89,7 @@ ExtbanInfo extban; return MOD_FAILED; } - EventAdd(modinfo->handle, "timedban_timeout", TIMEDBAN_TIMER, 0, timedban_timeout, NULL); + EventAdd(modinfo->handle, "timedban_timeout", timedban_timeout, NULL, TIMEDBAN_TIMER*1000, 0); return MOD_SUCCESS; } diff --git a/src/modules/ident_lookup.c b/src/modules/ident_lookup.c index a2267cffe..3d07abe90 100644 --- a/src/modules/ident_lookup.c +++ b/src/modules/ident_lookup.c @@ -24,7 +24,7 @@ MOD_INIT() { MARK_AS_OFFICIAL_MODULE(modinfo); ModuleSetOptions(modinfo->handle, MOD_OPT_PERM, 1); /* needed? or not? */ - EventAdd(NULL, "check_ident_timeout", 1, 0, check_ident_timeout, NULL); + EventAdd(NULL, "check_ident_timeout", check_ident_timeout, NULL, 1000, 0); HookAdd(modinfo->handle, HOOKTYPE_IDENT_LOOKUP, 0, ident_lookup_connect); return MOD_SUCCESS; diff --git a/src/modules/jointhrottle.c b/src/modules/jointhrottle.c index 483d92195..23c41c6dc 100644 --- a/src/modules/jointhrottle.c +++ b/src/modules/jointhrottle.c @@ -100,7 +100,7 @@ MOD_INIT() MOD_LOAD() { - EventAdd(ModInfo->handle, "jointhrottle_cleanup_structs", 60, 0, jointhrottle_cleanup_structs, NULL); + EventAdd(ModInfo->handle, "jointhrottle_cleanup_structs", jointhrottle_cleanup_structs, NULL, 60000, 0); return MOD_SUCCESS; } diff --git a/src/modules/link-security.c b/src/modules/link-security.c index b50884fc6..57e41692a 100644 --- a/src/modules/link-security.c +++ b/src/modules/link-security.c @@ -81,7 +81,7 @@ MOD_LOAD() cap.parameter = link_security_capability_parameter; ClientCapabilityAdd(modinfo->handle, &cap, NULL); - EventAdd(modinfo->handle, "checklinksec", 2, 0, checklinksec, NULL); + EventAdd(modinfo->handle, "checklinksec", checklinksec, NULL, 2000, 0); checklinksec(NULL); return MOD_SUCCESS; } diff --git a/src/modules/list.c b/src/modules/list.c index aa59f4150..15f6ea197 100644 --- a/src/modules/list.c +++ b/src/modules/list.c @@ -90,7 +90,7 @@ MOD_INIT() } CommandAdd(modinfo->handle, MSG_LIST, cmd_list, MAXPARA, M_USER); - EventAdd(modinfo->handle, "send_queued_list_data", 1, 0, send_queued_list_data, NULL); + EventAdd(modinfo->handle, "send_queued_list_data", send_queued_list_data, NULL, 1500, 0); return MOD_SUCCESS; } diff --git a/src/modules/reputation.c b/src/modules/reputation.c index eb0ddfeee..f1d7ed960 100644 --- a/src/modules/reputation.c +++ b/src/modules/reputation.c @@ -175,9 +175,9 @@ MOD_LOAD() load_db(); if (reputation_starttime == 0) reputation_starttime = TStime(); - EventAdd(ModInf.handle, "delete_old_records", DELETE_OLD_EVERY, 0, delete_old_records, NULL); - EventAdd(ModInf.handle, "add_scores", BUMP_SCORE_EVERY, 0, add_scores, NULL); - EventAdd(ModInf.handle, "save_db", SAVE_DB_EVERY, 0, save_db_evt, NULL); + EventAdd(ModInf.handle, "delete_old_records", delete_old_records, NULL, DELETE_OLD_EVERY*1000, 0); + EventAdd(ModInf.handle, "add_scores", add_scores, NULL, BUMP_SCORE_EVERY*1000, 0); + EventAdd(ModInf.handle, "save_db", save_db_evt, NULL, SAVE_DB_EVERY*1000, 0); return MOD_SUCCESS; } diff --git a/src/modules/tkl.c b/src/modules/tkl.c index 8a7c9a462..ced8035f4 100644 --- a/src/modules/tkl.c +++ b/src/modules/tkl.c @@ -195,7 +195,7 @@ MOD_INIT() MOD_LOAD() { - EventAdd(modinfo->handle, "tklexpire", 5, 0, tkl_check_expire, NULL); + EventAdd(modinfo->handle, "tklexpire", tkl_check_expire, NULL, 5000, 0); return MOD_SUCCESS; } diff --git a/src/modules/tkldb.c b/src/modules/tkldb.c index e5a0c0dc5..40c5fa5a9 100644 --- a/src/modules/tkldb.c +++ b/src/modules/tkldb.c @@ -134,7 +134,7 @@ MOD_INIT() MOD_LOAD() { - EventAdd(modinfo->handle, "tkldb_write_tkldb", TKL_DB_SAVE_EVERY, 0, write_tkldb_evt, NULL); + EventAdd(modinfo->handle, "tkldb_write_tkldb", write_tkldb_evt, NULL, TKL_DB_SAVE_EVERY*1000, 0); if (ModuleGetError(modinfo->handle) != MODERR_NOERROR) { config_error("A critical error occurred when loading module %s: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); diff --git a/src/url.c b/src/url.c index e6b0c3b7b..8535651e2 100644 --- a/src/url.c +++ b/src/url.c @@ -371,7 +371,7 @@ void url_init(void) multihandle = curl_multi_init(); curl_multi_setopt(multihandle, CURLMOPT_SOCKETFUNCTION, url_socket_cb); - curl_socket_timeout_hdl = EventAdd(NULL, "curl_socket_timeout", 1, 0, curl_socket_timeout, NULL); + curl_socket_timeout_hdl = EventAdd(NULL, "curl_socket_timeout", curl_socket_timeout, NULL, 500, 0); } /*