~ubuntu-branches/ubuntu/feisty/irssi/feisty-backports

« back to all changes in this revision

Viewing changes to src/core/signals.c

  • Committer: Bazaar Package Importer
  • Author(s): David Pashley
  • Date: 2005-12-10 21:25:51 UTC
  • Revision ID: james.westby@ubuntu.com-20051210212551-5qwm108g7inyu2f2
Tags: upstream-0.8.10
ImportĀ upstreamĀ versionĀ 0.8.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 signals.c : irssi
 
3
 
 
4
    Copyright (C) 1999-2002 Timo Sirainen
 
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
19
*/
 
20
 
 
21
#include "module.h"
 
22
#include "signals.h"
 
23
#include "modules.h"
 
24
 
 
25
typedef struct _SignalHook {
 
26
        struct _SignalHook *next;
 
27
 
 
28
        int priority;
 
29
        const char *module;
 
30
        SIGNAL_FUNC func;
 
31
        void *user_data;
 
32
} SignalHook;
 
33
 
 
34
typedef struct {
 
35
        int id; /* signal id */
 
36
        int refcount;
 
37
 
 
38
        int emitting; /* signal is being emitted */
 
39
        int stop_emit; /* this signal was stopped */
 
40
        int continue_emit; /* this signal emit was continued elsewhere */
 
41
        int remove_count; /* hooks were removed from signal */
 
42
 
 
43
        SignalHook *hooks;
 
44
} Signal;
 
45
 
 
46
void *signal_user_data;
 
47
 
 
48
static GHashTable *signals;
 
49
static Signal *current_emitted_signal;
 
50
static SignalHook *current_emitted_hook;
 
51
 
 
52
#define signal_ref(signal) ++(signal)->refcount
 
53
#define signal_unref(signal) (signal_unref_full(signal, TRUE))
 
54
 
 
55
static int signal_unref_full(Signal *rec, int remove)
 
56
{
 
57
        g_assert(rec->refcount > 0);
 
58
 
 
59
        if (--rec->refcount != 0)
 
60
                return TRUE;
 
61
 
 
62
        /* remove whole signal from memory */
 
63
        if (rec->hooks != NULL) {
 
64
                g_error("signal_unref(%s) : BUG - hook list wasn't empty",
 
65
                        signal_get_id_str(rec->id));
 
66
        }
 
67
 
 
68
        if (remove)
 
69
                g_hash_table_remove(signals, GINT_TO_POINTER(rec->id));
 
70
        g_free(rec);
 
71
 
 
72
        return FALSE;
 
73
}
 
74
 
 
75
static void signal_hash_ref(void *key, Signal *rec)
 
76
{
 
77
        signal_ref(rec);
 
78
}
 
79
 
 
80
static int signal_hash_unref(void *key, Signal *rec)
 
81
{
 
82
        return !signal_unref_full(rec, FALSE);
 
83
}
 
84
 
 
85
void signal_add_full(const char *module, int priority,
 
86
                     const char *signal, SIGNAL_FUNC func, void *user_data)
 
87
{
 
88
        signal_add_full_id(module, priority, signal_get_uniq_id(signal),
 
89
                           func, user_data);
 
90
}
 
91
 
 
92
/* bind a signal */
 
93
void signal_add_full_id(const char *module, int priority,
 
94
                        int signal_id, SIGNAL_FUNC func, void *user_data)
 
95
{
 
96
        Signal *signal;
 
97
        SignalHook *hook, **tmp;
 
98
 
 
99
        g_return_if_fail(signal_id >= 0);
 
100
        g_return_if_fail(func != NULL);
 
101
 
 
102
        signal = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
 
103
        if (signal == NULL) {
 
104
                /* new signal */
 
105
                signal = g_new0(Signal, 1);
 
106
                signal->id = signal_id;
 
107
                g_hash_table_insert(signals, GINT_TO_POINTER(signal_id), signal);
 
108
        }
 
109
 
 
110
        hook = g_new0(SignalHook, 1);
 
111
        hook->priority = priority;
 
112
        hook->module = module;
 
113
        hook->func = func;
 
114
        hook->user_data = user_data;
 
115
 
 
116
        /* insert signal to proper position in list */
 
117
        for (tmp = &signal->hooks; ; tmp = &(*tmp)->next) {
 
118
                if (*tmp == NULL) {
 
119
                        /* last in list */
 
120
                        *tmp = hook;
 
121
                        break;
 
122
                } else if (priority <= (*tmp)->priority) {
 
123
                        /* insert before others with same priority */
 
124
                        hook->next = *tmp;
 
125
                        *tmp = hook;
 
126
                        break;
 
127
                }
 
128
        }
 
129
 
 
130
        signal_ref(signal);
 
131
}
 
