1 /* Event handling functions */
9 #include "main/event.h"
10 #include "util/error.h"
11 #include "util/hash.h"
12 #include "util/memory.h"
13 #include "util/snprintf.h"
14 #include "util/string.h"
17 /* First, we should set some terminology:
19 * o event -> Message being [triggerred] by some ELinks part and [catched]
20 * by various random other ones.
22 * o event hook -> Device being deployed by various ELinks parts,
23 * associated with certain [event It [catches] that event by having a
24 * handler executed when the [event] is triggerred.
26 * o event chain -> Line of [event hook]s associated with a given [event
27 * The [hook]s are ordered by [priority Each hook returns whenever should
28 * the chain continue or that no other events in the chain should be
32 struct event_handler
{
33 /* The function to be called with the event data. */
34 event_hook_T callback
;
36 /* The @priority of this handler. */
39 /* Handler specific data. */
44 /* The event name has to be unique. */
47 /* There are @count event @handlers all ordered by priority. */
48 struct event_handler
*handlers
;
51 /* The unique event id and position in events. */
55 static struct event
*events
= NULL
;
56 static unsigned int eventssize
= 0;
57 static struct hash
*event_hash
= NULL
;
59 /* TODO: This should be tuned to the number of events. When we will have a lot
60 * of them, this should be some big enough number to reduce unnecessary
61 * slavery of CPU on the startup. Then after all main modules will be
62 * initialized and their events will be registered, we could call something
63 * like adjust_events_list() which will tune it to the exactly needed number.
64 * This should be also called after each new plugin loaded. */
65 #define EVENT_GRANULARITY 0x07
67 #define realloc_events(ptr, size) \
68 mem_align_alloc(ptr, (size), (size) + 1, struct event, EVENT_GRANULARITY)
71 invalid_event_id(register int id
)
73 return (id
< 0 || id
>= eventssize
|| id
== EVENT_NONE
);
77 register_event(unsigned char *name
)
79 int id
= get_event_id(name
);
83 if (id
!= EVENT_NONE
) return id
;
86 if (!realloc_events(&events
, eventssize
)) return EVENT_NONE
;
88 /* If @events got relocated update the hash. */
89 if (event
!= events
) {
90 for (id
= 0; id
< eventssize
; id
++) {
91 struct hash_item
*item
;
92 int len
= strlen(events
[id
].name
);
94 item
= get_hash_item(event_hash
, events
[id
].name
, len
);
96 if (item
) item
->value
= &events
[id
];
100 event
= &events
[eventssize
];
102 namelen
= strlen(name
);
103 event
->name
= memacpy(name
, namelen
);
104 if (!event
->name
) return EVENT_NONE
;
106 if (!add_hash_item(event_hash
, event
->name
, namelen
, event
)) {
107 mem_free(event
->name
);
112 event
->handlers
= NULL
;
114 event
->id
= eventssize
++;
120 get_event_id(unsigned char *name
)
122 struct hash_item
*item
;
125 assertm(name
&& name
[0], "Empty or missing event name");
126 if_assert_failed
return EVENT_NONE
;
128 if (!event_hash
) return EVENT_NONE
;
130 namelen
= strlen(name
);
131 item
= get_hash_item(event_hash
, name
, namelen
);
133 struct event
*event
= item
->value
;
135 assertm(event
, "Hash item with no value");
136 if_assert_failed
return EVENT_NONE
;
145 get_event_name(int id
)
147 if (invalid_event_id(id
)) return NULL
;
149 return events
[id
].name
;
153 trigger_event_va(int id
, va_list ap_init
)
156 struct event_handler
*ev_handler
;
158 if (invalid_event_id(id
)) return;
160 ev_handler
= events
[id
].handlers
;
161 for (i
= 0; i
< events
[id
].count
; i
++, ev_handler
++) {
162 enum evhook_status ret
;
165 VA_COPY(ap
, ap_init
);
166 ret
= ev_handler
->callback(ap
, ev_handler
->data
);
169 if (ret
== EVENT_HOOK_STATUS_LAST
) return;
174 trigger_event(int id
, ...)
179 trigger_event_va(id
, ap
);
183 trigger_event_name(unsigned char *name
, ...)
186 int id
= get_event_id(name
);
189 trigger_event_va(id
, ap
);
193 move_event_handler(struct event
*event
, int to
, int from
)
195 int d
= int_max(to
, from
);
197 memmove(&event
->handlers
[to
], &event
->handlers
[from
],
198 (event
->count
- d
) * sizeof(*event
->handlers
));
202 register_event_hook(int id
, event_hook_T callback
, int priority
, void *data
)
208 if_assert_failed
return EVENT_NONE
;
210 if (invalid_event_id(id
)) return EVENT_NONE
;
214 for (i
= 0; i
< event
->count
; i
++)
215 if (event
->handlers
[i
].callback
== callback
) break;
217 if (i
== event
->count
) {
218 struct event_handler
*eh
;
220 eh
= mem_realloc(event
->handlers
,
221 (event
->count
+ 1) * sizeof(*event
->handlers
));
223 if (!eh
) return EVENT_NONE
;
225 event
->handlers
= eh
;
228 move_event_handler(event
, i
, i
+ 1);
231 for (i
= 0; i
< event
->count
- 1; i
++)
232 if (event
->handlers
[i
].priority
< priority
) break;
234 move_event_handler(event
, i
+ 1, i
);
236 event
->handlers
[i
].callback
= callback
;
237 event
->handlers
[i
].priority
= priority
;
238 event
->handlers
[i
].data
= data
;
244 unregister_event_hook(int id
, event_hook_T callback
)
249 if_assert_failed
return;
251 if (invalid_event_id(id
)) return;
254 if (event
->handlers
) {
257 for (i
= 0; i
< event
->count
; i
++) {
258 if (event
->handlers
[i
].callback
!= callback
)
261 move_event_handler(event
, i
, i
+ 1);
264 mem_free(event
->handlers
);
265 event
->handlers
= NULL
;
267 struct event_handler
*eh
;
269 eh
= mem_realloc(event
->handlers
,
270 event
->count
* sizeof(*event
->handlers
));
271 if (eh
) event
->handlers
= eh
;
280 register_event_hooks(struct event_hook_info
*hooks
)
284 for (i
= 0; hooks
[i
].name
; i
++) {
285 int id
= register_event(hooks
[i
].name
);
287 if (id
== EVENT_NONE
) continue;
289 register_event_hook(id
, hooks
[i
].callback
, hooks
[i
].priority
,
295 unregister_event_hooks(struct event_hook_info
*hooks
)
299 for (i
= 0; hooks
[i
].name
; i
++) {
300 int id
= get_event_id(hooks
[i
].name
);
302 if (id
== EVENT_NONE
) continue;
304 unregister_event_hook(id
, hooks
[i
].callback
);
311 event_hash
= init_hash(8, strhash
);
319 if (event_hash
) free_hash(event_hash
);
321 for (i
= 0; i
< eventssize
; i
++) {
322 mem_free_if(events
[i
].handlers
);
323 mem_free(events
[i
].name
);
326 mem_free_set(&events
, NULL
);