diff options
Diffstat (limited to 'src/g_unix_signal.c')
-rw-r--r-- | src/g_unix_signal.c | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/src/g_unix_signal.c b/src/g_unix_signal.c new file mode 100644 index 0000000..4d6a1a1 --- /dev/null +++ b/src/g_unix_signal.c @@ -0,0 +1,112 @@ +#define _POSIX_SOURCE +#include <signal.h> +#include <glib.h> + +static GPtrArray *signal_infos = NULL; + +typedef struct _GUnixSignalInfo { + guint source_id; + GMainContext *context; + gboolean triggered; + gint signum; +} GUnixSignalInfo; + +typedef struct _GUnixSignalSource { + GSource source; + GUnixSignalInfo *info; +} GUnixSignalSource; + +static inline GUnixSignalInfo* get_signal_info(guint index) +{ + return (GUnixSignalInfo*)g_ptr_array_index(signal_infos, index); +} + +static void handler(gint signum) { + g_assert(signal_infos != NULL); + for (guint i = 0; i < signal_infos->len; ++i) + if (get_signal_info(i)->signum == signum) + get_signal_info(i)->triggered = TRUE; + sigaction(signum, &(struct sigaction){handler}, NULL); +} + +static gboolean check(GSource *source) +{ + GUnixSignalSource *signal_source = (GUnixSignalSource*) source; + return signal_source->info->triggered; +} + +static gboolean prepare(GSource *source, gint *timeout_) +{ + GUnixSignalSource *signal_source = (GUnixSignalSource*) source; + if (signal_source->info->context == NULL) { + g_main_context_ref(signal_source->info->context = g_source_get_context(source)); + signal_source->info->source_id = g_source_get_id(source); + } + + *timeout_ = -1; + return signal_source->info->triggered; +} + +static gboolean dispatch(GSource *source, GSourceFunc callback, gpointer user_data) +{ + GUnixSignalSource *signal_source = (GUnixSignalSource*) source; + signal_source->info->triggered = FALSE; + return callback(user_data) ? TRUE : FALSE; +} +static void finalize(GSource *source) +{ + GUnixSignalSource *signal_source = (GUnixSignalSource*) source; + sigaction(signal_source->info->signum, &(struct sigaction){NULL}, NULL); + g_main_context_unref(signal_source->info->context); + g_ptr_array_remove_fast(signal_infos, signal_source->info); + if (signal_infos->len == 0) + signal_infos = (GPtrArray*) g_ptr_array_free(signal_infos, TRUE); + g_free(signal_source->info); + +} +static GSourceFuncs SourceFuncs = +{ + .prepare = prepare, + .check = check, + .dispatch = dispatch, + .finalize = finalize, + .closure_callback = NULL, .closure_marshal = NULL +}; + +static void g_unix_signal_source_init(GSource *source, gint signum) +{ + GUnixSignalSource *signal_source = (GUnixSignalSource *) source; + signal_source->info = g_new(GUnixSignalInfo, 1); + signal_source->info->triggered = FALSE; + signal_source->info->signum = signum; + signal_source->info->context = NULL; + + if (signal_infos == NULL) + signal_infos = g_ptr_array_new(); + g_ptr_array_add(signal_infos, signal_source->info); +} + +GSource *g_unix_signal_source_new(gint signum) +{ + GSource *source = g_source_new(&SourceFuncs, sizeof(GUnixSignalSource)); + g_unix_signal_source_init(source, signum); + sigaction(signum, &(struct sigaction){handler}, NULL); + return source; +} + +guint g_unix_signal_add_full(gint priority, gint signum, GSourceFunc function, gpointer data, GDestroyNotify notify) +{ + g_return_val_if_fail(function != NULL, 0); + GSource *source = g_unix_signal_source_new(signum); + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority (source, priority); + g_source_set_callback(source, function, data, notify); + guint id = g_source_attach(source, NULL); + g_source_unref(source); + return id; +} + +guint g_unix_signal_add(gint signum, GSourceFunc function, gpointer data) +{ + return g_unix_signal_add_full(G_PRIORITY_DEFAULT, signum, function, data, NULL); +} |