/pidgin/main: 79fe6b95f105: Fix a race condition in appsrc read/...

Jakub Adam <[email protected]>
Newsgroups gmane.comp.gnome.gaim.cvs
Message-ID <[email protected]>
Changeset: 79fe6b95f1057b65505559ffdc66136e2145cf25
Author:	 Jakub Adam <[email protected]>
Date:	 2015-08-21 10:35 +0200
Branch:	 release-2.x.y
URL: https://hg.pidgin.im/pidgin/main/rev/79fe6b95f105

Description:

Fix a race condition in appsrc read/write callbacks

We can't use writable_timer_id as a token because the timeout is added
into libpurple's main event loop, which runs in a different thread than
from where call_appsrc_writable_locked() was called. Consequently, the
callback may run even before purple_timeout_add() returns the timer ID
to us.

The same applies to readable_timer_id in call_appsink_readable_locked().

diffstat:

 libpurple/mediamanager.c |  55 ++++++++++++++++++++++++++++++++---------------
 1 files changed, 37 insertions(+), 18 deletions(-)

diffs (155 lines):

diff --git a/libpurple/mediamanager.c b/libpurple/mediamanager.c
--- a/libpurple/mediamanager.c
+++ b/libpurple/mediamanager.c
@@ -105,6 +105,7 @@ struct _PurpleMediaManagerPrivate
 	/* Application data streams */
 	GList *appdata_info; /* holds PurpleMediaAppDataInfo */
 	GMutex appdata_mutex;
+	guint appdata_cb_token; /* last used read/write callback token */
 #endif
 };
 
