g_time_zone_new_local: cache the result
authorRyan Lortie <desrt@desrt.ca>
Sat, 19 Mar 2011 03:09:52 +0000 (23:09 -0400)
committerRyan Lortie <desrt@desrt.ca>
Thu, 31 Mar 2011 07:17:02 +0000 (12:47 +0530)
Add a function to drop the cache.

docs/reference/glib/glib-sections.txt
glib/gtimezone.c
glib/gtimezone.h

index ca512b2..c1c68b0 100644 (file)
@@ -1427,6 +1427,8 @@ g_time_zone_new
 g_time_zone_new_local
 g_time_zone_new_utc
 <SUBSECTION>
+g_time_zone_refresh_local
+<SUBSECTION>
 GTimeType
 g_time_zone_find_interval
 g_time_zone_adjust_time
index c8cd948..637a5ad 100644 (file)
@@ -129,6 +129,9 @@ struct _GTimeZone
   gint     ref_count;
 };
 
+G_LOCK_DEFINE_STATIC (local_timezone);
+static GTimeZone *local_timezone;
+
 G_LOCK_DEFINE_STATIC (time_zones);
 static GHashTable/*<string?, GTimeZone>*/ *time_zones;
 
@@ -147,6 +150,22 @@ g_time_zone_unref (GTimeZone *tz)
 
   if (g_atomic_int_dec_and_test (&tz->ref_count))
     {
+      if G_UNLIKELY (tz == local_timezone)
+        {
+          g_critical ("The last reference on the local timezone was just "
+                      "dropped, but GTimeZone itself still owns one.  This "
+                      "means that g_time_zone_unref() was called too many "
+                      "times.  Restoring the refcount to 1.");
+
+          /* We don't want to just inc this back again since if there
+           * are refcounting bugs in the code then maybe we are already
+           * at -1 and inc will just take us back to 0.  Set to 1 to be
+           * sure.
+           */
+          tz->ref_count = 1;
+          return;
+        }
+
       if (tz->name != NULL)
         {
           G_LOCK(time_zones);
@@ -449,7 +468,43 @@ g_time_zone_new_utc (void)
 GTimeZone *
 g_time_zone_new_local (void)
 {
-  return g_time_zone_new (getenv ("TZ"));
+  GTimeZone *result;
+
+  G_LOCK (local_timezone);
+  if (local_timezone == NULL)
+    local_timezone = g_time_zone_new (getenv ("TZ"));
+
+  result = g_time_zone_ref (local_timezone);
+  G_UNLOCK (local_timezone);
+
+  return result;
+}
+
+/**
+ * g_time_zone_refresh_local:
+ *
+ * Notifies #GTimeZone that the local timezone may have changed.
+ *
+ * In response, #GTimeZone will drop its cache of the local time zone.
+ * No existing #GTimeZone will be modified and no #GDateTime will change
+ * its timezone but future calls to g_time_zone_new_local() will start
+ * returning the new timezone.
+ *
+ * #GTimeZone does no monitoring of the local timezone on its own, which
+ * is why you have to call this function to notify it of the change.
+ **/
+void
+g_time_zone_refresh_local (void)
+{
+  GTimeZone *drop_this_ref = NULL;
+
+  G_LOCK (local_timezone);
+  drop_this_ref = local_timezone;
+  local_timezone = NULL;
+  G_UNLOCK (local_timezone);
+
+  if (drop_this_ref)
+    g_time_zone_unref (drop_this_ref);
 }
 
 /* Internal helpers {{{1 */
index 8bc6684..bc72c20 100644 (file)
@@ -54,6 +54,8 @@ typedef enum
   G_TIME_TYPE_UNIVERSAL
 } GTimeType;
 
+void                    g_time_zone_refresh_local                       (void);
+
 GTimeZone *             g_time_zone_new                                 (const gchar *identifier);
 GTimeZone *             g_time_zone_new_utc                             (void);
 GTimeZone *             g_time_zone_new_local                           (void);