SF.net SVN: geany:[5650] trunk

colombanw at users.sourceforge.net colombanw at xxxxx
Tue Mar 29 23:20:15 UTC 2011


Revision: 5650
          http://geany.svn.sourceforge.net/geany/?rev=5650&view=rev
Author:   colombanw
Date:     2011-03-29 23:20:14 +0000 (Tue, 29 Mar 2011)

Log Message:
-----------
Add plugin_{idle_add,timeout_add,timeout_add_seconds}() to the plugin API

These functions does the same as the corresponding GLib functions but
makes sure that the added GSource will be removed when the plugin is
unloaded, preventing possible crashes.

These are only convenience functions for the plugin author not to have to
care about the case the plugin gets unloaded, he can still manually
manage hes GSources if he wants to.

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

Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2011-03-29 18:06:26 UTC (rev 5649)
+++ trunk/ChangeLog	2011-03-29 23:20:14 UTC (rev 5650)
@@ -1,3 +1,13 @@
+2011-03-30  Colomban Wendling  <colomban(at)geany(dot)org>
+
+ * src/plugindata.h, src/pluginprivate.h, src/plugins.c,
+   src/pluginutils.c, src/pluginutils.h plugins/geanyfunctions.h:
+   Add plugin_idle_add(), plugin_timeout_add() and
+   plugin_timeout_add_seconds() to the plugin API. These are
+   convenience wrappers to ensure the added timeouts are properly
+   removed when unloading the plugin, preventing possible crashes.
+
+
 2011-03-29  Nick Treleaven  <nick(dot)treleaven(at)btinternet(dot)com>
 
  * doc/geany.txt, doc/geany.html:

Modified: trunk/plugins/geanyfunctions.h
===================================================================
--- trunk/plugins/geanyfunctions.h	2011-03-29 18:06:26 UTC (rev 5649)
+++ trunk/plugins/geanyfunctions.h	2011-03-29 23:20:14 UTC (rev 5650)
@@ -26,6 +26,12 @@
 	geany_functions->p_plugin->plugin_set_key_group
 #define plugin_show_configure \
 	geany_functions->p_plugin->plugin_show_configure
+#define plugin_timeout_add \
+	geany_functions->p_plugin->plugin_timeout_add
+#define plugin_timeout_add_seconds \
+	geany_functions->p_plugin->plugin_timeout_add_seconds
+#define plugin_idle_add \
+	geany_functions->p_plugin->plugin_idle_add
 #define document_new_file \
 	geany_functions->p_document->document_new_file
 #define document_get_current \

Modified: trunk/src/plugindata.h
===================================================================
--- trunk/src/plugindata.h	2011-03-29 18:06:26 UTC (rev 5649)
+++ trunk/src/plugindata.h	2011-03-29 23:20:14 UTC (rev 5650)
@@ -54,7 +54,7 @@
  * @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 204
+#define GEANY_API_VERSION 205
 
 /** The Application Binary Interface (ABI) version, incremented whenever
  * existing fields in the plugin data types have to be changed or reordered.
@@ -645,6 +645,11 @@
 	struct GeanyKeyGroup* (*plugin_set_key_group)(GeanyPlugin *plugin,
 		const gchar *section_name, gsize count, _GeanyKeyGroupCallback callback);
 	void	(*plugin_show_configure)(GeanyPlugin *plugin);
+	guint	(*plugin_timeout_add) (GeanyPlugin *plugin, guint interval, GSourceFunc function,
+		gpointer data);
+	guint	(*plugin_timeout_add_seconds) (GeanyPlugin *plugin, guint interval,
+		GSourceFunc function, gpointer data);
+	guint	(*plugin_idle_add) (GeanyPlugin *plugin, GSourceFunc function, gpointer data);
 }
 PluginFuncs;
 

Modified: trunk/src/pluginprivate.h
===================================================================
--- trunk/src/pluginprivate.h	2011-03-29 18:06:26 UTC (rev 5649)
+++ trunk/src/pluginprivate.h	2011-03-29 23:20:14 UTC (rev 5650)
@@ -57,6 +57,7 @@
 	GeanyKeyGroup	*key_group;
 	GeanyAutoSeparator	toolbar_separator;
 	GArray			*signal_ids;			/* SignalConnection's to disconnect when unloading */
