[Xfce4-commits] <xfce4-weather-plugin:master> Rework update code using precise wake-ups instead of constant intervals.

Harald Judt noreply at xfce.org
Fri Dec 14 13:28:02 CET 2012


Updating branch refs/heads/master
         to d7ecefdce49469f9a01417a32de47ac3739f4871 (commit)
       from d022129310cbf78302b685b121cafcf1508b252d (commit)

commit d7ecefdce49469f9a01417a32de47ac3739f4871
Author: Harald Judt <h.judt at gmx.at>
Date:   Thu Dec 13 14:39:22 2012 +0100

    Rework update code using precise wake-ups instead of constant intervals.
    
    The new code handles especially day/night updates more timely and deals better
    with download retries. It sends the plugin to sleep between data or widget updates,
    saving cpu cycles.

 panel-plugin/weather-debug.c |   40 +++++-
 panel-plugin/weather.c       |  308 +++++++++++++++++++++++++-----------------
 panel-plugin/weather.h       |   16 ++-
 3 files changed, 230 insertions(+), 134 deletions(-)

diff --git a/panel-plugin/weather-debug.c b/panel-plugin/weather-debug.c
index 2e85acd..3fe0677 100644
--- a/panel-plugin/weather-debug.c
+++ b/panel-plugin/weather-debug.c
@@ -407,13 +407,19 @@ gchar *
 weather_dump_plugindata(const plugin_data *data)
 {
     GString *out;
-    gchar *last_astro_update, *last_data_update, *last_conditions_update;
-    gchar *result;
+    gchar *last_astro_update, *last_weather_update, *last_conditions_update;
+    gchar *next_astro_update, *next_weather_update, *next_conditions_update;
+    gchar *next_wakeup, *result;
 
-    last_astro_update = weather_debug_strftime_t(data->last_astro_update);
-    last_data_update = weather_debug_strftime_t(data->last_data_update);
+    last_astro_update = weather_debug_strftime_t(data->astro_update->last);
+    last_weather_update = weather_debug_strftime_t(data->weather_update->last);
     last_conditions_update =
-        weather_debug_strftime_t(data->last_conditions_update);
+        weather_debug_strftime_t(data->conditions_update->last);
+    next_astro_update = weather_debug_strftime_t(data->astro_update->next);
+    next_weather_update = weather_debug_strftime_t(data->weather_update->next);
+    next_conditions_update =
+        weather_debug_strftime_t(data->conditions_update->next);
+    next_wakeup = weather_debug_strftime_t(data->next_wakeup);
 
     out = g_string_sized_new(1024);
     g_string_assign(out, "xfce_weatherdata:\n");
@@ -425,8 +431,15 @@ weather_dump_plugindata(const plugin_data *data)
                            "  plugin orientation: %d\n"
                            "  --------------------------------------------\n"
                            "  last astro update: %s\n"
-                           "  last data update: %s\n"
+                           "  next astro update: %s\n"
+                           "  astro download attempts: %d\n"
+                           "  last weather update: %s\n"
+                           "  next weather update: %s\n"
+                           "  weather download attempts: %d\n"
                            "  last conditions update: %s\n"
+                           "  next conditions update: %s\n"
+                           "  next scheduled wakeup: %s\n"
+                           "  wakeup reason: %s\n"
                            "  --------------------------------------------\n"
                            "  location name: %s\n"
                            "  latitude: %s\n"
@@ -453,8 +466,15 @@ weather_dump_plugindata(const plugin_data *data)
                            data->panel_orientation,
                            data->orientation,
                            last_astro_update,
-                           last_data_update,
+                           next_astro_update,
+                           data->astro_update->attempt,
+                           last_weather_update,
+                           next_weather_update,
+                           data->weather_update->attempt,
                            last_conditions_update,
+                           next_conditions_update,
+                           next_wakeup,
+                           data->next_wakeup_reason,
                            data->location_name,
                            data->lat,
                            data->lon,
@@ -472,8 +492,12 @@ weather_dump_plugindata(const plugin_data *data)
                            gdk_color_to_string(&(data->scrollbox_color)),
                            YESNO(data->scrollbox_use_color),
                            YESNO(data->scrollbox_animate));