132
 
 
133
static void signal_remove_hook(Signal *rec, SignalHook **hook_pos)
 
134
{
 
135
        SignalHook *hook;
 
136
 
 
137
        hook = *hook_pos;
 
138
        *hook_pos = hook->next;
 
139
 
 
140
        g_free(hook);
 
141
 
 
142
        signal_unref(rec);
 
143
}
 
144
 
 
145
/* Remove function from signal's emit list */
 
146
static int signal_remove_func(Signal *rec, SIGNAL_FUNC func, void *user_data)
 
147
{
 
148
        SignalHook **hook;
 
149
 
 
150
        for (hook = &rec->hooks; *hook != NULL; hook = &(*hook)->next) {
 
151
                if ((*hook)->func == func && (*hook)->user_data == user_data) {
 
152
                        if (rec->emitting) {
 
153
                                /* mark it removed after emitting is done */
 
154
                                (*hook)->func = NULL;
 
155
                                rec->remove_count++;
 
156
                        } else {
 
157
                                /* remove the function from emit list */
 
158
                                signal_remove_hook(rec, hook);
 
159
                        }
 
160
                        return TRUE;
 
161
                }
 
162
        }
 
163
 
 
164
        return FALSE;
 
165
}
 
166
 
 
167
void signal_remove_id(int signal_id, SIGNAL_FUNC func, void *user_data)
 
168
{
 
169
        Signal *rec;
 
170
 
 
171
        g_return_if_fail(signal_id >= 0);
 
172
        g_return_if_fail(func != NULL);
 
173
 
 
174
        rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
 
175
        if (rec != NULL)
 
176
                signal_remove_func(rec, func, user_data);
 
177
}
 
178
 
 
179
/* unbind signal */
 
180
void signal_remove_full(const char *signal, SIGNAL_FUNC func, void *user_data)
 
181
{
 
182
        g_return_if_fail(signal != NULL);
 
183
 
 
184
        signal_remove_id(signal_get_uniq_id(signal), func, user_data);
 
185
}
 
186
 
 
187
static void signal_hooks_clean(Signal *rec)
 
188
{
 
189
        SignalHook **hook, **next;
 
190
        int count;
 
191
 
 
192
        count = rec->remove_count;
 
193
        rec->remove_count = 0;
 
194
 
 
195
        for (hook = &rec->hooks; *hook != NULL; hook = next) {
 
196
                next = &(*hook)->next;
 
197
 
 
198
                if ((*hook)->func == NULL) {
 
199
                        next = hook;
 
200
                        signal_remove_hook(rec, hook);
 
201
 
 
202
                        if (--count == 0)
 
203
                                break;
 
204
                }
 
205
        }
 
206
}
 
207
 
 
208
static int signal_emit_real(Signal *rec, int params, va_list va,
 
209
                            SignalHook *first_hook)
 