+	GList			*sources;				/* GSources to destroy when unloading */
 }
 GeanyPluginPrivate;
 

Modified: trunk/src/plugins.c
===================================================================
--- trunk/src/plugins.c	2011-03-29 18:06:26 UTC (rev 5649)
+++ trunk/src/plugins.c	2011-03-29 23:20:14 UTC (rev 5650)
@@ -85,7 +85,10 @@
 	&plugin_module_make_resident,
 	&plugin_signal_connect,
 	&plugin_set_key_group,
-	&plugin_show_configure
+	&plugin_show_configure,
+	&plugin_timeout_add,
+	&plugin_timeout_add_seconds,
+	&plugin_idle_add
 };
 
 static DocumentFuncs doc_funcs = {
@@ -760,6 +763,22 @@
 }
 
 
+static void remove_sources(Plugin *plugin)
+{
+	GList *item;
+
+	item = plugin->sources;
+	while (item != NULL)
+	{
+		GList *next = item->next; /* cache the next pointer because current item will be freed */
+
+		g_source_destroy(item->data);
+		item = next;
+	}
+	/* don't free the list here, it is allocated inside each source's data */
+}
+
+
 static gboolean is_active_plugin(Plugin *plugin)
 {
 	return (g_list_find(active_plugin_list, plugin) != NULL);
@@ -776,6 +795,7 @@
 		plugin->cleanup();
 
 	remove_callbacks(plugin);
+	remove_sources(plugin);
 
 	if (plugin->key_group)
 		keybindings_free_group(plugin->key_group);

Modified: trunk/src/pluginutils.c
===================================================================
--- trunk/src/pluginutils.c	2011-03-29 18:06:26 UTC (rev 5649)
+++ trunk/src/pluginutils.c	2011-03-29 23:20:14 UTC (rev 5650)
@@ -131,6 +131,143 @@
 }
 
 
