2 * IRC - Internet Relay Chat, ircd/ircd_events.c
3 * Copyright (C) 2001 Kevin L. Mitchell <klmitch@mit.edu>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 1, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * @brief Implementation of event loop mid-layer.
25 #include "ircd_events.h"
28 #include "ircd_alloc.h"
30 #include "ircd_snprintf.h"
33 /* #include <assert.h> -- Now using assert in ircd_log.h */
38 #define SIGS_PER_SOCK 10 /**< number of signals to process per socket
42 extern struct Engine engine_kqueue;
43 #define ENGINE_KQUEUE &engine_kqueue,
45 /** Address of kqueue engine (if used). */
47 #endif /* USE_KQUEUE */
50 extern struct Engine engine_devpoll;
51 #define ENGINE_DEVPOLL &engine_devpoll,
53 /** Address of /dev/poll engine (if used). */
54 #define ENGINE_DEVPOLL
55 #endif /* USE_DEVPOLL */
58 extern struct Engine engine_epoll;
59 #define ENGINE_EPOLL &engine_epoll,
61 /** Address of epoll engine (if used). */
63 #endif /* USE_EPOLL */
66 extern struct Engine engine_poll;
67 /** Address of fallback (poll) engine. */
68 #define ENGINE_FALLBACK &engine_poll,
70 extern struct Engine engine_select;
71 /** Address of fallback (select) engine. */
72 #define ENGINE_FALLBACK &engine_select,
75 /** list of engines to try */
76 static const struct Engine *evEngines[] = {
84 /** Signal routines pipe data.
85 * This is used if an engine does not implement signal handling itself
86 * (when Engine::eng_signal is NULL).
89 int fd; /**< signal routine's fd */
90 struct Socket sock; /**< and its struct Socket */
93 /** All the thread info */
95 struct Generators gens; /**< List of all generators */
96 struct Event* events_free; /**< struct Event free list */
97 unsigned int events_alloc; /**< count of allocated struct Events */
98 const struct Engine* engine; /**< core engine being used */
100 struct GenHeader* genq_head; /**< head of generator event queue */
101 struct GenHeader* genq_tail; /**< tail of generator event queue */
102 unsigned int genq_count; /**< count of generators on queue */
112 /** Initialize a struct GenHeader.
113 * @param[in,out] gen GenHeader to initialize.
114 * @param[in] call Callback for generated events.
115 * @param[in] data User data pointer.
116 * @param[in] next Pointer to next generator.
117 * @param[in,out] prev_p Pointer to previous pointer for this list.
120 gen_init(struct GenHeader* gen, EventCallBack call, void* data,
121 struct GenHeader* next, struct GenHeader** prev_p)
126 gen->gh_prev_p = prev_p;
133 gen->gh_flags = GEN_ACTIVE;
137 gen->gh_engdata.ed_int = 0;
139 if (prev_p) { /* Going to link into list? */
140 if (next) /* do so */
141 next->gh_prev_p = &gen->gh_next;
146 /** Execute an event.
147 * Optimizations should inline this.
148 * @param[in] event Event to execute.
151 event_execute(struct Event* event)
154 assert(0 == event->ev_prev_p); /* must be off queue first */
155 assert(event->ev_gen.gen_header->gh_flags & GEN_ACTIVE);
157 if (event->ev_type == ET_DESTROY) /* turn off active flag *before* destroy */
158 event->ev_gen.gen_header->gh_flags &= ~GEN_ACTIVE;
159 if (event->ev_type == ET_ERROR) /* turn on error flag before callback */
160 event->ev_gen.gen_header->gh_flags |= GEN_ERROR;
162 (*event->ev_gen.gen_header->gh_call)(event); /* execute the event */
164 /* The logic here is very careful; if the event was an ET_DESTROY,
165 * then we must assume the generator is now invalid; fortunately, we
166 * don't need to do anything to it if so. Otherwise, we decrement
167 * the reference count; if reference count goes to zero, AND we need
168 * to destroy the generator, THEN we generate a DESTROY event.
170 if (event->ev_type != ET_DESTROY)
171 gen_ref_dec(event->ev_gen.gen_header);
173 event->ev_gen.gen_header = 0; /* clear event data */
174 event->ev_type = ET_DESTROY;
176 event->ev_next = evInfo.events_free; /* add to free list */
177 evInfo.events_free = event;
180 #ifndef IRCD_THREADED
181 /** we synchronously execute the event when not threaded */
182 #define event_add(event) \
184 struct Event* _ev = (event); \
186 _ev->ev_prev_p = 0; \
187 event_execute(_ev); \
191 /** Add an event to the work queue.
192 * @param[in] event Event to enqueue.
194 /* This is just a placeholder; don't expect ircd to be threaded soon */
195 /* There should be locks all over the place in here */
197 event_add(struct Event* event)
199 struct GenHeader* gen;
203 gen = event->ev_gen.gen_header;
205 /* First, place event on generator's event queue */
208 assert(0 != gen->gh_tail);
210 event->ev_prev_p = &gen->gh_tail->ev_next;
211 gen->gh_tail->ev_next = event;
212 gen->gh_tail = event;
213 } else { /* queue was empty */
214 assert(0 == gen->gh_tail);
216 event->ev_prev_p = &gen->gh_head;
217 gen->gh_head = event;
218 gen->gh_tail = event;
221 /* Now, if the generator isn't on the queue yet... */
222 if (!gen->gh_qprev_p) {
224 if (evInfo.genq_head) {
225 assert(0 != evInfo.genq_tail);
227 gen->gh_qprev_p = &evInfo.genq_tail->gh_qnext;
228 evInfo.genq_tail->gh_qnext = gen;
229 evInfo.genq_tail = gen;
230 } else { /* queue was empty */
231 assert(0 == evInfo.genq_tail);
233 gen->gh_qprev_p = &evInfo.genq_head;
234 evInfo.genq_head = gen;
235 evInfo.genq_tail = gen;
238 /* We'd also have to signal the work crew here */
241 #endif /* IRCD_THREADED */
243 /** Place a timer in the correct spot on the queue.
244 * @param[in] timer Timer to enqueue.
247 timer_enqueue(struct Timer* timer)
249 struct GenHeader** ptr_p;
252 assert(0 == timer->t_header.gh_prev_p); /* not already on queue */
253 assert(timer->t_header.gh_flags & GEN_ACTIVE); /* timer is active */
255 /* Calculate expire time */
256 switch (timer->t_type) {
257 case TT_ABSOLUTE: /* no need to consider it relative */
258 timer->t_expire = timer->t_value;
261 case TT_RELATIVE: case TT_PERIODIC: /* relative timer */
262 timer->t_expire = timer->t_value + CurrentTime;
266 /* Find a slot to insert timer */
267 for (ptr_p = &evInfo.gens.g_timer; ;
268 ptr_p = &(*ptr_p)->gh_next)
269 if (!*ptr_p || timer->t_expire < ((struct Timer*)*ptr_p)->t_expire)
272 /* link it in the right place */
273 timer->t_header.gh_next = *ptr_p;
274 timer->t_header.gh_prev_p = ptr_p;
276 (*ptr_p)->gh_prev_p = &timer->t_header.gh_next;
277 *ptr_p = &timer->t_header;
280 /** &Signal handler for writing signal notification to pipe.
281 * @param[in] sig Signal number that just happened.
284 signal_handler(int sig)
288 assert(sigInfo.fd >= 0);
290 c = (unsigned char) sig; /* only write 1 byte to identify sig */
292 write(sigInfo.fd, &c, 1);
295 /** Callback for signal "socket" (really pipe) events.
296 * @param[in] event Event activity descriptor.
299 signal_callback(struct Event* event)
301 unsigned char sigstr[SIGS_PER_SOCK];
303 struct GenHeader* ptr;
305 assert(event->ev_type == ET_READ); /* readable events only */
307 n_sigs = read(event->ev_gen.gen_socket->s_fd, sigstr, sizeof(sigstr));
309 for (i = 0; i < n_sigs; i++) {
310 sig = (int) sigstr[i]; /* get signal */
312 for (ptr = evInfo.gens.g_signal; ptr;
314 if (((struct Signal*)ptr)->sig_signal == sig) /* find its descriptor... */
318 event_generate(ET_SIGNAL, ptr, sig); /* generate signal event */
322 /** Remove a generator from its queue.
323 * @param[in] arg Pointer to a GenHeader to dequeue.
326 gen_dequeue(void* arg)
328 struct GenHeader* gen = (struct GenHeader*) arg;
330 if (gen->gh_next) /* clip it out of the list */
331 gen->gh_next->gh_prev_p = gen->gh_prev_p;
333 *gen->gh_prev_p = gen->gh_next;
335 gen->gh_next = 0; /* mark that it's not in the list anymore */
339 /** Initializes the event system.
340 * @param[in] max_sockets Maximum number of sockets to support.
343 event_init(int max_sockets)
347 for (i = 0; evEngines[i]; i++) { /* look for an engine... */
348 assert(0 != evEngines[i]->eng_name);
349 assert(0 != evEngines[i]->eng_init);
351 if ((*evEngines[i]->eng_init)(max_sockets))
352 break; /* Found an engine that'll work */
355 assert(0 != evEngines[i]);
357 evInfo.engine = evEngines[i]; /* save engine */
359 if (!evInfo.engine->eng_signal) { /* engine can't do signals */
361 log_write(LS_SYSTEM, L_CRIT, 0, "Failed to open signal pipe");
365 sigInfo.fd = p[1]; /* write end of pipe */
366 socket_add(&sigInfo.sock, signal_callback, 0, SS_NOTSOCK,
367 SOCK_EVENT_READABLE, p[0]); /* read end of pipe */
371 /** Do the event loop. */
375 assert(0 != evInfo.engine);
376 assert(0 != evInfo.engine->eng_loop);
378 (*evInfo.engine->eng_loop)(&evInfo.gens);
381 /** Generate an event and add it to the queue (or execute it).
382 * @param[in] type Type of event to generate.
383 * @param[in] arg Pointer to an event generator (GenHeader).
384 * @param[in] data Extra data for event.
387 event_generate(enum EventType type, void* arg, int data)
390 struct GenHeader* gen = (struct GenHeader*) arg;
394 /* don't create events (other than ET_DESTROY) for destroyed generators */
395 if (type != ET_DESTROY && (gen->gh_flags & GEN_DESTROY))
398 Debug((DEBUG_LIST, "Generating event type %s for generator %p (%s)",
399 event_to_name(type), gen, gen_flags(gen->gh_flags)));
401 if ((ptr = evInfo.events_free))
402 evInfo.events_free = ptr->ev_next; /* pop one off the freelist */
403 else { /* allocate another structure */
404 ptr = (struct Event*) MyMalloc(sizeof(struct Event));
405 evInfo.events_alloc++; /* count of allocated events */
408 ptr->ev_type = type; /* Record event type */
411 ptr->ev_gen.gen_header = (struct GenHeader*) gen;
412 ptr->ev_gen.gen_header->gh_ref++;
414 event_add(ptr); /* add event to queue */
418 /* Try to verify the timer list */
423 struct Timer** ptr_p = &evInfo.gens.g_timer;
426 for (ptr = evInfo.gens.g_timer; ptr;
427 ptr = (struct Timer*) ptr->t_header.gh_next) {
428 /* verify timer is supposed to be in the list */
429 assert(ptr->t_header.gh_prev_p);
430 /* verify timer is correctly ordered */
431 assert((struct Timer**) ptr->t_header.gh_prev_p == ptr_p);
432 /* verify timer is active */
433 assert(ptr->t_header.gh_flags & GEN_ACTIVE);
434 /* verify timer ordering is correct */
435 assert(lasttime <= ptr->t_expire);
437 lasttime = ptr->t_expire; /* store time for ordering check */
438 ptr_p = (struct Timer**) &ptr->t_header.gh_next; /* store prev pointer */
443 /** Initialize a timer structure.
444 * @param[in,out] timer Timer to initialize.
445 * @return The pointer \a timer.
448 timer_init(struct Timer* timer)
450 gen_init(&timer->t_header, 0, 0, 0, 0);
452 timer->t_header.gh_flags = 0; /* turn off active flag */
454 return timer; /* convenience return */
457 /** Add a timer to be processed.
458 * @param[in] timer Timer to add.
459 * @param[in] call Callback for when the timer expires or is removed.
460 * @param[in] data User data pointer for the timer.
461 * @param[in] type Timer type.
462 * @param[in] value Timer expiration, duration or interval (per \a type).
465 timer_add(struct Timer* timer, EventCallBack call, void* data,
466 enum TimerType type, time_t value)
471 Debug((DEBUG_LIST, "Adding timer %p; time out %Tu (type %s)", timer, value,
472 timer_to_name(type)));
474 /* initialize a timer... */
475 timer->t_header.gh_flags |= GEN_ACTIVE;
476 if (timer->t_header.gh_flags & GEN_MARKED)
477 timer->t_header.gh_flags |= GEN_READD;
479 timer->t_header.gh_ref = 0;
480 timer->t_header.gh_call = call;
481 timer->t_header.gh_data = data;
483 timer->t_type = type;
484 timer->t_value = value;
487 if (!(timer->t_header.gh_flags & GEN_MARKED))
488 timer_enqueue(timer); /* and enqueue it */
491 /** Remove a timer from the processing queue.
492 * @param[in] timer Timer to remove.
495 timer_del(struct Timer* timer)
499 timer->t_header.gh_flags &= ~GEN_READD;
501 if (timer->t_header.gh_flags & GEN_MARKED)
502 return; /* timer is being used */
504 Debug((DEBUG_LIST, "Deleting timer %p (type %s)", timer,
505 timer_to_name(timer->t_type)));
508 event_generate(ET_DESTROY, timer, 0);
511 /** Change the time a timer expires.
512 * @param[in] timer Timer to update.
513 * @param[in] type New timer type.
514 * @param[in] value New timer expiration value.
517 timer_chg(struct Timer* timer, enum TimerType type, time_t value)
521 assert(TT_PERIODIC != timer->t_type);
522 assert(TT_PERIODIC != type);
524 Debug((DEBUG_LIST, "Changing timer %p from type %s timeout %Tu to type %s "
525 "timeout %Tu", timer, timer_to_name(timer->t_type), timer->t_value,
526 timer_to_name(type), value));
528 timer->t_type = type; /* Set the new type and value */
529 timer->t_value = value;
532 /* If the timer expiration callback tries to change the timer
533 * expiration, flag the timer but do not dequeue it yet.
535 if (timer->t_header.gh_flags & GEN_MARKED)
537 timer->t_header.gh_flags |= GEN_READD;
540 gen_dequeue(timer); /* remove the timer from the queue */
541 timer_enqueue(timer); /* re-queue the timer */
544 /** Execute all expired timers. */
550 /* go through queue... */
551 while ((ptr = (struct Timer*)evInfo.gens.g_timer)) {
552 if (CurrentTime < ptr->t_expire)
553 break; /* processed all pending timers */
555 gen_dequeue(ptr); /* must dequeue timer here */
556 ptr->t_header.gh_flags |= (GEN_MARKED |
557 (ptr->t_type == TT_PERIODIC ? GEN_READD : 0));
559 event_generate(ET_EXPIRE, ptr, 0); /* generate expire event */
561 ptr->t_header.gh_flags &= ~GEN_MARKED;
563 if (!(ptr->t_header.gh_flags & GEN_READD)) {
564 Debug((DEBUG_LIST, "Destroying timer %p", ptr));
565 event_generate(ET_DESTROY, ptr, 0);
567 Debug((DEBUG_LIST, "Re-enqueuing timer %p", ptr));
568 timer_enqueue(ptr); /* re-queue timer */
569 ptr->t_header.gh_flags &= ~GEN_READD;
574 /** Adds a signal to the event callback system.
575 * @param[in] signal Signal event generator to use.
576 * @param[in] call Callback function to use.
577 * @param[in] data User data pointer for generator.
578 * @param[in] sig Signal number to hook.
581 signal_add(struct Signal* signal, EventCallBack call, void* data, int sig)
583 struct sigaction act;
587 assert(0 != evInfo.engine);
590 gen_init(&signal->sig_header, call, data,
591 evInfo.gens.g_signal,
592 &evInfo.gens.g_signal);
594 signal->sig_signal = sig;
596 if (evInfo.engine->eng_signal)
597 (*evInfo.engine->eng_signal)(signal); /* tell engine */
599 act.sa_handler = signal_handler; /* set up signal handler */
601 sigemptyset(&act.sa_mask);
602 sigaction(sig, &act, 0);
606 /** Adds a socket to the event system.
607 * @param[in] sock Socket event generator to use.
608 * @param[in] call Callback function to use.
609 * @param[in] data User data pointer for the generator.
610 * @param[in] state Current socket state.
611 * @param[in] events Event interest mask for connected or connectionless sockets.
612 * @param[in] fd &Socket file descriptor.
613 * @return Zero on error, non-zero on success.
616 socket_add(struct Socket* sock, EventCallBack call, void* data,
617 enum SocketState state, unsigned int events, int fd)
622 assert(0 != evInfo.engine);
623 assert(0 != evInfo.engine->eng_add);
626 gen_init(&sock->s_header, call, data,
627 evInfo.gens.g_socket,
628 &evInfo.gens.g_socket);
630 sock->s_state = state;
631 sock->s_events = events & SOCK_EVENT_MASK;
634 return (*evInfo.engine->eng_add)(sock); /* tell engine about it */
637 /** Deletes (or marks for deletion) a socket generator.
638 * @param[in] sock Event generator to clear.
641 socket_del(struct Socket* sock)
644 assert(!(sock->s_header.gh_flags & GEN_DESTROY));
645 assert(0 != evInfo.engine);
646 assert(0 != evInfo.engine->eng_closing);
648 /* tell engine socket is going away */
649 (*evInfo.engine->eng_closing)(sock);
651 sock->s_header.gh_flags |= GEN_DESTROY;
653 if (!sock->s_header.gh_ref) { /* not in use; destroy right now */
655 event_generate(ET_DESTROY, sock, 0);
659 /** Sets the socket state to something else.
660 * @param[in] sock Socket generator to update.
661 * @param[in] state New socket state.
664 socket_state(struct Socket* sock, enum SocketState state)
667 assert(0 != evInfo.engine);
668 assert(0 != evInfo.engine->eng_state);
670 /* assertions for invalid socket state transitions */
671 assert(sock->s_state != state); /* not changing states ?! */
672 assert(sock->s_state != SS_LISTENING); /* listening socket to...?! */
673 assert(sock->s_state != SS_CONNECTED); /* connected socket to...?! */
674 /* connecting socket now connected */
675 assert(sock->s_state != SS_CONNECTING || state == SS_CONNECTED);
676 /* unconnected datagram socket now connected */
677 assert(sock->s_state != SS_DATAGRAM || state == SS_CONNECTDG);
678 /* connected datagram socket now unconnected */
679 assert(sock->s_state != SS_CONNECTDG || state == SS_DATAGRAM);
681 /* Don't continue if an error occurred or the socket got destroyed */
682 if (sock->s_header.gh_flags & (GEN_DESTROY | GEN_ERROR))
685 /* tell engine we're changing socket state */
686 (*evInfo.engine->eng_state)(sock, state);
688 sock->s_state = state; /* set new state */
691 /** Sets the events a socket's interested in.
692 * @param[in] sock Socket generator to update.
693 * @param[in] events New event interest mask.
696 socket_events(struct Socket* sock, unsigned int events)
698 unsigned int new_events = 0;
701 assert(0 != evInfo.engine);
702 assert(0 != evInfo.engine->eng_events);
704 /* Don't continue if an error occurred or the socket got destroyed */
705 if (sock->s_header.gh_flags & (GEN_DESTROY | GEN_ERROR))
708 switch (events & SOCK_ACTION_MASK) {
709 case SOCK_ACTION_SET: /* set events to given set */
710 new_events = events & SOCK_EVENT_MASK;
713 case SOCK_ACTION_ADD: /* add some events */
714 new_events = sock->s_events | (events & SOCK_EVENT_MASK);
717 case SOCK_ACTION_DEL: /* remove some events */
718 new_events = sock->s_events & ~(events & SOCK_EVENT_MASK);
722 if (sock->s_events == new_events)
723 return; /* no changes have been made */
725 /* tell engine about event mask change */
726 (*evInfo.engine->eng_events)(sock, new_events);
728 sock->s_events = new_events; /* set new events */
731 /** Returns the current engine's name for informational purposes.
732 * @return Pointer to a static buffer containing the engine name.
737 assert(0 != evInfo.engine);
738 assert(0 != evInfo.engine->eng_name);
740 return evInfo.engine->eng_name;
744 /* These routines pretty-print names for states and types for debug printing */
746 /** Declares a struct variable containing name(s) and value(s) of \a TYPE. */
753 /** Declares an element initialize for an NS() struct. */
754 #define NM(name) { #name, name }
756 /** Declares end of an NS() struct array. */
759 /** Looks up name for a socket state.
760 * @param[in] state &Socket state to look up.
761 * @return Pointer to a static buffer containing the name, or "Undefined socket state".
764 state_to_name(enum SocketState state)
767 NS(enum SocketState) map[] = {
777 for (i = 0; map[i].name; i++)
778 if (map[i].value == state)
781 return "Undefined socket state";
784 /** Looks up name for a timer type.
785 * @param[in] type &Timer type to look up.
786 * @return Pointer to a static buffer containing the name, or "Undefined timer type".
789 timer_to_name(enum TimerType type)
792 NS(enum TimerType) map[] = {
799 for (i = 0; map[i].name; i++)
800 if (map[i].value == type)
803 return "Undefined timer type";
806 /** Looks up name for an event type.
807 * @param[in] type &Event type to look up.
808 * @return Pointer to a static buffer containing the name, or "Undefined event type".
811 event_to_name(enum EventType type)
814 NS(enum EventType) map[] = {
827 for (i = 0; map[i].name; i++)
828 if (map[i].value == type)
831 return "Undefined event type";
834 /** Constructs a string describing certain generator flags.
835 * @param[in] flags Bitwise combination of generator flags.
836 * @return Pointer to a static buffer containing the names of flags set in \a flags.
839 gen_flags(unsigned int flags)
842 static char buf[256];
843 NS(unsigned int) map[] = {
854 for (i = 0; map[i].name; i++)
855 if (map[i].value & flags) {
858 loc += ircd_snprintf(0, buf + loc, sizeof(buf) - loc, "%s", map[i].name);
859 if (loc >= sizeof(buf))
860 return buf; /* overflow case */
866 /** Constructs a string describing certain socket flags.
867 * @param[in] flags Bitwise combination of socket flags.
868 * @return Pointer to a static buffer containing the names of flags set in \a flags.
871 sock_flags(unsigned int flags)
874 static char buf[256];
875 NS(unsigned int) map[] = {
876 NM(SOCK_EVENT_READABLE),
877 NM(SOCK_EVENT_WRITABLE),
886 for (i = 0; map[i].name; i++)
887 if (map[i].value & flags) {
890 loc += ircd_snprintf(0, buf + loc, sizeof(buf) - loc, "%s", map[i].name);
891 if (loc >= sizeof(buf))
892 return buf; /* overflow case */
898 #endif /* DEBUGMODE */