210
{
 
211
        const void *arglist[SIGNAL_MAX_ARGUMENTS];
 
212
        Signal *prev_emitted_signal;
 
213
        SignalHook *hook, *prev_emitted_hook;
 
214
        int i, stopped, stop_emit_count, continue_emit_count;
 
215
 
 
216
        for (i = 0; i < SIGNAL_MAX_ARGUMENTS; i++)
 
217
                arglist[i] = i >= params ? NULL : va_arg(va, const void *);
 
218
 
 
219
        /* signal_stop_by_name("signal"); signal_emit("signal", ...);
 
220
           fails if we compare rec->stop_emit against 0. */
 
221
        stop_emit_count = rec->stop_emit;
 
222
        continue_emit_count = rec->continue_emit;
 
223
 
 
224
        signal_ref(rec);
 
225
 
 
226
        stopped = FALSE;
 
227
        rec->emitting++;
 
228
 
 
229
        prev_emitted_signal = current_emitted_signal;
 
230
        prev_emitted_hook = current_emitted_hook;
 
231
        current_emitted_signal = rec;
 
232
 
 
233
        for (hook = first_hook; hook != NULL; hook = hook->next) {
 
234
                if (hook->func == NULL)
 
235
                        continue; /* removed */
 
236
 
 
237
                current_emitted_hook = hook;
 
238
#if SIGNAL_MAX_ARGUMENTS != 6
 
239
#  error SIGNAL_MAX_ARGUMENTS changed - update code
 
240
#endif
 
241
                signal_user_data = hook->user_data;
 
242
                hook->func(arglist[0], arglist[1], arglist[2], arglist[3],
 
243
                           arglist[4], arglist[5]);
 
244
 
 
245
                if (rec->continue_emit != continue_emit_count)
 
246
                        rec->continue_emit--;
 
247
 
 
248
                if (rec->stop_emit != stop_emit_count) {
 
249
                        stopped = TRUE;
 
250
                        rec->stop_emit--;
 
251
                        break;
 
252
                }
 
253
        }
 
254
 
 
255
        current_emitted_signal = prev_emitted_signal;
 
256
        current_emitted_hook = prev_emitted_hook;
 
257
 
 
258
        rec->emitting--;
 
259
        signal_user_data = NULL;
 
260
 
 
261
        if (!rec->emitting) {
 
262
                g_assert(rec->stop_emit == 0);
 
263
                g_assert(rec->continue_emit == 0);
 
264
 
 
265
                if (rec->remove_count > 0)
 
266
                        signal_hooks_clean(rec);
 
267
        }
 
268
 
 
269
        signal_unref(rec);
 
270
        return stopped;
 
271
}
 
272
 
 
273
int signal_emit(const char *signal, int params, ...)
 
274
{
 
275
        Signal *rec;
 
276
        va_list va;
 
277
        int signal_id;
 
278
 
 
279
        g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
 
280
 
 
281
        signal_id = signal_get_uniq_id(signal);
 
282
 
 
283
        rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
 
284
        if (rec != NULL) {
 
285
                va_start(va, params);
 
286
                signal_emit_real(rec, params, va, rec->hooks);
 
287
                va_end(va);
 
288
        }
 
289
 
 
290
        return rec != NULL;
 
291
}
 
292
 
 
293
int signal_emit_id(int signal_id, int params, ...)
 
294
{
 
295
        Signal *rec;
 
296
        va_list va;
 
297
 
 
298
        g_return_val_if_fail(signal_id >= 0, FALSE);
 
299
        g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
 
300
 
 
301
        rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
 
302
        if (rec != NULL) {
 
303
                va_start(va, params);
 
304
                signal_emit_real(rec, params, va, rec->hooks);
 
305
                va_end(va);
 
306
        }
 
307
 
 
308
        return rec != NULL;
 
309
}
 
310
 
 
311
void signal_continue(int params, ...)
 
312
{
 
313
        Signal *rec;
 
314
        va_list va;
 
315
 
 
316
        rec = current_emitted_signal;
 
317
        if (rec == NULL || rec->emitting <= rec->continue_emit)
 
318
                g_warning("signal_continue() : no signals are being emitted currently");
 
319
        else {
 
320
                va_start(va, params);
 
321
 
 
322
                /* stop the signal */
 
323
                if (rec->emitting > rec->stop_emit)
 
324
                        rec->stop_emit++;
 
325
 
 
326
                /* re-emit */
 
327
                rec->continue_emit++;
 
328
                signal_emit_real(rec, params, va, current_emitted_hook->next);
 
329
                va_end(va);
 
330
        }
 
331
}
 
332
 
 
333
/* stop the current ongoing signal emission */
 
334
void signal_stop(void)
 