+typedef struct PluginSourceData
+{
+	Plugin		*plugin;
+	GList		list_link;	/* element of plugin->sources cointaining this GSource */
+	GSourceFunc	function;
+	gpointer	user_data;
+} PluginSourceData;
+
+
+/* use GSlice if available */
+#if GLIB_CHECK_VERSION(2,10,0)
+#	define PSD_ALLOC()		(g_slice_alloc(sizeof(PluginSourceData)))
+#	define PSD_FREE(psd)	(g_slice_free1(sizeof(PluginSourceData), (psd)))
+#else
+#	define PSD_ALLOC()		(g_malloc(sizeof(PluginSourceData)))
+#	define PSD_FREE(psd)	(g_free(psd))
+#endif
+
+
+/* prepend psd->list_link to psd->plugin->sources */
+static void psd_register(PluginSourceData *psd, GSource *source)
+{
+	psd->list_link.data = source;
+	psd->list_link.prev = NULL;
+	psd->list_link.next = psd->plugin->sources;
+	if (psd->list_link.next)
+		psd->list_link.next->prev = &psd->list_link;
+	psd->plugin->sources = &psd->list_link;
+}
+
+
+/* removes psd->list_link from psd->plugin->sources */
+static void psd_unregister(PluginSourceData *psd)
+{
+	if (psd->list_link.next)
+		psd->list_link.next->prev = psd->list_link.prev;
+	if (psd->list_link.prev)
+		psd->list_link.prev->next = psd->list_link.next;
+	else /* we were the first of the list, update the plugin->sources pointer */
+		psd->plugin->sources = psd->list_link.next;
+}
+
+
+static void on_plugin_source_destroy(gpointer data)
+{
+	PluginSourceData *psd = data;
+
+	psd_unregister(psd);
+	PSD_FREE(psd);
+}
+
+
+static gboolean on_plugin_source_callback(gpointer data)
+{
+	PluginSourceData *psd = data;
+
+	return psd->function(psd->user_data);
+}
+
+
+/* adds the given source to the default GMainContext and to the list of sources to remove at plugin
+ * unloading time */
+static guint plugin_source_add(GeanyPlugin *plugin, GSource *source, GSourceFunc func, gpointer data)
+{
+	guint id;
+	PluginSourceData *psd = PSD_ALLOC();
+
+	psd->plugin = plugin->priv;
+	psd->function = func;
+	psd->user_data = data;
+
+	g_source_set_callback(source, on_plugin_source_callback, psd, on_plugin_source_destroy);
+	psd_register(psd, source);
+	id = g_source_attach(source, NULL);
+	g_source_unref(source);
+
+	return id;
+}
+
+
+/** Adds a GLib main loop timeout callback that will be removed when unloading the plugin,
+ *  preventing it to run after the plugin has been unloaded (which may lead to a segfault).
+ *
+ * @param plugin Must be @ref geany_plugin.
+ * @param interval The time between calls to the function, in milliseconds.
+ * @param function The function to call after the given timeout.
+ * @param data The user data passed to the function.
+ * @return the ID of the event source (you generally won't need it, or better use g_timeout_add()
+ *   directly if you want to manage this event source manually).
+ *
+ * @see g_timeout_add()
+ * @since 0.21, plugin API 205.
+ */
+guint plugin_timeout_add(GeanyPlugin *plugin, guint interval, GSourceFunc function, gpointer data)
+{
+	return plugin_source_add(plugin, g_timeout_source_new(interval), function, data);
+}
+
+
+/** Adds a GLib main loop timeout callback that will be removed when unloading the plugin,
+ *  preventing it to run after the plugin has been unloaded (which may lead to a segfault).
+ *
+ * @param plugin Must be @ref geany_plugin.
+ * @param interval The time between calls to the function, in seconds.
+ * @param function The function to call after the given timeout.
+ * @param data The user data passed to the function.
+ * @return the ID of the event source (you generally won't need it, or better use
+ *   g_timeout_add_seconds() directly if you want to manage this event source manually).
+ *
+ * @see g_timeout_add_seconds()
+ * @since 0.21, plugin API 205.
+ */
+guint plugin_timeout_add_seconds(GeanyPlugin *plugin, guint interval, GSourceFunc function,
+		gpointer data)
+{
+	return plugin_source_add(plugin, g_timeout_source_new_seconds(interval), function, data);
+}
+
+
+/** Adds a GLib main loop IDLE callback that will be removed when unloading the plugin, preventing
+ *  it to run after the plugin has been unloaded (which may lead to a segfault).
+ *
+ * @param plugin Must be @ref geany_plugin.
+ * @param function The function to call in IDLE time.
+ * @param data The user data passed to the function.
+ * @return the ID of the event source (you generally won't need it, or better use g_idle_add()
+ *   directly if you want to manage this event source manually).
+ *
+ * @see g_idle_add()
+ * @since 0.21, plugin API 205.
+ */
+guint plugin_idle_add(GeanyPlugin *plugin, GSourceFunc function, gpointer data)
+{
+	return plugin_source_add(plugin, g_idle_source_new(), function, data);
+}
+
+
 /** Sets up or resizes a keybinding group for the plugin.
  * You should then call keybindings_set_item() for each keybinding in the group.
  * @param plugin Must be @ref geany_plugin.

Modified: trunk/src/pluginutils.h
===================================================================
--- trunk/src/pluginutils.h	2011-03-29 18:06:26 UTC (rev 5649)
+++ trunk/src/pluginutils.h	2011-03-29 23:20:14 UTC (rev 5650)
@@ -43,6 +43,14 @@
 		GObject *object, const gchar *signal_name, gboolean after,
 		GCallback callback, gpointer user_data);
 
+guint plugin_timeout_add(struct GeanyPlugin *plugin, guint interval, GSourceFunc function,
+		gpointer data);
+
+guint plugin_timeout_add_seconds(struct GeanyPlugin *plugin, guint interval, GSourceFunc function,
+		gpointer data);
+
+guint plugin_idle_add(struct GeanyPlugin *plugin, GSourceFunc function, gpointer data);
+
 struct GeanyKeyGroup *plugin_set_key_group(struct GeanyPlugin *plugin,
 		const gchar *section_name, gsize count, GeanyKeyGroupCallback callback);
 


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.



More information about the Commits mailing list