Re: supporting notifications from backends



Hi Cyrille,

On Fri, 2004-03-26 at 09:40, Cyrille Moureaux wrote:

>  From the point of view of the relationship between the backend and the 
> daemon, the addition of yet another client listener for 
> /apps/metacity/general when there's already a client listening for 
> /apps/metacity shouldn't change the state of the backend, it should 
> still only report changes to anything below /apps/metacity to the daemon 
> for further action.
> 
> If there's an add_listener called for each client listener, all backends 
> have to do some comparisons to realise that the same callback/user data 
> pair is being passed each time so that they can ignore most of the 
> requests, otherwise if they just assume all listeners passed are somehow 
> different, everytime something changes in the store the daemon is going 
> to be called once per client listening to that point.

	Ah, I hadn't thought of that extra complexity for the backends. Because
of that and the fact that, as you say, allowing a different callback per
listener is useless I've added back a set_notify_func() vtable member.

> + GList *tmp;
> +
> +  tmp = sources->sources;
> +
> +  while (sources != NULL)
> 
> I think it should be "while (tmp != NULL)" since sources is unlikely to 
> change (hopefully) and it's a bit late to check its nullity when it's 
> already been dereferenced.

	Well spotted, thanks.

	Attaching how she's looking now. Let me know if you see any further
problems with this. 

	It should be in CVS by tommorrow. I'd really appreciate it if you could
try updating your backend then and making sure everything is okay.

Thanks,
Mark.
Index: ChangeLog
===================================================================
RCS file: /home/markmc/gnome-devel/local-repository/gconf/ChangeLog,v
retrieving revision 1.490
retrieving revision 1.494
diff -u -p -r1.490 -r1.494
--- ChangeLog	24 Mar 2004 20:02:56 -0000	1.490
+++ ChangeLog	29 Mar 2004 09:52:54 -0000	1.494
@@ -1,1 +1,65 @@
+2004-03-29  Mark McLoughlin  <mark skynet ie>
+
+	* backends/markup-backend.c,
+	  backends/xml-backend.c: initialize the set_notify_func
+	vtable member.
+
+2004-03-29  Mark McLoughlin  <mark skynet ie>
+
+	Re-work the notifications-from-backends patch so that the
+	backend doesn't have to keep track of a callback per
+	listener as suggested by Cyrille.
+
+	* gconf/gconf-backend.h: add a set_notify_func() member	to
+	the vtable and remove the callback arg from add_listener().
+	
+	* gconf/gconf-database.c:
+	(gconf_database_new): set the notification callback here.
+	(gconf_database_readd_listener): upd.
+	
+	* gconf/gconf-sources.[ch]:
+	(gconf_source_set_notify_func): add.
+	(gconf_source_add_listener): upd.
+	(gconf_sources_set_notify_func): add.
+	(gconf_sources_add_listener): upd.
+
+2004-03-26  Mark McLoughlin  <mark skynet ie>
+
+	* gconf/gconf-database.c: (source_notify_cb): don't leak
+	the schema name.
+	
+	* gconf/gconf-sources.c:
+	(gconf_sources_add_listener),
+	(gconf_sources_remove_listener): fix mistake pointed out
+	by Cyrille.
+
+2004-03-25  Mark McLoughlin  <mark skynet ie>
+
+	Allow backends to notify the daemon of changes to entries.
+	Based on a patch from Cyrille Moureaux <Cyrille Moureaux Sun COM>
+	in bug #07692.
+
+	* gconf/gconf-backend.h: add add_listener() and
+	remove_listener() members to the vtable.
+	
+	* gconf/gconf-database.c:
+	(source_notify_cb): re-compute the value and notify
+	listeners when the backend reports the key has changed.
+	(gconf_database_readd_listener),
+	(gconf_database_remove_listener): add/remove backend
+	listeners.
+	
+	* gconf/gconf-sources.[c]:
+	(gconf_source_add_listener),
+	(gconf_source_remove_listener),
+	(gconf_sources_add_listener),
+	(gconf_sources_remove_listener): impl. the glue.
+	
+	* doc/gconf/tmpl/gconf-backend.sgml: update the backend
+	documentation.
+	
+	* backends/markup-backend.c,
+	  backends/xml-backend.c: set the add_listener() and
+	remove_listener() members to NULL.
+	
Index: backends/markup-backend.c
===================================================================
RCS file: /home/markmc/gnome-devel/local-repository/gconf/backends/markup-backend.c,v
retrieving revision 1.8
retrieving revision 1.10
diff -u -p -r1.8 -r1.10
--- backends/markup-backend.c	24 Mar 2004 20:03:01 -0000	1.8
+++ backends/markup-backend.c	29 Mar 2004 09:52:58 -0000	1.10
@@ -151,7 +151,10 @@ static GConfBackendVTable markup_vtable 
   sync_all,
   destroy_source,
   clear_cache,
