diff options
author | Timo Sirainen <cras@irssi.org> | 2002-04-15 13:59:09 +0000 |
---|---|---|
committer | cras <cras@dbcabf3a-b0e7-0310-adc4-f8d773084564> | 2002-04-15 13:59:09 +0000 |
commit | 8b9c4823bfb9a1b60f31aa62eb882143e3dec153 (patch) | |
tree | 5370cc87f33e181e8d71933bddd701bb48093050 /src/core | |
parent | 15667ec7ff00b151fde5e9d9927bb75f00f7c79a (diff) | |
download | irssi-8b9c4823bfb9a1b60f31aa62eb882143e3dec153.zip |
Rewritten signal handling code - it wasn't supposed to come before 0.9 irssi
but it doesn't break much things and is needed for Qt port :)
git-svn-id: http://svn.irssi.org/repos/irssi/trunk@2682 dbcabf3a-b0e7-0310-adc4-f8d773084564
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/commands.c | 2 | ||||
-rw-r--r-- | src/core/expandos.c | 5 | ||||
-rw-r--r-- | src/core/signals.c | 325 | ||||
-rw-r--r-- | src/core/signals.h | 64 |
4 files changed, 215 insertions, 181 deletions
diff --git a/src/core/commands.c b/src/core/commands.c index 0899c9fa..9c0ab3fa 100644 --- a/src/core/commands.c +++ b/src/core/commands.c @@ -154,7 +154,7 @@ void command_bind_to(const char *module, int pos, const char *cmd, if (func != NULL) { str = g_strconcat("command ", cmd, NULL); - signal_add_to(module, pos, str, func); + signal_add_full(module, pos, str, func, NULL); g_free(str); } diff --git a/src/core/expandos.c b/src/core/expandos.c index 130d6025..5eb76410 100644 --- a/src/core/expandos.c +++ b/src/core/expandos.c @@ -179,7 +179,8 @@ void expando_bind(const char *key, int funccount, SIGNAL_FUNC *funcs) func = arg < funccount ? funcs[arg] : NULL; if (func == NULL) func = funcs[EXPANDO_ARG_NONE]; - signal_add_to_id(MODULE_NAME, 1, rec->signal_ids[n], func); + signal_add_full_id(MODULE_NAME, 1, rec->signal_ids[n], + func, NULL); } } @@ -208,7 +209,7 @@ void expando_unbind(const char *key, int funccount, SIGNAL_FUNC *funcs) func = arg < funccount ? funcs[arg] : NULL; if (func == NULL) func = funcs[EXPANDO_ARG_NONE]; - signal_remove_id(rec->signal_ids[n], func); + signal_remove_id(rec->signal_ids[n], func, NULL); } } diff --git a/src/core/signals.c b/src/core/signals.c index 92064e8d..c7f1ace3 100644 --- a/src/core/signals.c +++ b/src/core/signals.c @@ -1,7 +1,7 @@ /* signals.c : irssi - Copyright (C) 1999 Timo Sirainen + Copyright (C) 1999-2002 Timo Sirainen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,179 +18,205 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "../common.h" +#include "module.h" #include "signals.h" #include "modules.h" #define SIGNAL_LISTS 3 +typedef struct _SignalHook { + struct _SignalHook *next; + + int priority; + const char *module; + SIGNAL_FUNC func; + void *user_data; +} SignalHook; + typedef struct { int id; /* signal id */ int refcount; int emitting; /* signal is being emitted */ int stop_emit; /* this signal was stopped */ + int remove_count; /* hooks were removed from signal */ - GPtrArray *modulelist[SIGNAL_LISTS]; /* list of what signals belong - to which module */ - GPtrArray *siglist[SIGNAL_LISTS]; /* signal lists */ -} SIGNAL_REC; + SignalHook *hooks; +} Signal; #define signal_is_emitlist_empty(a) \ (!(a)->siglist[0] && !(a)->siglist[1] && !(a)->siglist[2]) -static GMemChunk *signals_chunk; +void *signal_user_data; + static GHashTable *signals; -static SIGNAL_REC *current_emitted_signal; +static Signal *current_emitted_signal; #define signal_ref(signal) ++(signal)->refcount -#define signal_unref(rec) (signal_unref_full(rec, TRUE)) +#define signal_unref(signal) (signal_unref_full(signal, TRUE)) -static int signal_unref_full(SIGNAL_REC *rec, int remove_hash) +static int signal_unref_full(Signal *rec, int remove) { - if (rec->refcount == 0) { - g_error("signal_unref(%s) : BUG - reference counter == 0", - signal_get_id_str(rec->id)); - } + g_assert(rec->refcount > 0); if (--rec->refcount != 0) - return FALSE; + return TRUE; /* remove whole signal from memory */ - if (!signal_is_emitlist_empty(rec)) { - g_error("signal_unref(%s) : BUG - emitlist wasn't empty", + if (rec->hooks != NULL) { + g_error("signal_unref(%s) : BUG - hook list wasn't empty", signal_get_id_str(rec->id)); } - if (remove_hash) + if (remove) g_hash_table_remove(signals, GINT_TO_POINTER(rec->id)); - g_mem_chunk_free(signals_chunk, rec); - return TRUE; + g_free(rec); + + return FALSE; } -static void signal_unref_count(SIGNAL_REC *rec, int count) +static void signal_hash_ref(void *key, Signal *rec) { - while (count-- > 0) - signal_unref(rec); + signal_ref(rec); } -void signal_add_to(const char *module, int pos, - const char *signal, SIGNAL_FUNC func) +static int signal_hash_unref(void *key, Signal *rec) { - g_return_if_fail(signal != NULL); + return !signal_unref_full(rec, FALSE); +} - signal_add_to_id(module, pos, signal_get_uniq_id(signal), func); +void signal_add_full(const char *module, int priority, + const char *signal, SIGNAL_FUNC func, void *user_data) +{ + signal_add_full_id(module, priority, signal_get_uniq_id(signal), + func, user_data); } /* bind a signal */ -void signal_add_to_id(const char *module, int pos, - int signal_id, SIGNAL_FUNC func) +void signal_add_full_id(const char *module, int priority, + int signal_id, SIGNAL_FUNC func, void *user_data) { - SIGNAL_REC *rec; + Signal *signal; + SignalHook *hook, **tmp; g_return_if_fail(signal_id >= 0); g_return_if_fail(func != NULL); - g_return_if_fail(pos >= 0 && pos < SIGNAL_LISTS); - - rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id)); - if (rec == NULL) { - rec = g_mem_chunk_alloc0(signals_chunk); - rec->id = signal_id; - g_hash_table_insert(signals, GINT_TO_POINTER(signal_id), rec); - } - if (rec->siglist[pos] == NULL) { - rec->siglist[pos] = g_ptr_array_new(); - rec->modulelist[pos] = g_ptr_array_new(); + signal = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id)); + if (signal == NULL) { + /* new signal */ + signal = g_new0(Signal, 1); + signal->id = signal_id; + g_hash_table_insert(signals, GINT_TO_POINTER(signal_id), signal); } - g_ptr_array_add(rec->siglist[pos], (void *) func); - g_ptr_array_add(rec->modulelist[pos], (void *) module); - - signal_ref(rec); -} - -static int signal_list_find(GPtrArray *array, void *data) -{ - unsigned int n; - - for (n = 0; n < array->len; n++) { - if (g_ptr_array_index(array, n) == data) - return n; + hook = g_new0(SignalHook, 1); + hook->priority = priority; + hook->module = module; + hook->func = func; + hook->user_data = user_data; + + /* insert signal to proper position in list */ + for (tmp = &signal->hooks; ; tmp = &(*tmp)->next) { + if (*tmp == NULL) { + /* last in list */ + *tmp = hook; + break; + } else if (priority <= (*tmp)->priority) { + /* insert before others with same priority */ + hook->next = *tmp; + *tmp = hook; + break; + } } - return -1; + signal_ref(signal); } -static void signal_list_free(SIGNAL_REC *rec, int list) +static void signal_remove_hook(Signal *rec, SignalHook **hook_pos) { - g_ptr_array_free(rec->siglist[list], TRUE); - g_ptr_array_free(rec->modulelist[list], TRUE); - rec->siglist[list] = NULL; - rec->modulelist[list] = NULL; -} + SignalHook *hook; -/* Returns TRUE if the whole signal is removed after this remove */ -static void signal_remove_from_list(SIGNAL_REC *rec, int list, int index) -{ - g_ptr_array_remove_index(rec->siglist[list], index); - g_ptr_array_remove_index(rec->modulelist[list], index); + hook = *hook_pos; + *hook_pos = hook->next; - if (rec->siglist[list]->len == 0) - signal_list_free(rec, list); + g_free(hook); signal_unref(rec); } -/* Remove signal from emit lists */ -static int signal_remove_from_lists(SIGNAL_REC *rec, SIGNAL_FUNC func) +/* Remove function from signal's emit list */ +static int signal_remove_func(Signal *rec, SIGNAL_FUNC func, void *user_data) { - int n, index; - - for (n = 0; n < SIGNAL_LISTS; n++) { - if (rec->siglist[n] == NULL) - continue; - - index = signal_list_find(rec->siglist[n], (void *) func); - if (index != -1) { - /* remove the function from emit list */ - signal_remove_from_list(rec, n, index); - return 1; + SignalHook **hook; + + for (hook = &rec->hooks; *hook != NULL; hook = &(*hook)->next) { + if ((*hook)->func == func && (*hook)->user_data == user_data) { + if (rec->emitting) { + /* mark it removed after emitting is done */ + (*hook)->func = NULL; + rec->remove_count++; + } else { + /* remove the function from emit list */ + signal_remove_hook(rec, hook); + } + return TRUE; } } - return 0; + return FALSE; } -void signal_remove_id(int signal_id, SIGNAL_FUNC func) +void signal_remove_id(int signal_id, SIGNAL_FUNC func, void *user_data) { - SIGNAL_REC *rec; + Signal *rec; g_return_if_fail(signal_id >= 0); g_return_if_fail(func != NULL); rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id)); if (rec != NULL) - signal_remove_from_lists(rec, func); + signal_remove_func(rec, func, user_data); } /* unbind signal */ -void signal_remove(const char *signal, SIGNAL_FUNC func) +void signal_remove_full(const char *signal, SIGNAL_FUNC func, void *user_data) { g_return_if_fail(signal != NULL); - signal_remove_id(signal_get_uniq_id(signal), func); + signal_remove_id(signal_get_uniq_id(signal), func, user_data); } -static int signal_emit_real(SIGNAL_REC *rec, int params, va_list va) +static void signal_hooks_clean(Signal *rec) { - gconstpointer arglist[SIGNAL_MAX_ARGUMENTS]; - SIGNAL_REC *prev_emitted_signal; - SIGNAL_FUNC func; - int n, index, stopped, stop_emit_count; + SignalHook **hook, **next; + int count; + + count = rec->remove_count; + rec->remove_count = 0; - for (n = 0; n < SIGNAL_MAX_ARGUMENTS; n++) - arglist[n] = n >= params ? NULL : va_arg(va, gconstpointer); + for (hook = &rec->hooks; *hook != NULL; hook = next) { + next = &(*hook)->next; + + if ((*hook)->func == NULL) { + next = hook; + signal_remove_hook(rec, hook); + + if (--count == 0) + break; + } + } +} + +static int signal_emit_real(Signal *rec, int params, va_list va) +{ + const void *arglist[SIGNAL_MAX_ARGUMENTS]; + Signal *prev_emitted_signal; + SignalHook *hook; + int i, stopped, stop_emit_count; + + for (i = 0; i < SIGNAL_MAX_ARGUMENTS; i++) + arglist[i] = i >= params ? NULL : va_arg(va, const void *); /* signal_stop_by_name("signal"); signal_emit("signal", ...); fails if we compare rec->stop_emit against 0. */ @@ -200,37 +226,39 @@ static int signal_emit_real(SIGNAL_REC *rec, int params, va_list va) stopped = FALSE; rec->emitting++; - for (n = 0; n < SIGNAL_LISTS; n++) { - /* run signals in emit lists */ - if (rec->siglist[n] == NULL) - continue; - for (index = rec->siglist[n]->len-1; index >= 0; index--) { - func = (SIGNAL_FUNC) g_ptr_array_index(rec->siglist[n], index); + for (hook = rec->hooks; hook != NULL; hook = hook->next) { + if (hook->func == NULL) + continue; /* removed */ - prev_emitted_signal = current_emitted_signal; - current_emitted_signal = rec; + prev_emitted_signal = current_emitted_signal; + current_emitted_signal = rec; #if SIGNAL_MAX_ARGUMENTS != 6 # error SIGNAL_MAX_ARGUMENTS changed - update code #endif - func(arglist[0], arglist[1], arglist[2], arglist[3], arglist[4], arglist[5]); - current_emitted_signal = prev_emitted_signal; - - if (rec->stop_emit != stop_emit_count) { - stopped = TRUE; - rec->stop_emit--; - n = SIGNAL_LISTS; - break; - } + signal_user_data = hook->user_data; + hook->func(arglist[0], arglist[1], arglist[2], arglist[3], + arglist[4], arglist[5]); + + current_emitted_signal = prev_emitted_signal; + + if (rec->stop_emit != stop_emit_count) { + stopped = TRUE; + rec->stop_emit--; + break; } } rec->emitting--; + signal_user_data = NULL; if (!rec->emitting) { if (rec->stop_emit != 0) { /* signal_stop() used too many times */ rec->stop_emit = 0; } + + if (rec->remove_count > 0) + signal_hooks_clean(rec); } signal_unref(rec); @@ -239,7 +267,7 @@ static int signal_emit_real(SIGNAL_REC *rec, int params, va_list va) int signal_emit(const char *signal, int params, ...) { - SIGNAL_REC *rec; + Signal *rec; va_list va; int signal_id; @@ -259,7 +287,7 @@ int signal_emit(const char *signal, int params, ...) int signal_emit_id(int signal_id, int params, ...) { - SIGNAL_REC *rec; + Signal *rec; va_list va; g_return_val_if_fail(signal_id >= 0, FALSE); @@ -278,7 +306,7 @@ int signal_emit_id(int signal_id, int params, ...) /* stop the current ongoing signal emission */ void signal_stop(void) { - SIGNAL_REC *rec; + Signal *rec; rec = current_emitted_signal; if (rec == NULL) @@ -290,7 +318,7 @@ void signal_stop(void) /* stop ongoing signal emission by signal name */ void signal_stop_by_name(const char *signal) { - SIGNAL_REC *rec; + Signal *rec; int signal_id; signal_id = signal_get_uniq_id(signal); @@ -310,7 +338,7 @@ const char *signal_get_emitted(void) /* return the ID of the signal that is currently being emitted */ int signal_get_emitted_id(void) { - SIGNAL_REC *rec; + Signal *rec; rec = current_emitted_signal; g_return_val_if_fail(rec != NULL, -1); @@ -320,7 +348,7 @@ int signal_get_emitted_id(void) /* return TRUE if specified signal was stopped */ int signal_is_stopped(int signal_id) { - SIGNAL_REC *rec; + Signal *rec; rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id)); g_return_val_if_fail(rec != NULL, FALSE); @@ -328,73 +356,56 @@ int signal_is_stopped(int signal_id) return rec->emitting <= rec->stop_emit; } -static void signal_remove_module(void *signal, SIGNAL_REC *rec, +static void signal_remove_module(void *signal, Signal *rec, const char *module) { - unsigned int index; - int list; + SignalHook **hook, **next; - for (list = 0; list < SIGNAL_LISTS; list++) { - if (rec->modulelist[list] == NULL) - continue; + for (hook = &rec->hooks; *hook != NULL; hook = next) { + next = &(*hook)->next; - for (index = rec->modulelist[list]->len; index > 0; index--) - if (g_strcasecmp(g_ptr_array_index(rec->modulelist[list], index-1), module) == 0) - signal_remove_from_list(rec, list, index-1); + if (strcasecmp((*hook)->module, module) == 0) { + next = hook; + signal_remove_hook(rec, hook); + } } } -static void signal_foreach_ref(void *signal, SIGNAL_REC *rec) -{ - signal_ref(rec); -} - -static int signal_foreach_unref(void *signal, SIGNAL_REC *rec) -{ - return signal_unref_full(rec, FALSE); -} - /* remove all signals that belong to `module' */ void signals_remove_module(const char *module) { g_return_if_fail(module != NULL); - g_hash_table_foreach(signals, (GHFunc) signal_foreach_ref, NULL); - g_hash_table_foreach(signals, (GHFunc) signal_remove_module, (void *) module); - g_hash_table_foreach_remove(signals, (GHRFunc) signal_foreach_unref, NULL); + g_hash_table_foreach(signals, (GHFunc) signal_hash_ref, NULL); + g_hash_table_foreach(signals, (GHFunc) signal_remove_module, + (void *) module); + g_hash_table_foreach_remove(signals, (GHRFunc) signal_hash_unref, NULL); } void signals_init(void) { - signals_chunk = g_mem_chunk_new("signals", sizeof(SIGNAL_REC), - sizeof(SIGNAL_REC)*200, G_ALLOC_AND_FREE); - signals = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal); + signals = g_hash_table_new(NULL, NULL); } -static void signal_free(void *key, SIGNAL_REC *rec) +static void signal_free(void *key, Signal *rec) { - int n; - - signal_ref(rec); + g_warning("signal_free(%s) : signal still has %d references:", + signal_get_id_str(rec->id), rec->refcount); - for (n = 0; n < SIGNAL_LISTS; n++) { - if (rec->siglist[n] != NULL) { - signal_unref_count(rec, rec->siglist[n]->len); - signal_list_free(rec, n); - } - } + while (rec->hooks != NULL) { + g_warning(" - module '%s' function %p", + rec->hooks->module, rec->hooks->func); - if (!signal_unref_full(rec, FALSE)) { - g_error("signal_free(%s) : BUG - signal still has %d references", - signal_get_id_str(rec->id), rec->refcount); + signal_remove_hook(rec, &rec->hooks); } } void signals_deinit(void) { - g_hash_table_foreach(signals, (GHFunc) signal_free, NULL); + g_hash_table_foreach(signals, (GHFunc) signal_hash_ref, NULL); + g_hash_table_foreach(signals, (GHFunc) signal_free, NULL); + g_hash_table_foreach_remove(signals, (GHRFunc) signal_hash_unref, NULL); g_hash_table_destroy(signals); module_uniq_destroy("signals"); - g_mem_chunk_destroy(signals_chunk); } diff --git a/src/core/signals.h b/src/core/signals.h index 795f7327..b3d5f72e 100644 --- a/src/core/signals.h +++ b/src/core/signals.h @@ -1,33 +1,43 @@ #ifndef __SIGNAL_H #define __SIGNAL_H +#define SIGNAL_PRIORITY_LOW 100 +#define SIGNAL_PRIORITY_DEFAULT 0 +#define SIGNAL_PRIORITY_HIGH -100 + #define SIGNAL_MAX_ARGUMENTS 6 -typedef void (*SIGNAL_FUNC) (gconstpointer, gconstpointer, - gconstpointer, gconstpointer, - gconstpointer, gconstpointer); +typedef void (*SIGNAL_FUNC) (const void *, const void *, + const void *, const void *, + const void *, const void *); -void signals_init(void); -void signals_deinit(void); - -/* signal name -> ID */ -#define signal_get_uniq_id(signal) \ - module_get_uniq_id_str("signals", signal) -/* signal ID -> name */ -#define signal_get_id_str(signal_id) \ - module_find_id_str("signals", signal_id) +extern void *signal_user_data; /* use signal_get_user_data() macro to access */ /* bind a signal */ -void signal_add_to(const char *module, int pos, - const char *signal, SIGNAL_FUNC func); -void signal_add_to_id(const char *module, int pos, - int signal, SIGNAL_FUNC func); -#define signal_add(a, b) signal_add_to(MODULE_NAME, 1, a, b) -#define signal_add_first(a, b) signal_add_to(MODULE_NAME, 0, a, b) -#define signal_add_last(a, b) signal_add_to(MODULE_NAME, 2, a, b) +void signal_add_full(const char *module, int priority, + const char *signal, SIGNAL_FUNC func, void *user_data); +void signal_add_full_id(const char *module, int priority, + int signal, SIGNAL_FUNC func, void *user_data); +#define signal_add(signal, func) \ + signal_add_full(MODULE_NAME, SIGNAL_PRIORITY_DEFAULT, (signal), (SIGNAL_FUNC) (func), NULL) +#define signal_add_first(signal, func) \ + signal_add_full(MODULE_NAME, SIGNAL_PRIORITY_HIGH, (signal), (SIGNAL_FUNC) (func), NULL) +#define signal_add_last(signal, func) \ + signal_add_full(MODULE_NAME, SIGNAL_PRIORITY_LOW, (signal), (SIGNAL_FUNC) (func), NULL) + +#define signal_add_data(signal, func, data) \ + signal_add_full(MODULE_NAME, SIGNAL_PRIORITY_DEFAULT, (signal), (SIGNAL_FUNC) (func), data) +#define signal_add_first_data(signal, func, data) \ + signal_add_full(MODULE_NAME, SIGNAL_PRIORITY_HIGH, (signal), (SIGNAL_FUNC) (func), data) +#define signal_add_last_data(signal, func, data) \ + signal_add_full(MODULE_NAME, SIGNAL_PRIORITY_LOW, (signal), (SIGNAL_FUNC) (func), data) /* unbind signal */ -void signal_remove(const char *signal, SIGNAL_FUNC func); -void signal_remove_id(int signal_id, SIGNAL_FUNC func); +void signal_remove_full(const char *signal, SIGNAL_FUNC func, void *user_data); +#define signal_remove(signal, func) \ + signal_remove_full((signal), (SIGNAL_FUNC) (func), NULL) +#define signal_remove_data(signal, func, data) \ + signal_remove_full((signal), (SIGNAL_FUNC) (func), data) +void signal_remove_id(int signal_id, SIGNAL_FUNC func, void *user_data); /* emit signal */ int signal_emit(const char *signal, int params, ...); @@ -44,8 +54,20 @@ const char *signal_get_emitted(void); int signal_get_emitted_id(void); /* return TRUE if specified signal was stopped */ int signal_is_stopped(int signal_id); +/* return the user data of the signal function currently being emitted */ +#define signal_get_user_data() signal_user_data /* remove all signals that belong to `module' */ void signals_remove_module(const char *module); +/* signal name -> ID */ +#define signal_get_uniq_id(signal) \ + module_get_uniq_id_str("signals", signal) +/* signal ID -> name */ +#define signal_get_id_str(signal_id) \ + module_find_id_str("signals", signal_id) + +void signals_init(void); +void signals_deinit(void); + #endif |