source: branches/ithildin-1.1/source/event.c @ 590

Revision 590, 7.6 KB checked in by wd, 7 years ago (diff)

Add a small optimization for hook_event.

  • Property svn:keywords set to Id Rev
Line 
1/*
2 * event.c: the event/hook system support functions
3 *
4 * Copyright 2002 the Ithildin Project.
5 * See the COPYING file for more information on licensing and use.
6 *
7 * This file contains functions for managing events and their hooks.  The
8 * system is relatively simple.  An event is created, and hooks are added to
9 * it.  Then the event is triggered and the hooks for that event are called
10 * one-by-one.  The system is deliberately simple to allow extremely fast
11 * hooking of events.
12 */
13
14#include <ithildin/stand.h>
15
16IDSTRING(rcsid, "$Id$");
17
18/* internally we the maximum number of hooks of any event in the system and
19 * an array used for passing returned values from hooks respectively */
20int maxhooks;
21void **hookreturns;
22int hook_num_returns;
23
24void add_hook_really(event_t *ep, hook_function_t func, struct hook *at);
25struct hook *find_hook(event_t *ep, hook_function_t func);
26
27void init_hooksystem(void) {
28    maxhooks = 16;
29    hookreturns = malloc(sizeof(void *) * maxhooks);
30}
31
32event_t *create_event(int flags) {
33    event_t *ep = NULL;
34       
35    ep = malloc(sizeof(event_t));
36    ep->numhooks = 0;
37    ep->flags = flags;
38
39    SLIST_INIT(&ep->hooks);
40
41    return ep;
42}
43
44/* this is the only really major function in the code, and will be the one
45 * which gets called by far the most often.  try to make it as fast as
46 * possible while being as robust as possible.  basically we call each hook
47 * for an event, and unless the event is such that return data is ignored,
48 * we build the return list along with it.  assume that data will NOT be
49 * modified by each function, although it is 'possible' that this might
50 * happen (but it's not our problem :). */
51void **hook_event(event_t *ep, void *data) {
52    struct hook *hp, *hp2;
53    void **returned;
54    void *ret, *econd = NULL;
55    int i = 0;
56    bool needclean = false;
57
58    /* set the calling flag.. deletions in this event will now be deferred */
59    ep->flags |= EVENT_FL_CALLING;
60
61    if (ep->flags & EVENT_FL_NORETURN)
62        returned = NULL;
63    else if (ep->flags & EVENT_FL_CONDITIONAL)
64        returned = (void **)HOOK_COND_PASS; /* success is the default */
65    else
66        returned = hookreturns;
67        SLIST_FOREACH(hp, &ep->hooks, lp) {
68        if (hp->flags & (HOOK_FL_DEFERRED | HOOK_FL_NEW)) {
69            needclean = true;
70            continue; /* either deferred for deletion or newly added */
71        }
72
73        ret = hookreturns[i++] = hp->function(ep, data);
74        if (ep->flags & EVENT_FL_CONDITIONAL) {
75            if (ret == (void *)HOOK_COND_ALWAYSOK) {
76                /* short-circuit success value.  stop here */
77                returned = (void **)HOOK_COND_SPASS;
78                break;
79            } else if (ret == (void *)HOOK_COND_NEVEROK) {
80                /* short-circuit failure value.  stop here */
81                returned = (void **)HOOK_COND_FAIL;
82                break;
83            } else if (ret == (void *)HOOK_COND_NOTOK)
84                returned = (void **)HOOK_COND_FAIL;
85            else if (ret != (void *)HOOK_COND_OK &&
86                    ret != (void *)HOOK_COND_NEUTRAL) {
87                returned = (void **)HOOK_COND_FAIL;
88                econd = ret;
89            }
90        }
91
92        if (ep->flags & EVENT_FL_HOOKONCE) {
93            hp->flags |= HOOK_FL_DEFERRED;
94            needclean = true;
95        }
96    }
97
98    if (ep->flags & EVENT_FL_CONDITIONAL) {
99        /* if we got a positive return (econd is set) then set returned
100         * properly. */
101        if (returned == (void **)HOOK_COND_FAIL && econd != NULL)
102            returned = (void **)econd;
103    } else
104        hook_num_returns = i;
105
106    /* trash the hooks which are deferred (will be all of them if this is a
107     * 'hookonce' event. */
108    ep->flags &= ~EVENT_FL_CALLING;
109
110    if (needclean) {
111        hp = SLIST_FIRST(&ep->hooks);
112        while (hp != NULL) {
113            hp2 = SLIST_NEXT(hp, lp);
114
115            if (hp->flags & HOOK_FL_DEFERRED) {
116                SLIST_REMOVE(&ep->hooks, hp, hook, lp);
117                free(hp);
118            } else if (hp->flags & HOOK_FL_NEW)
119                hp->flags &= ~HOOK_FL_NEW;
120            hp = hp2;
121        }
122    }
123
124    return returned;
125}
126               
127void destroy_event(event_t *ep) {
128    struct hook *hp;
129
130    if (ep == NULL)
131        return;
132
133    while (!SLIST_EMPTY(&ep->hooks)) {
134        hp = SLIST_FIRST(&ep->hooks);
135        SLIST_REMOVE_HEAD(&ep->hooks, lp);
136        free(hp);
137    }
138    free(ep);
139}
140
141
142/* add a hook after the specified one.  if after is NULL, it is added as the
143 * last hook on the event. */
144int add_hook_after(event_t *ep, hook_function_t func,
145        hook_function_t after) {
146    struct hook *hp = NULL;
147
148    if (ep == NULL || func == NULL)
149        return 0;
150
151    if (ep->numhooks && ep->flags & EVENT_FL_ONEHOOK) {
152        log_debug("tried to add another hook to a one-hook event");
153        return 0;
154    }
155
156    /* find the hook they're after.  add it on to that.  if we can't find the
157     * hook they're after, add it onto the end.  if they specify a NULL func,
158     * add it on to the end. */
159    if (after == NULL) {
160        SLIST_FOREACH(hp, &ep->hooks, lp) {
161            if (SLIST_NEXT(hp, lp) == NULL)
162                break;
163        }
164    } else {
165        SLIST_FOREACH(hp, &ep->hooks, lp) {
166            if (hp->function == after)
167                break; /* found it */
168            if (SLIST_NEXT(hp, lp) == NULL)
169                break; /* if the list ends here, be sure to preserve this */
170        }
171    }
172
173    add_hook_really(ep, func, hp);
174    return 1;
175}
176
177int add_hook_before(event_t *ep, hook_function_t func,
178        hook_function_t before) {
179    struct hook *hp = NULL;
180    struct hook *hp2 = NULL;
181
182    if (ep == NULL || func == NULL)
183        return 0;
184
185    if (ep->numhooks && ep->flags & EVENT_FL_ONEHOOK) {
186        log_debug("tried to add another hook to a one-hook event");
187        return 0;
188    }
189
190    /* find the hook they're after.  add it on to the one before that.  if we
191     * can't find it we add it on to the end.  If they specify NULL, the list
192     * is empty, or the first entry is the one to insert before, we add it at
193     * the head of the list. */
194    if (before == NULL || SLIST_EMPTY(&ep->hooks) ||
195            ((SLIST_FIRST(&ep->hooks))->function == before))
196        hp = NULL;
197    else {
198        SLIST_FOREACH(hp, &ep->hooks, lp) {
199            hp2 = SLIST_NEXT(hp, lp);
200            if (hp2 == NULL)
201                break; /* add it on to the end */
202            else if (hp2->function == before)
203                break; /* hp is the one before 'function'.  tada */
204        }
205    }
206
207    add_hook_really(ep, func, hp);
208    return 1;
209}
210
211void add_hook_really(event_t *ep, hook_function_t func, struct hook *at) {
212    struct hook *hp;
213
214    hp = malloc(sizeof(struct hook));
215    hp->function = func;
216    if (ep->flags & EVENT_FL_CALLING)
217        hp->flags = HOOK_FL_NEW;
218    else
219        hp->flags = 0;
220    if (at == NULL)
221        SLIST_INSERT_HEAD(&ep->hooks, hp, lp);
222    else
223        SLIST_INSERT_AFTER(at, hp, lp);
224    if (maxhooks <= ++ep->numhooks) {
225        maxhooks *= 2;
226        hookreturns = (void **)realloc(hookreturns, sizeof(void *) * maxhooks);
227    }
228}
229
230struct hook *find_hook(event_t *ep, hook_function_t func) {
231    struct hook *hp = NULL;
232
233    SLIST_FOREACH(hp, &ep->hooks, lp) {
234        if (hp->function == func)
235            return hp;
236    }
237
238    return NULL;
239}
240
241int remove_hook(event_t *ep, hook_function_t func) {
242    struct hook *hp = find_hook(ep, func);
243
244    if (hp != NULL) {
245        ep->numhooks--;
246
247        if (ep->flags & EVENT_FL_CALLING)
248            hp->flags |= HOOK_FL_DEFERRED; /* just defer this for deletion */
249        else {
250            SLIST_REMOVE(&ep->hooks, hp, hook, lp);
251        free(hp);
252        }
253        return 1;
254    }
255
256    return 0;
257}
258/* vi:set ts=8 sts=4 sw=4 tw=76 et: */
Note: See TracBrowser for help on using the repository browser.