335
{
 
336
        Signal *rec;
 
337
 
 
338
        rec = current_emitted_signal;
 
339
        if (rec == NULL)
 
340
                g_warning("signal_stop() : no signals are being emitted currently");
 
341
        else if (rec->emitting > rec->stop_emit)
 
342
                rec->stop_emit++;
 
343
}
 
344
 
 
345
/* stop ongoing signal emission by signal name */
 
346
void signal_stop_by_name(const char *signal)
 
347
{
 
348
        Signal *rec;
 
349
        int signal_id;
 
350
 
 
351
        signal_id = signal_get_uniq_id(signal);
 
352
        rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
 
353
        if (rec == NULL)
 
354
                g_warning("signal_stop_by_name() : unknown signal \"%s\"", signal);
 
355
        else if (rec->emitting > rec->stop_emit)
 
356
                rec->stop_emit++;
 
357
}
 
358
 
 
359
/* return the name of the signal that is currently being emitted */
 
360
const char *signal_get_emitted(void)
 
361
{
 
362
        return signal_get_id_str(signal_get_emitted_id());
 
363
}
 
364
 
 
365
/* return the ID of the signal that is currently being emitted */
 
366
int signal_get_emitted_id(void)
 
367
{
 
368
        Signal *rec;
 
369
 
 
370
        rec = current_emitted_signal;
 
371
        g_return_val_if_fail(rec != NULL, -1);
 
372
        return rec->id;
 
373
}
 
374
 
 
375
/* return TRUE if specified signal was stopped */
 
376
int signal_is_stopped(int signal_id)
 
377
{
 
378
        Signal *rec;
 
379
 
 
380
        rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
 
381
        g_return_val_if_fail(rec != NULL, FALSE);
 
382
 
 
383
        return rec->emitting <= rec->stop_emit;
 
384
}
 
385
 
 
386
static void signal_remove_module(void *signal, Signal *rec,
 
387
                                 const char *module)
 
388
{
 
389
        SignalHook **hook, **next;
 
390
 
 
391
        for (hook = &rec->hooks; *hook != NULL; hook = next) {
 
392
                next = &(*hook)->next;
 
393
 
 
394
                if (strcasecmp((*hook)->module, module) == 0) {
 
395
                        next = hook;
 
396
                        signal_remove_hook(rec, hook);
 
397
                }
 
398
        }
 
399
}
 
400
 
 
401
/* remove all signals that belong to `module' */
 
402
void signals_remove_module(const char *module)
 
403
{
 
404
        g_return_if_fail(module != NULL);
 
405
 
 
406
        g_hash_table_foreach(signals, (GHFunc) signal_hash_ref, NULL);
 
407
        g_hash_table_foreach(signals, (GHFunc) signal_remove_module,
 
408
                             (void *) module);
 
409
        g_hash_table_foreach_remove(signals, (GHRFunc) signal_hash_unref, NULL);
 
410
}
 
411
 
 
412
void signals_init(void)
 
413
{
 
414
        signals = g_hash_table_new(NULL, NULL);
 
415
}
 
416
 
 
417
static void signal_free(void *key, Signal *rec)
 
418
{
 
419
        /* refcount-1 because we just referenced it ourself */
 
420
        g_warning("signal_free(%s) : signal still has %d references:",
 
421
                  signal_get_id_str(rec->id), rec->refcount-1);
 
422
 
 
423
        while (rec->hooks != NULL) {
 
424
                g_warning(" - module '%s' function %p",
 
425
                          rec->hooks->module, rec->hooks->func);
 
426
 
 
427
                signal_remove_hook(rec, &rec->hooks);
 
428
        }
 
429
}
 
430
 
 
431
void signals_deinit(void)
 
432
{
 
433
        g_hash_table_foreach(signals, (GHFunc) signal_hash_ref, NULL);
 
434
        g_hash_table_foreach(signals, (GHFunc) signal_free, NULL);
 
435
        g_hash_table_foreach_remove(signals, (GHRFunc) signal_hash_unref, NULL);
 
436
        g_hash_table_destroy(signals);
 
437
 
 
438
        module_uniq_destroy("signals");
 
439
}