[geany/geany] 27a073: Make plugin_signal_connect() safe on any object

Colomban Wendling git-noreply at xxxxx
Sun Apr 13 17:59:37 UTC 2014


Branch:      refs/heads/master
Author:      Colomban Wendling <ban at herbesfolles.org>
Committer:   Colomban Wendling <ban at herbesfolles.org>
Date:        Sun, 13 Apr 2014 17:59:37 UTC
Commit:      27a073f1a65c60e0568a5b379a6eaad385c717b0
             https://github.com/geany/geany/commit/27a073f1a65c60e0568a5b379a6eaad385c717b0

Log Message:
-----------
Make plugin_signal_connect() safe on any object

Watch the lifetime of objects referenced in plugin->signal_ids and
remove our references to them if they get destroyed.  This avoids
possibly trying to disconnect signals on destroyed objects when the
plugin is unloaded.

Supporting this case is safer, and is useful for objects that may or
may not outlive the plugin (like ScintillaObjects), because in such
cases plugin_signal_connect() is handy to make sure the signals are
disconnected if the object is still alive, but used to crash if the
object was destroyed.


Modified Paths:
--------------
    src/plugindata.h
    src/pluginprivate.h
    src/plugins.c
    src/pluginutils.c

Modified: src/plugindata.h
2 files changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -55,7 +55,7 @@ G_BEGIN_DECLS
  * @warning You should not test for values below 200 as previously
  * @c GEANY_API_VERSION was defined as an enum value, not a macro.
  */
-#define GEANY_API_VERSION 217
+#define GEANY_API_VERSION 218
 
 /* hack to have a different ABI when built with GTK3 because loading GTK2-linked plugins
  * with GTK3-linked Geany leads to crash */


Modified: src/pluginprivate.h
3 files changed, 3 insertions(+), 0 deletions(-)
===================================================================
@@ -61,4 +61,7 @@ GeanyPluginPrivate;
 typedef GeanyPluginPrivate Plugin;	/* shorter alias */
 
 
+void plugin_watch_object(Plugin *plugin, gpointer object);
+
+
 #endif /* GEANY_PLUGINPRIVATE_H */


Modified: src/plugins.c
34 files changed, 34 insertions(+), 0 deletions(-)
===================================================================
@@ -769,6 +769,37 @@ plugin_new(const gchar *fname, gboolean init_plugin, gboolean add_to_list)
 }
 
 
+static void on_object_weak_notify(gpointer data, GObject *old_ptr)
+{
+	Plugin *plugin = data;
+	guint i = 0;
+
+	g_return_if_fail(plugin && plugin->signal_ids);
+
+	for (i = 0; i < plugin->signal_ids->len; i++)
+	{
+		SignalConnection *sc = &g_array_index(plugin->signal_ids, SignalConnection, i);
+
+		if (sc->object == old_ptr)
+		{
+			g_array_remove_index_fast(plugin->signal_ids, i);
+			/* we can break the loop right after finding the first match,
+			 * because we will get one notification per connected signal */
+			break;
+		}
+	}
+}
+
+
+/* add an object to watch for destruction, and release pointers to it when destroyed.
+ * this should only be used by plugin_signal_connect() to add a watch on
+ * the object lifetime and nuke out references to it in plugin->signal_ids */
+void plugin_watch_object(Plugin *plugin, gpointer object)
+{
+	g_object_weak_ref(object, on_object_weak_notify, plugin);
+}
+
+
 static void remove_callbacks(Plugin *plugin)
 {
 	GArray *signal_ids = plugin->signal_ids;
@@ -778,7 +809,10 @@ static void remove_callbacks(Plugin *plugin)
 		return;
 
 	foreach_array(SignalConnection, sc, signal_ids)
+	{
 		g_signal_handler_disconnect(sc->object, sc->handler_id);
+		g_object_weak_unref(sc->object, on_object_weak_notify, plugin);
+	}
 
 	g_array_free(signal_ids, TRUE);
 }


Modified: src/pluginutils.c
11 files changed, 9 insertions(+), 2 deletions(-)
===================================================================
@@ -105,7 +105,8 @@ void plugin_module_make_resident(GeanyPlugin *plugin)
  * @param user_data The user data passed to the signal handler.
  * @see plugin_callbacks.
  *
- * @warning This should only be used on objects that outlive the plugin, never on
+ * @warning Before version 1.25 (API < 218),
+ *          this should only be used on objects that outlive the plugin, never on
  *          objects that will get destroyed before the plugin is unloaded.  For objects
  *          created and destroyed by the plugin, you can simply use @c g_signal_connect(),
  *          since all handlers are disconnected when the object is destroyed anyway.
@@ -116,7 +117,10 @@ void plugin_module_make_resident(GeanyPlugin *plugin)
  *          disconnect the signal on @c plugin_cleanup()), and when the object is destroyed
  *          during the plugin's lifetime (in which case you cannot and should not disconnect
  *          manually in @c plugin_cleanup() since it already has been disconnected and the
- *          object has been destroyed), and disconnect yourself or not as appropriate. */
+ *          object has been destroyed), and disconnect yourself or not as appropriate.
+ * @note Since version 1.25 (API >= 218), the object lifetime is watched and so the above
+ *       restriction does not apply.  However, for objects destroyed by the plugin,
+ *       @c g_signal_connect() is safe and has lower overhead. */
 void plugin_signal_connect(GeanyPlugin *plugin,
 		GObject *object, const gchar *signal_name, gboolean after,
 		GCallback callback, gpointer user_data)
@@ -137,6 +141,9 @@ void plugin_signal_connect(GeanyPlugin *plugin,
 	sc.object = object;
 	sc.handler_id = id;
 	g_array_append_val(plugin->priv->signal_ids, sc);
+
+	/* watch the object lifetime to nuke our pointers to it */
+	plugin_watch_object(plugin->priv, object);
 }
 
 



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list