+    g_free(next_wakeup);
+    g_free(next_astro_update);
+    g_free(next_weather_update);
+    g_free(next_conditions_update);
     g_free(last_astro_update);
-    g_free(last_data_update);
+    g_free(last_weather_update);
     g_free(last_conditions_update);
 
     /* Free GString only and return its character data */
diff --git a/panel-plugin/weather.c b/panel-plugin/weather.c
index 71f7ec8..51c8eaf 100644
--- a/panel-plugin/weather.c
+++ b/panel-plugin/weather.c
@@ -38,12 +38,12 @@
 #include "weather-debug.h"
 
 #define XFCEWEATHER_ROOT "weather"
-#define UPDATE_INTERVAL (15)
-#define DATA_MAX_AGE (20 * 60)
 #define CACHE_FILE_MAX_AGE (48 * 3600)
 #define BORDER (8)
-#define CONNECTION_TIMEOUT (10)        /* connection timeout in seconds */
-#define DATA_RETRY_WAIT (180)          /* download retry wait time in seconds */
+#define CONN_TIMEOUT (10)        /* connection timeout in seconds */
+#define CONN_MAX_ATTEMPTS (3)    /* max retry attempts using small interval */
+#define CONN_RETRY_INTERVAL_SMALL (10)
+#define CONN_RETRY_INTERVAL_LARGE (10 * 60)
 
 #define DATA_AND_UNIT(var, item)                                    \
     value = get_data(conditions, data->units, item, data->round);   \
@@ -69,11 +69,20 @@
     if (g_key_file_has_key(keyfile, group, key, NULL))          \
         var = g_key_file_get_string(keyfile, group, key, NULL);
 
+#define SCHEDULE_WAKEUP_COMPARE(var, reason)        \
+    if (difftime(var, now_t) < diff) {              \
+        data->next_wakeup = var;                    \
+        diff = difftime(data->next_wakeup, now_t);  \
+        data->next_wakeup_reason = reason;          \
+    }
+
 
 gboolean debug_mode = FALSE;
 
-static void
-write_cache_file(plugin_data *data);
+
+static void write_cache_file(plugin_data *data);
+
+static void schedule_next_wakeup(plugin_data *data);
 
 
 void
@@ -180,6 +189,38 @@ make_label(const plugin_data *data,
 }
 
 
+static update_info *
+make_update_info(const guint check_interval)
+{
+    update_info *upi;
+
+    upi = g_slice_new0(update_info);
+    if (G_UNLIKELY(upi == NULL))
+        return NULL;
+
+    memset(&upi->last, 0, sizeof(upi->last));
+    upi->next = time(NULL);
+    upi->check_interval = check_interval;
+    return upi;
+}
+
+
+static void
+init_update_infos(plugin_data *data)
+{
+    if (G_LIKELY(data->astro_update))
+        g_slice_free(update_info, data->astro_update);
+    if (G_LIKELY(data->weather_update))
+        g_slice_free(update_info, data->weather_update);
+    if (G_LIKELY(data->conditions_update))
+        g_slice_free(update_info, data->conditions_update);
+
+    data->astro_update = make_update_info(24 * 3600);
+    data->weather_update = make_update_info(20 * 60);
+    data->conditions_update = make_update_info(5 * 60);
+}
+
+
 /*
  * Return the weather plugin cache directory, creating it if
  * necessary. The string returned does not contain a trailing slash.
@@ -296,18 +337,25 @@ update_current_conditions(plugin_data *data)
         data->weatherdata->current_conditions = NULL;
     }
     /* use exact 5 minute intervals for calculation */
