From a6d5818f66df9ecf6c96c700fdf32a514c134ce3 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Mon, 17 Sep 2001 20:04:42 +0000 Subject: Switch the alarm system from using SIGALRM to normal glib timers. Also, 2001-09-17 Federico Mena Quintero Switch the alarm system from using SIGALRM to normal glib timers. Also, use a more robust de-queueing mechanism. * gui/alarm-notify/alarm.c (alarm_init): Removed. (alarm_done): Remove the glib timeout instead of closing the pipes and the signal handler. (alarm_add): Allow adding alarms that happen before right now. (queue_alarm): Use a glib timer instead of a signal. (alarm_remove): Adjust the timeout as appropriate. * gui/alarm-notify/notify-main.c (main): There is no need to initialize the alarm system now. * gui/main.c (main): Likewise. svn path=/trunk/; revision=12904 --- calendar/gui/alarm-notify/alarm.c | 255 ++++++++++++++------------------------ 1 file changed, 95 insertions(+), 160 deletions(-) (limited to 'calendar/gui/alarm-notify/alarm.c') diff --git a/calendar/gui/alarm-notify/alarm.c b/calendar/gui/alarm-notify/alarm.c index 8f6ba013a4..7f1a2eaa5f 100644 --- a/calendar/gui/alarm-notify/alarm.c +++ b/calendar/gui/alarm-notify/alarm.c @@ -32,14 +32,11 @@ -/* Whether the timer system has been initialized */ -static gboolean alarm_inited; - -/* The pipes used to notify about an alarm */ -static int alarm_pipes [2]; +/* Our glib timeout */ +static guint timeout_id; /* The list of pending alarms */ -static GList *alarms; +static GList *alarms = NULL; /* A queued alarm structure */ typedef struct { @@ -49,108 +46,90 @@ typedef struct { AlarmDestroyNotify destroy_notify_fn; } AlarmRecord; +static void setup_timeout (time_t now); + -/* SIGALRM handler. Notifies the callback about the alarm. */ +/* Removes the head alarm from the queue. Does not touch the timeout_id. */ static void -alarm_signal (int arg) +pop_alarm (void) { - char c = 0; + AlarmRecord *ar; + GList *l; + + if (!alarms) + return; + + ar = alarms->data; + + l = alarms; + alarms = g_list_remove_link (alarms, l); + g_list_free_1 (l); - write (alarm_pipes [1], &c, 1); + g_free (ar); } -/* Sets up an itimer and returns a success code */ +/* Callback from the alarm timeout */ static gboolean -setup_itimer (time_t diff) +alarm_ready_cb (gpointer data) { - struct itimerval itimer; - int v; + time_t now; - itimer.it_interval.tv_sec = 0; - itimer.it_interval.tv_usec = 0; - itimer.it_value.tv_sec = diff; - itimer.it_value.tv_usec = 0; + g_assert (alarms != NULL); + timeout_id = 0; - v = setitimer (ITIMER_REAL, &itimer, NULL); + now = time (NULL); - return (v == 0) ? TRUE : FALSE; -} + while (alarms) { + AlarmRecord *ar; + AlarmRecord ar_copy; -/* Clears the itimer we have pending */ -static gboolean -clear_itimer (void) -{ - return setup_itimer (0); -} + ar = alarms->data; -/* Removes the head alarm, returns it, and schedules the next alarm in the - * queue. - */ -static AlarmRecord * -pop_alarm (void) -{ - AlarmRecord *ar; - GList *l; + if (ar->trigger > now) + break; - if (!alarms) - return NULL; + ar_copy = *ar; + ar = &ar_copy; - ar = alarms->data; + pop_alarm (); /* This will free the original AlarmRecord; that's why we copy it */ - l = alarms; - alarms = g_list_remove_link (alarms, l); - g_list_free_1 (l); + (* ar->alarm_fn) (ar, ar->trigger, ar->data); - if (alarms) { - time_t now; - AlarmRecord *new_ar; - - now = time (NULL); - new_ar = alarms->data; - - if (!setup_itimer (new_ar->trigger - now)) { - g_message ("pop_alarm(): Could not reset the timer! " - "Weird things will happen."); - - /* FIXME: should we free the alarm list? What - * about further alarm removal requests that - * will fail? - */ - } - } else - if (!clear_itimer ()) - g_message ("pop_alarm(): Could not clear the timer! " - "Weird things may happen."); + if (ar->destroy_notify_fn) + (* ar->destroy_notify_fn) (ar, ar->data); + } - return ar; + if (alarms) + setup_timeout (now); + + return FALSE; } -/* Input handler for our own alarm notification pipe */ +/* Sets up a timeout for the next minute */ static void -alarm_ready (gpointer data, gint fd, GdkInputCondition cond) +setup_timeout (time_t now) { - AlarmRecord *ar; - char c; - - if (read (alarm_pipes [0], &c, 1) != 1) { - g_message ("alarm_ready(): Uh? Could not read from notification pipe."); - return; - } + time_t next, diff; + struct tm tm; + g_assert (timeout_id == 0); g_assert (alarms != NULL); - ar = pop_alarm (); - g_print ("alarm_ready(): Notifying about alarm on %s\n", ctime (&ar->trigger)); + tm = *localtime (&now); + tm.tm_sec = 0; + tm.tm_min++; /* next minute */ - (* ar->alarm_fn) (ar, ar->trigger, ar->data); + next = mktime (&tm); + g_assert (next != -1); - if (ar->destroy_notify_fn) - (* ar->destroy_notify_fn) (ar, ar->data); + diff = next - now; - g_free (ar); + g_assert (diff >= 0); + timeout_id = g_timeout_add (diff * 1000, alarm_ready_cb, NULL); } +/* Used from g_list_insert_sorted(); compares the trigger times of two AlarmRecord structures. */ static int compare_alarm_by_time (gconstpointer a, gconstpointer b) { @@ -163,41 +142,35 @@ compare_alarm_by_time (gconstpointer a, gconstpointer b) } /* Adds an alarm to the queue and sets up the timer */ -static gboolean -queue_alarm (time_t now, AlarmRecord *ar) +static void +queue_alarm (AlarmRecord *ar) { - time_t diff; + time_t now; AlarmRecord *old_head; - if (alarms) + if (alarms) { + g_assert (timeout_id != 0); + old_head = alarms->data; - else + } else { + g_assert (timeout_id == 0); + old_head = NULL; + } alarms = g_list_insert_sorted (alarms, ar, compare_alarm_by_time); if (old_head == alarms->data) - return TRUE; + return; /* Set the timer for removal upon activation */ - diff = ar->trigger - now; - if (!setup_itimer (diff)) { - GList *l; - - g_message ("queue_alarm(): Could not set up timer! Not queueing alarm."); - - l = g_list_find (alarms, ar); - g_assert (l != NULL); - - alarms = g_list_remove_link (alarms, l); - g_list_free_1 (l); - return FALSE; - } - - return TRUE; + now = time (NULL); + setup_timeout (now); } + + /** * alarm_add: * @trigger: Time at which alarm will trigger. @@ -215,29 +188,18 @@ gpointer alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data, AlarmDestroyNotify destroy_notify_fn) { - time_t now; AlarmRecord *ar; - g_return_val_if_fail (alarm_inited, NULL); g_return_val_if_fail (trigger != -1, NULL); g_return_val_if_fail (alarm_fn != NULL, NULL); - now = time (NULL); - if (trigger < now) - return NULL; - ar = g_new (AlarmRecord, 1); ar->trigger = trigger; ar->alarm_fn = alarm_fn; ar->data = data; ar->destroy_notify_fn = destroy_notify_fn; - g_print ("alarm_add(): Adding alarm for %s\n", ctime (&trigger)); - - if (!queue_alarm (now, ar)) { - g_free (ar); - ar = NULL; - } + queue_alarm (ar); return ar; } @@ -252,10 +214,10 @@ void alarm_remove (gpointer alarm) { AlarmRecord *ar; + AlarmRecord ar_copy; AlarmRecord *old_head; GList *l; - g_return_if_fail (alarm_inited); g_return_if_fail (alarm != NULL); ar = alarm; @@ -268,48 +230,29 @@ alarm_remove (gpointer alarm) old_head = alarms->data; - if (old_head == ar) - pop_alarm (); - else { + if (old_head == ar) { + ar_copy = *ar; + ar = &ar_copy; + pop_alarm (); /* This will free the original AlarmRecord; that's why we copy it */ + } else { alarms = g_list_remove_link (alarms, l); g_list_free_1 (l); } - if (ar->destroy_notify_fn) - (* ar->destroy_notify_fn) (ar, ar->data); - - g_free (ar); -} - -/** - * alarm_init: - * - * Initializes the alarm timer mechanism. This must be called near the - * beginning of the program. - **/ -void -alarm_init (void) -{ - struct sigaction sa; - int flags; + /* Reset the timeout */ - g_return_if_fail (alarm_inited == FALSE); + g_assert (timeout_id != 0); - pipe (alarm_pipes); + if (!alarms) { + g_source_remove (timeout_id); + timeout_id = 0; + } - /* set non blocking mode */ - flags = 0; - fcntl (alarm_pipes [0], F_GETFL, &flags); - fcntl (alarm_pipes [0], F_SETFL, flags | O_NONBLOCK); - gdk_input_add (alarm_pipes [0], GDK_INPUT_READ, alarm_ready, NULL); + /* Notify about destructiono of the alarm */ - /* Setup the signal handler */ - sa.sa_handler = alarm_signal; - sigemptyset (&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sigaction (SIGALRM, &sa, NULL); + if (ar->destroy_notify_fn) + (* ar->destroy_notify_fn) (ar, ar->data); - alarm_inited = TRUE; } /** @@ -323,11 +266,15 @@ alarm_done (void) { GList *l; - g_return_if_fail (alarm_inited); + if (timeout_id == 0) { + g_assert (alarms == NULL); + return; + } + + g_assert (alarms != NULL); - if (!clear_itimer ()) - g_message ("alarm_done(): Could not clear the timer! " - "Weird things may happen."); + g_source_remove (timeout_id); + timeout_id = 0; for (l = alarms; l; l = l->next) { AlarmRecord *ar; @@ -342,16 +289,4 @@ alarm_done (void) g_list_free (alarms); alarms = NULL; - - if (close (alarm_pipes[0]) != 0) - g_message ("alarm_done(): Could not close the input pipe for notification"); - - alarm_pipes[0] = -1; - - if (close (alarm_pipes[1]) != 0) - g_message ("alarm_done(): Could not close the output pipe for notification"); - - alarm_pipes[1] = -1; - - alarm_inited = FALSE; } -- cgit v1.2.3