-  blow_away_locks
+  blow_away_locks,
+  NULL, /* set_notify_func */
+  NULL, /* add_listener    */
+  NULL  /* remove_listener */
 };
 
 static void          
Index: backends/xml-backend.c
===================================================================
RCS file: /home/markmc/gnome-devel/local-repository/gconf/backends/xml-backend.c,v
retrieving revision 1.86
retrieving revision 1.88
diff -u -p -r1.86 -r1.88
--- backends/xml-backend.c	24 Mar 2004 20:03:01 -0000	1.86
+++ backends/xml-backend.c	29 Mar 2004 09:52:58 -0000	1.88
@@ -228,7 +228,10 @@ static GConfBackendVTable xml_vtable = {
   sync_all,
   destroy_source,
   clear_cache,
-  blow_away_locks
+  blow_away_locks,
+  NULL, /* set_notify_func */
+  NULL, /* add_listener    */
+  NULL  /* remove_listener */
 };
 
 static void          
Index: doc/gconf/tmpl/gconf-backend.sgml
===================================================================
RCS file: /home/markmc/gnome-devel/local-repository/gconf/doc/gconf/tmpl/gconf-backend.sgml,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -p -r1.8 -r1.9
--- doc/gconf/tmpl/gconf-backend.sgml	24 Mar 2004 20:03:05 -0000	1.8
+++ doc/gconf/tmpl/gconf-backend.sgml	25 Mar 2004 17:59:07 -0000	1.9
@@ -23,13 +23,23 @@ called gconf_backend_get_vtable() that r
 </para>
 
 <para>
-Here are the methods the backend must implement, and their specification:
+Here is the specification of the vtable members:
 
 <informaltable pgwide="1" frame="none">
 <tgroup cols="2"><colspec colwidth="2*"/><colspec colwidth="8*"/>
 <tbody>
 
 <row>
+<entry>@vtable_size</entry> 
+<entry>
+The size of the vtable structure. This is used by the daemon to ensure that
+a mismatch between the version of GConf the backend was compiled against and
+the version the daemon was compiled against can be handled gracefully. Set
+this field to sizeof (GConfBackendVtable).
+</entry>
+</row>
+ 
+<row>
 <entry>@shutdown</entry> 
 <entry>Called prior to unloading the dynamic
 module. Should ensure that no functions or static/global variables from the
@@ -227,7 +237,67 @@ Should destroy a source obtained with @r
 
 </row>
  
+<row>
+<entry>@clear_cache</entry>
+
+<entry>
+Discard any cached data after saving the data to permanent storage.
+</entry>
+
+</row>
+ 
+<row>
+<entry>@blow_away_locks</entry>
+
+<entry>
+Unconditionally discard any locks whether they are stale or otherwise in
+order to force the backend to be able to obtain access to its data store.
+</entry>
+
+</row>
+ 
+<row>
+<entry>@add_listener</entry>
+
+<entry>
+If it is possible for entries to be changed concurrently by another
+daemon, the backend may support notifying the daemon (and any listening
+clients) of such changes. This function should add a listener to a
+section of the tree and when any of the following events occur, the
+backend should invoke the notify function with the key that has changed:
+ <itemizedlist mark="bullet">
+  <listitem>
+    <para>If the entry is set or unset</para>
+  </listitem>
+  <listitem>
+    <para>If the entry's value changes</para>
+  </listitem>
+  <listitem>
+    <para>If the entry's schema name changes</para>
+  </listitem>
+  <listitem>
+    <para>
+      If the entry is a schema and its value in <emphasis>any</emphasis>
+      locale changes
+    </para>
+  </listitem>
+ </itemizedlist>
+Note, the backend should <emphasis>not</emphasis> notify the daemon of
+any changes that originated from the daemon itself.
+</entry>
+
+</row>
+
+<row>
+<entry>@remove_listener</entry>
+
+<entry>
+Remove a listener added with @add_listener. The listener is identified
+by the integer supplied.
+</entry>
 
+</row>
+ 
 </tbody></tgroup></informaltable>
 </para>
 
@@ -251,6 +321,8 @@ Should destroy a source obtained with @r
 @destroy_source: 
 @clear_cache: 
 @blow_away_locks: 
+ add_listener: 
+ remove_listener: 
 
 <!-- ##### STRUCT GConfBackend ##### -->
 <para>