-    time(&data->last_conditions_update);
-    now_tm = *localtime(&data->last_conditions_update);
+    time(&data->conditions_update->last);
+    now_tm = *localtime(&data->conditions_update->last);
     now_tm.tm_min -= (now_tm.tm_min % 5);
     if (now_tm.tm_min < 0)
         now_tm.tm_min = 0;
     now_tm.tm_sec = 0;
-    data->last_conditions_update = mktime(&now_tm);
+    data->conditions_update->last = mktime(&now_tm);
 
     data->weatherdata->current_conditions =
         make_current_conditions(data->weatherdata,
-                                data->last_conditions_update);
+                                data->conditions_update->last);
+
+    /* schedule next update */
+    now_tm.tm_min += 5;
+    data->conditions_update->next = mktime(&now_tm);
     data->night_time = is_night_time(data->astrodata);
+    schedule_next_wakeup(data);
+
+    /* update widgets */
     update_icon(data);
     update_scrollbox(data);
     weather_debug("Updated current conditions.");
@@ -315,19 +363,36 @@ update_current_conditions(plugin_data *data)
 
 
 static time_t
-calc_conn_retry_time(gint regular_interval) {
-    time_t now_t = time(NULL);
-    struct tm now_tm;
+calc_next_download_time(const update_info *upi) {
+    time_t retry_t = time(NULL);
+    struct tm retry_tm;
+    guint interval;
 
-    now_tm = *localtime(&now_t);
-    now_t = time_calc(now_tm, 0, 0, 0, 0, 0,
-                      0 - regular_interval + DATA_RETRY_WAIT);
-    weather_debug("Calculated time to delay next retry to %d seconds, is %d.",
-                  DATA_RETRY_WAIT, now_t);
-    return now_t;
+    retry_tm = *localtime(&retry_t);
+
+    /* If the download failed, retry immediately using a small retry
+     * interval for a limited number of times. If it still fails after
+     * that, continue using a larger interval or the default check,
+     * whatever is smaller.
+     */
+    if (G_LIKELY(upi->attempt == 0))
+        interval = upi->check_interval;
+    else if (upi->attempt <= CONN_MAX_ATTEMPTS)
+        interval = CONN_RETRY_INTERVAL_SMALL;
+    else {
+        if (upi->check_interval > CONN_RETRY_INTERVAL_LARGE)
+            interval = CONN_RETRY_INTERVAL_LARGE;
+        else
+            interval = upi->check_interval;
+    }
+
+    return time_calc(retry_tm, 0, 0, 0, 0, 0, interval);
 }
 
 
+/*
+ * Process downloaded astro data and schedule next astro update.
+ */
 static void
 cb_astro_update(SoupSession *session,
                 SoupMessage *msg,
@@ -335,22 +400,36 @@ cb_astro_update(SoupSession *session,
 {
     plugin_data *data = user_data;
     xml_astro *astro;
+    time_t now_t;
+    struct tm now_tm;
 
+    time(&now_t);
+    now_tm = *localtime(&now_t);
     if ((astro =
          (xml_astro *) parse_xml_document(msg, (XmlParseFunc) parse_astro))) {
         if (data->astrodata)
             xml_astro_free(data->astrodata);
         data->astrodata = astro;
-        data->last_astro_update = time(NULL);
+
+        /* schedule next update at 00:00 of the next day */
+        data->astro_update->last = now_t;
+        data->astro_update->next =
+            time_calc(now_tm, 0, 0, 1, 0 - now_tm.tm_hour,
+                      0 - now_tm.tm_min, 0 - now_tm.tm_sec);
+        data->astro_update->attempt = 0;
     } else {
-        /* download or parsing failed, set last_astro_update so that
-           downloading will be retried after DATA_RETRY_WAIT time */
-        data->last_astro_update = calc_conn_retry_time(DATA_MAX_AGE);
+        /* download or parsing failed, schedule retry */
+        data->astro_update->next = calc_next_download_time(data->astro_update);
+        data->astro_update->attempt++;
     }
+    schedule_next_wakeup(data);
     weather_dump(weather_dump_astrodata, data->astrodata);
 }
 
 
+/*
+ * Process downloaded weather data and schedule next weather update.
+ */
 static void
 cb_weather_update(SoupSession *session,
                   SoupMessage *msg,
@@ -359,23 +438,24 @@ cb_weather_update(SoupSession *session,
     plugin_data *data = user_data;
     xmlDoc *doc;
     xmlNode *root_node;
+    time_t now_t;
+    struct tm now_tm;
 
     weather_debug("Processing downloaded weather data.");
+    time(&now_t);
+    now_tm = *localtime(&now_t);
+    data->weather_update->attempt++;
     doc = get_xml_document(msg);
     if (G_LIKELY(doc)) {
         root_node = xmlDocGetRootElement(doc);
-        if (G_LIKELY(root_node)) {
-            if (parse_weather(root_node, data->weatherdata))
-                data->last_data_update = time(NULL);
-            else
-                data->last_data_update = calc_conn_retry_time(DATA_MAX_AGE);
-        }
+        if (G_LIKELY(root_node))
+            if (parse_weather(root_node, data->weatherdata)) {
+                data->weather_update->attempt = 0;
+                data->weather_update->last = now_t;
+            }
         xmlFreeDoc(doc);
-    } else {
-        /* download or parsing failed, set last_data_update so that
-           downloading will be retried after DATA_RETRY_WAIT time */
-        data->last_data_update = calc_conn_retry_time(DATA_MAX_AGE);
     }
+    data->weather_update->next = calc_next_download_time(data->weather_update);
 
     xml_weather_clean(data->weatherdata);
     g_array_sort(data->weatherdata->timeslices,
@@ -383,65 +463,13 @@ cb_weather_update(SoupSession *session,
     weather_debug("Updating current conditions.");
     update_current_conditions(data);
     write_cache_file(data);
+    schedule_next_wakeup(data);
     weather_dump(weather_dump_weatherdata, data->weatherdata);
 }
 
 
 static gboolean
-need_astro_update(const plugin_data *data)
-{
-    time_t now_t;
-    struct tm now_tm, last_tm;
-
-    if (!data->update_timer || !data->last_astro_update)
-        return TRUE;
-
-    time(&now_t);
-    now_tm = *localtime(&now_t);
-    last_tm = *localtime(&(data->last_astro_update));
-    if (now_tm.tm_mday != last_tm.tm_mday)
-        return TRUE;
-
-    return FALSE;
-}
-
-
-static gboolean
-need_data_update(const plugin_data *data)
-{
-    time_t now_t;
-    gint diff;
-
-    if (!data->update_timer || !data->last_data_update)
-        return TRUE;
-
-    time(&now_t);
-    diff = (gint) difftime(now_t, data->last_data_update);
-    if (diff >= DATA_MAX_AGE)
-        return TRUE;
-
-    return FALSE;
-}
-
-
-static gboolean
-need_conditions_update(const plugin_data *data)
-{
-    time_t now_t;
-    struct tm now_tm;
-
-    if (!data->update_timer || !data->last_conditions_update)
-        return TRUE;
-
-    time(&now_t);
-    now_tm = *localtime(&now_t);
-    return (difftime(now_t, data->last_conditions_update) > 300 &&
-            (now_tm.tm_min % 5 == 0 || now_tm.tm_min == 0));
-}
-
-
-static gboolean
-update_weatherdata(plugin_data *data)
+update_handler(plugin_data *data)
 {
     gchar *url;
     gboolean night_time;
@@ -450,18 +478,18 @@ update_weatherdata(plugin_data *data)
 
     g_assert(data != NULL);
     if (G_UNLIKELY(data == NULL))
-        return TRUE;
+        return FALSE;
 
-    if ((!data->lat || !data->lon) ||
-        strlen(data->lat) == 0 || strlen(data->lon) == 0) {
+    if (G_UNLIKELY(data->lat == NULL || data->lon == NULL)) {
         update_icon(data);
         update_scrollbox(data);
-        return TRUE;
+        return FALSE;
     }
 
+    now_t = time(NULL);
+
     /* fetch astronomical data */
-    if (need_astro_update(data)) {
-        now_t = time(NULL);
+    if (difftime(data->astro_update->next, now_t) <= 0) {
         now_tm = *localtime(&now_t);
 
         /* build url */
@@ -479,7 +507,7 @@ update_weatherdata(plugin_data *data)
     }
 
     /* fetch weather data */
-    if (need_data_update(data)) {
+    if (difftime(data->weather_update->next, now_t) <= 0) {
         /* build url */
         url =
             g_strdup_printf("http://api.yr.no/weatherapi"
@@ -494,11 +522,11 @@ update_weatherdata(plugin_data *data)
 
         /* cb_update will deal with everything that follows this
          * block, so let's return instead of doing things twice */
-        return TRUE;
+        return FALSE;
     }
 
     /* update current conditions, icon and labels */
-    if (need_conditions_update(data)) {
+    if (difftime(data->conditions_update->next, now_t) <= 0) {
         weather_debug("Updating current conditions.");
         update_current_conditions(data);
     }
@@ -511,8 +539,46 @@ update_weatherdata(plugin_data *data)
         update_icon(data);
     }
 
-    /* keep timeout running */
-    return TRUE;
+    schedule_next_wakeup(data);
+    return FALSE;
+}
+
+
+static void
+schedule_next_wakeup(plugin_data *data)
+{
+    time_t now_t = time(NULL), future_t;
+    struct tm now_tm;
+    gdouble diff;
+
+    if (data->update_timer) {
+        g_source_remove(data->update_timer);
+        data->update_timer = 0;
+    }
+
+    now_tm = *localtime(&now_t);
+    future_t = time_calc_day(now_tm, 1);
+    diff = difftime(future_t, now_t);
+    SCHEDULE_WAKEUP_COMPARE(data->astro_update->next,
+                            "astro data download");
+    SCHEDULE_WAKEUP_COMPARE(data->weather_update->next,
+                            "weather data download");
+    SCHEDULE_WAKEUP_COMPARE(data->conditions_update->next,
+                            "current conditions update");
+    /* If astronomical data is unavailable, current conditions update
+       will usually handle night/day. */
+    if (data->astrodata) {
+        if (difftime(data->astrodata->sunrise, now_t) > 0)
+            SCHEDULE_WAKEUP_COMPARE(data->astrodata->sunrise,
+                                    "sunrise icon change");
+        if (difftime(data->astrodata->sunset, now_t) > 0)
+            SCHEDULE_WAKEUP_COMPARE(data->astrodata->sunset,
+                                    "sunset icon change");
+    }
+    data->update_timer =
+        g_timeout_add_seconds((guint) diff,
+                              (GSourceFunc) update_handler, data);
+    weather_dump(weather_dump_plugindata, data);
 }
 
 
@@ -1011,10 +1077,8 @@ update_weatherdata_with_reset(plugin_data *data, gboolean clear)
         data->update_timer = 0;
     }
 
-    memset(&data->last_data_update, 0, sizeof(data->last_data_update));
-    memset(&data->last_astro_update, 0, sizeof(data->last_astro_update));
-    memset(&data->last_conditions_update, 0,
-           sizeof(data->last_conditions_update));
+    /* reset update times */
+    init_update_infos(data);
 
     /* clear existing weather data, needed for location changes */
     if (clear && data->weatherdata) {
@@ -1024,13 +1088,7 @@ update_weatherdata_with_reset(plugin_data *data, gboolean clear)
         /* make use of previously saved data */
         read_cache_file(data);
     }
-
-    update_weatherdata(data);
-
-    data->update_timer =
-        g_timeout_add_seconds(UPDATE_INTERVAL,
-                              (GSourceFunc) update_weatherdata,
-                              data);
+    schedule_next_wakeup(data);
     weather_debug("Updated weatherdata with reset.");
 }
 
@@ -1369,10 +1427,14 @@ xfceweather_create_control(XfcePanelPlugin *plugin)
     data->forecast_days = DEFAULT_FORECAST_DAYS;
     data->round = TRUE;
 
+    /* Setup update infos */
+    init_update_infos(data);
+    data->next_wakeup = time(NULL);
+
     /* Setup session for HTTP connections */
     data->session = soup_session_async_new();
     g_object_set(data->session, SOUP_SESSION_TIMEOUT,
-                 CONNECTION_TIMEOUT, NULL);
+                 CONN_TIMEOUT, NULL);
 
     /* Set the proxy URI from environment */
     proxy_uri = g_getenv("HTTP_PROXY");
@@ -1455,10 +1517,8 @@ xfceweather_create_control(XfcePanelPlugin *plugin)
     gtk_scrollbox_set_label(GTK_SCROLLBOX(data->scrollbox), -1, "1");
     gtk_scrollbox_clear(GTK_SCROLLBOX(data->scrollbox));
 
-    data->update_timer =
-        g_timeout_add_seconds(UPDATE_INTERVAL,
-                              (GSourceFunc) update_weatherdata,
-                              data);
+    /* prepare data and widget updates */
+    schedule_next_wakeup(data);
 
     weather_debug("Plugin widgets set up and ready.");
     return data;
@@ -1472,6 +1532,11 @@ xfceweather_free(XfcePanelPlugin *plugin,
     weather_debug("Freeing plugin data.");
     g_assert(data != NULL);
 
+    if (data->update_timer) {
+        g_source_remove(data->update_timer);
+        data->update_timer = 0;
+    }
+
     if (data->weatherdata)
         xml_weather_free(data->weatherdata);
 
@@ -1481,11 +1546,6 @@ xfceweather_free(XfcePanelPlugin *plugin,
     if (data->units)
         g_slice_free(units_config, data->units);
 
-    if (data->update_timer) {
-        g_source_remove(data->update_timer);
-        data->update_timer = 0;
-    }
-
     xmlCleanupParser();
 
     /* free chars */
@@ -1494,14 +1554,18 @@ xfceweather_free(XfcePanelPlugin *plugin,
     g_free(data->location_name);
     g_free(data->scrollbox_font);
 
-    /* free array */
+    /* free update infos */
+    g_slice_free(update_info, data->weather_update);
+    g_slice_free(update_info, data->astro_update);
+    g_slice_free(update_info, data->conditions_update);
+
+    /* free label array */
     g_array_free(data->labels, TRUE);
 
     /* free icon theme */
     icon_theme_free(data->icon_theme);
 
     g_slice_free(plugin_data, data);
-    data = NULL;
 }
 
 
@@ -1692,8 +1756,6 @@ weather_construct(XfcePanelPlugin *plugin)
                      G_CALLBACK(xfceweather_show_about), data);
 
     weather_dump(weather_dump_plugindata, data);
-
-    update_weatherdata(data);
 }
 
 XFCE_PANEL_PLUGIN_REGISTER(weather_construct)
diff --git a/panel-plugin/weather.h b/panel-plugin/weather.h
index 61b4095..c307415 100644
--- a/panel-plugin/weather.h
+++ b/panel-plugin/weather.h
@@ -50,6 +50,13 @@ typedef struct {
 } summary_details;
 
 typedef struct {
+    time_t last;
+    time_t next;
+    guint attempt;
+    guint check_interval;
+} update_info;
+
+typedef struct {
     XfcePanelPlugin *plugin;
 
     SoupSession *session;
@@ -68,9 +75,12 @@ typedef struct {
     GtkOrientation panel_orientation;
     xml_weather *weatherdata;
     xml_astro *astrodata;
-    time_t last_astro_update;
-    time_t last_data_update;
-    time_t last_conditions_update;
+
+    update_info *astro_update;
+    update_info *weather_update;
+    update_info *conditions_update;
+    time_t next_wakeup;
+    gchar *next_wakeup_reason;
     guint update_timer;
 
     GtkWidget *scrollbox;


More information about the Xfce4-commits mailing list