@@ -124,6 +125,8 @@ typedef struct {
 	guint sample_offset;
 	gboolean writable;
 	gboolean connected;
+	guint writable_cb_token;
+	guint readable_cb_token;
 	guint writable_timer_id;
 	guint readable_timer_id;
 	GCond readable_cond;
@@ -562,6 +565,11 @@ free_appdata_info_locked (PurpleMediaApp
 	g_free (info->session_id);
 	g_free (info->participant);
 
+	/* This lets the potential read or write callbacks waiting for appdata_mutex
+	 * know the info structure has been destroyed. */
+	info->readable_cb_token = 0;
+	info->writable_cb_token = 0;
+
 	if (info->readable_timer_id) {
 		purple_timeout_remove (info->readable_timer_id);
 		info->readable_timer_id = 0;
@@ -750,19 +758,19 @@ appsrc_writable (gpointer user_data)
 	gchar *participant;
 	gboolean writable;
 	gpointer cb_data;
-	guint *timer_id_ptr = &info->writable_timer_id;
-	guint timer_id = *timer_id_ptr;
+	guint *cb_token_ptr = &info->writable_cb_token;
+	guint cb_token = *cb_token_ptr;
 
 
 	g_mutex_lock (&manager->priv->appdata_mutex);
-	if (timer_id == 0 || timer_id != *timer_id_ptr) {
+	if (cb_token == 0 || cb_token != *cb_token_ptr) {
 		/* In case info was freed while we were waiting for the mutex to unlock
-		 * we still have a pointer to the timer_id which should still be
+		 * we still have a pointer to the cb_token which should still be
 		 * accessible since it's in the Glib slice allocator. It gets set to 0
 		 * just after the timeout is canceled which happens also before the
 		 * AppDataInfo is freed, so even if that memory slice gets reused, the
-		 * timer_id would be different from its previous value (unless
-		 * extremely unlucky). So checking if the value for the timer_id changed
+		 * cb_token would be different from its previous value (unless
+		 * extremely unlucky). So checking if the value for the cb_token changed
 		 * should be enough to prevent any kind of race condition in which the
 		 * media/AppDataInfo gets destroyed in one thread while the timeout was
 		 * triggered and is waiting on the mutex to get unlocked in this thread
@@ -777,7 +785,7 @@ appsrc_writable (gpointer user_data)
 	writable = info->writable && info->connected;
 	cb_data = info->user_data;
 
-    info->writable_timer_id = 0;
+	info->writable_cb_token = 0;
 	g_mutex_unlock (&manager->priv->appdata_mutex);
 
 
@@ -805,10 +813,18 @@ appsrc_writable (gpointer user_data)
 static void
 call_appsrc_writable_locked (PurpleMediaAppDataInfo *info)
 {
+	PurpleMediaManager *manager = purple_media_manager_get ();
+
 	/* We already have a writable callback scheduled, don't create another one */
-	if (info->writable_timer_id || info->callbacks.writable == NULL)
+	if (info->writable_cb_token || info->callbacks.writable == NULL)
 		return;
 
+	/* We can't use writable_timer_id as a token, because the timeout is added
+	 * into libpurple's main event loop, which runs in a different thread than
+	 * from where call_appsrc_writable_locked() was called. Consequently, the
+	 * callback may run even before purple_timeout_add() returns the timer ID
+	 * to us. */
+	info->writable_cb_token = ++manager->priv->appdata_cb_token;
 	info->writable_timer_id = purple_timeout_add (0, appsrc_writable, info);
 }
 
@@ -930,11 +946,11 @@ appsink_readable (gpointer user_data)
 	gchar *session_id;
 	gchar *participant;
 	gpointer cb_data;
-	guint *timer_id_ptr = &info->readable_timer_id;
-	guint timer_id = *timer_id_ptr;
+	guint *cb_token_ptr = &info->readable_cb_token;
+	guint cb_token = *cb_token_ptr;
 
 	g_mutex_lock (&manager->priv->appdata_mutex);
-	if (timer_id == 0 || timer_id != *timer_id_ptr) {
+	if (cb_token == 0 || cb_token != *cb_token_ptr) {
 		/* Avoided a race condition (see writable callback) */
 		g_mutex_unlock (&manager->priv->appdata_mutex);
 		return FALSE;
@@ -956,13 +972,13 @@ appsink_readable (gpointer user_data)
 		g_object_unref (media);
 		g_free (session_id);
 		g_free (participant);
-		if (timer_id == 0 || timer_id != *timer_id_ptr) {
+		if (cb_token == 0 || cb_token != *cb_token_ptr) {
 			/* We got cancelled */
 			g_mutex_unlock (&manager->priv->appdata_mutex);
 			return FALSE;
 		}
 	}
-    info->readable_timer_id = 0;
+	info->readable_cb_token = 0;
 	g_mutex_unlock (&manager->priv->appdata_mutex);
 	return FALSE;
 }
@@ -970,13 +986,16 @@ appsink_readable (gpointer user_data)
 static void
 call_appsink_readable_locked (PurpleMediaAppDataInfo *info)
 {
+	PurpleMediaManager *manager = purple_media_manager_get ();
+
 	/* We must signal that a new sample has arrived to release blocking reads */
 	g_cond_broadcast (&info->readable_cond);
 
 	/* We already have a writable callback scheduled, don't create another one */
-	if (info->readable_timer_id || info->callbacks.readable == NULL)
+	if (info->readable_cb_token || info->callbacks.readable == NULL)
 		return;
 
+	info->readable_cb_token = ++manager->priv->appdata_cb_token;
 	info->readable_timer_id = purple_timeout_add (0, appsink_readable, info);
 }
 
@@ -1663,14 +1682,14 @@ purple_media_manager_set_application_dat
 	if (info->notify)
 		info->notify (info->user_data);
 
-	if (info->readable_timer_id) {
+	if (info->readable_cb_token) {
 		purple_timeout_remove (info->readable_timer_id);
-		info->readable_timer_id = 0;
+		info->readable_cb_token = 0;
 	}
 
-	if (info->writable_timer_id) {
+	if (info->writable_cb_token) {
 		purple_timeout_remove (info->writable_timer_id);
-		info->writable_timer_id = 0;
+		info->writable_cb_token = 0;
 	}
 
 	if (callbacks) {

_______________________________________________
Commits mailing list
[email protected]
https://pidgin.im/cgi-bin/mailman/listinfo/commits
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.