Index: gconf/gconf-backend.h
===================================================================
RCS file: /home/markmc/gnome-devel/local-repository/gconf/gconf/gconf-backend.h,v
retrieving revision 1.31
retrieving revision 1.33
diff -u -p -r1.31 -r1.33
--- gconf/gconf-backend.h	24 Mar 2004 20:03:09 -0000	1.31
+++ gconf/gconf-backend.h	29 Mar 2004 09:47:33 -0000	1.33
@@ -133,6 +133,17 @@ struct _GConfBackendVTable {
 
   /* used by gconf-sanity-check */
   void                (* blow_away_locks) (const char *address);
+
+  void                (* set_notify_func) (GConfSource           *source,
+					   GConfSourceNotifyFunc  notify_func,
+					   gpointer               user_data);
+
+  void                (* add_listener)    (GConfSource           *source,
+					   guint                  id,
+					   const gchar           *namespace_section);
+
+  void                (* remove_listener) (GConfSource           *source,
+					   guint                  id);
 };
 
 struct _GConfBackend {
Index: gconf/gconf-database.c
===================================================================
RCS file: /home/markmc/gnome-devel/local-repository/gconf/gconf/gconf-database.c,v
retrieving revision 1.33
retrieving revision 1.36
diff -u -p -r1.33 -r1.36
--- gconf/gconf-database.c	24 Mar 2004 20:03:10 -0000	1.33
+++ gconf/gconf-database.c	29 Mar 2004 09:47:34 -0000	1.36
@@ -800,7 +800,10 @@ static POA_ConfigDatabase3__epv server3_
 
 static POA_ConfigDatabase3__vepv poa_server_vepv = { &base_epv, &server_epv, &server2_epv, &server3_epv };
 
-static void gconf_database_really_sync(GConfDatabase* db);
+static void gconf_database_really_sync (GConfDatabase *db);
+static void source_notify_cb           (GConfSource   *source,
+					const gchar   *location,
+					GConfDatabase *db);
 
 GConfDatabase*
 gconf_database_new (GConfSources  *sources)
@@ -832,6 +835,10 @@ gconf_database_new (GConfSources  *sourc
 
   db->sources = sources;
 
+  gconf_sources_set_notify_func (db->sources,
+				 (GConfSourceNotifyFunc) source_notify_cb,
+				 db);
+
   db->last_access = time(NULL);
 
   db->sync_idle = 0;
@@ -1027,6 +1034,58 @@ gconf_database_schedule_sync(GConfDataba
     }
 }
 
+static void
+source_notify_cb (GConfSource   *source,
+		  const gchar   *location,
+		  GConfDatabase *db)
+{
+  GConfValue  *value;
+  ConfigValue *cvalue;
+  GError      *error;
+  gboolean     is_default;
+  gboolean     is_writable;
+
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (location != NULL);
+  g_return_if_fail (db != NULL);
+
+  error = NULL;
+  is_default = is_writable = FALSE;
+
+  /* FIXME: only notify if this location isn't already set
+   *        in a source above this one.
+   */
+
+  value = gconf_database_query_value (db,
+				      location,
+				      NULL,
+				      TRUE,
+				      NULL,
+				      &is_default,
+				      &is_writable,
+				      &error);
+  if (error != NULL)
+    {
+      gconf_log (GCL_WARNING,
+		 _("Error obtaining new value for `%s' after change notification from backend `%s': %s"),
+		 location,
+		 source->address,
+		 error->message);
+      g_error_free (error);
+      return;
+    }
+
+  cvalue = gconf_corba_value_from_gconf_value (value);
+  gconf_database_notify_listeners (db,
+				   location,
+				   cvalue,
+				   is_default,
+				   is_writable);
+
+  CORBA_free (cvalue);
+  gconf_value_free (value);
+}
+
 CORBA_unsigned_long
 gconf_database_readd_listener   (GConfDatabase       *db,
                                  ConfigListener       who,
@@ -1047,6 +1106,8 @@ gconf_database_readd_listener   (GConfDa
   cnxn = gconf_listeners_add (db->listeners, where, l,
                               (GFreeFunc)listener_destroy);
 
+  gconf_sources_add_listener (db->sources, cnxn, where);
+
   if (l->name == NULL)
     l->name = g_strdup_printf ("%u", cnxn);
   
@@ -1130,6 +1191,8 @@ gconf_database_remove_listener (GConfDat
       g_error_free (err);
     }
   
+  gconf_sources_remove_listener (db->sources, cnxn);
+
   /* calls destroy notify */
   gconf_listeners_remove (db->listeners, cnxn);
 }
Index: gconf/gconf-sources.c
===================================================================
RCS file: /home/markmc/gnome-devel/local-repository/gconf/gconf/gconf-sources.c,v
retrieving revision 1.44
retrieving revision 1.47
diff -u -p -r1.44 -r1.47
--- gconf/gconf-sources.c	24 Mar 2004 20:03:10 -0000	1.44
+++ gconf/gconf-sources.c	29 Mar 2004 09:47:34 -0000	1.47
@@ -288,6 +288,46 @@ gconf_source_sync_all         (GConfSour
   return (*source->backend->vtable.sync_all)(source, err);
 }
 
+static void
+gconf_source_set_notify_func (GConfSource           *source,
+			      GConfSourceNotifyFunc  notify_func,
+			      gpointer               user_data)
+{
+  g_return_if_fail (source != NULL);
+
+  if (source->backend->vtable.set_notify_func)
+    {
+      (*source->backend->vtable.set_notify_func) (source, notify_func, user_data);
+    }
+}
+
+static void
+gconf_source_add_listener (GConfSource *source,
+			   guint        id,
+			   const gchar *namespace_section)
+{
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (id > 0);
+
+  if (source->backend->vtable.add_listener)
+    {
+      (*source->backend->vtable.add_listener) (source, id, namespace_section);
+    }
+}
+
+static void
+gconf_source_remove_listener (GConfSource *source,
+			      guint        id)
+{
+  g_return_if_fail (source != NULL);
+  g_return_if_fail (id > 0);
+
+  if (source->backend->vtable.remove_listener)
+    {
+      (*source->backend->vtable.remove_listener) (source, id);
+    }
+}
+
 /*
  *   Source stacks
  */
@@ -1548,5 +1588,52 @@ gconf_sources_query_default_value(GConfS
       gconf_meta_info_free(mi);
       
       return NULL;
+    }
+}
+
+void
+gconf_sources_set_notify_func (GConfSources          *sources,
+			       GConfSourceNotifyFunc  notify_func,
+			       gpointer               user_data)
+{
+  GList *tmp;
+
+  tmp = sources->sources;
+  while (tmp != NULL)
+    {
+      gconf_source_set_notify_func (tmp->data, notify_func, user_data);
+
+      tmp = tmp->next;
+    }
+}
+
+void
+gconf_sources_add_listener (GConfSources *sources,
+			    guint         id,
+			    const gchar  *namespace_section)
+{
+  GList *tmp;
+
+  tmp = sources->sources;
+  while (tmp != NULL)
+    {
+      gconf_source_add_listener (tmp->data, id, namespace_section);
+
+      tmp = tmp->next;
+    }
+}
+
+void
+gconf_sources_remove_listener (GConfSources *sources,
+			       guint         id)
+{
+  GList *tmp;
+
+  tmp = sources->sources;
+  while (tmp != NULL)
+    {
+      gconf_source_remove_listener (tmp->data, id);
+
+      tmp = tmp->next;
     }
 }
Index: gconf/gconf-sources.h
===================================================================
RCS file: /home/markmc/gnome-devel/local-repository/gconf/gconf/gconf-sources.h,v
retrieving revision 1.21
retrieving revision 1.23
diff -u -p -r1.21 -r1.23
--- gconf/gconf-sources.h	18 Jun 2003 10:39:40 -0000	1.21
+++ gconf/gconf-sources.h	29 Mar 2004 09:47:34 -0000	1.23
@@ -24,6 +24,7 @@
 #include <glib.h>
 #include "gconf-error.h"
 #include "gconf-value.h"
+#include "gconf-listeners.h"
 
 /* Sources are not interchangeable; different backend engines will return 
  * GConfSource with different private elements.
@@ -50,6 +51,10 @@ typedef enum {
   GCONF_SOURCE_ALL_FLAGS = ((1 << 0) | (1 << 1))
 } GConfSourceFlags;
 
+typedef void (* GConfSourceNotifyFunc) (GConfSource *source,
+					const gchar *location,
+					gpointer     user_data);
+
 GConfSource*  gconf_resolve_address         (const gchar* address,
                                              GError** err);
 
@@ -124,11 +129,13 @@ GConfValue*   gconf_sources_query_defaul
                                                 gboolean* is_writable,
                                                 GError** err);
 
-#endif
-
-
-
-
-
-
+void          gconf_sources_set_notify_func    (GConfSources          *sources,
+					        GConfSourceNotifyFunc  notify_func,
+					        gpointer               user_data);
+void          gconf_sources_add_listener       (GConfSources          *sources,
+						guint                  id,
+					        const gchar           *location);
+void          gconf_sources_remove_listener    (GConfSources          *sources,
+						guint                  id);
 
+#endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]