1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 2000-2021 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file event.c
23 * \brief Timer based event execution
24 * \version $Id: event.c 9858 2021-01-01 04:43:42Z michael $
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "ircd.h"
30 #include "event.h"
31 #include "rng_mt.h"
32 #include "log.h"
33
34
35 struct event_base ebase;
36 struct event_base *event_base = &ebase;
37
38 static dlink_list event_list;
39
40 const dlink_list *
event_get_list(void)41 event_get_list(void)
42 {
43 return &event_list;
44 }
45
46 void
event_add(struct event * ev,void * data)47 event_add(struct event *ev, void *data)
48 {
49 dlink_node *node;
50
51 event_delete(ev);
52
53 ev->data = data;
54 ev->next = event_base->time.sec_monotonic + ev->when;
55 ev->active = true;
56
57 DLINK_FOREACH(node, event_list.head)
58 {
59 struct event *e = node->data;
60
61 if (e->next > ev->next)
62 {
63 dlinkAddBefore(node, ev, &ev->node, &event_list);
64 return;
65 }
66 }
67
68 dlinkAddTail(ev, &ev->node, &event_list);
69 }
70
71 void
event_addish(struct event * ev,void * data)72 event_addish(struct event *ev, void *data)
73 {
74 if (ev->when >= 3)
75 {
76 const uintmax_t two_third = (2 * ev->when) / 3;
77
78 ev->when = two_third + ((genrand_int32() % 1000) * two_third) / 1000;
79 }
80
81 event_add(ev, data);
82 }
83
84 void
event_delete(struct event * ev)85 event_delete(struct event *ev)
86 {
87 if (ev->active == false)
88 return;
89
90 dlinkDelete(&ev->node, &event_list);
91 ev->active = false;
92 }
93
94 void
event_run(void)95 event_run(void)
96 {
97 static uintmax_t last = 0;
98
99 if (last == event_base->time.sec_monotonic)
100 return;
101 last = event_base->time.sec_monotonic;
102
103 unsigned int len = dlink_list_length(&event_list);
104 while (len-- && dlink_list_length(&event_list))
105 {
106 struct event *ev = event_list.head->data;
107
108 if (ev->next > event_base->time.sec_monotonic)
109 break;
110
111 event_delete(ev);
112
113 ev->handler(ev->data);
114
115 if (ev->oneshot == false)
116 event_add(ev, ev->data);
117 }
118 }
119
120 void
event_time_set(void)121 event_time_set(void)
122 {
123 struct timespec newtime;
124
125 if (clock_gettime(CLOCK_REALTIME, &newtime))
126 exit(EXIT_FAILURE);
127 else if (event_base->time.sec_real > (uintmax_t)newtime.tv_sec)
128 ilog(LOG_TYPE_IRCD, "System clock is running backwards - (%ju < %ju)",
129 (uintmax_t)newtime.tv_sec, event_base->time.sec_real);
130 event_base->time.sec_real = newtime.tv_sec;
131
132 #ifdef CLOCK_MONOTONIC_RAW
133 if (clock_gettime(CLOCK_MONOTONIC_RAW, &newtime) == 0)
134 #else
135 if (clock_gettime(CLOCK_MONOTONIC, &newtime) == 0)
136 #endif
137 event_base->time.sec_monotonic = newtime.tv_sec;
138 else
139 exit(EXIT_FAILURE);
140 }
141