r1154 - in /unstable/evolution-data-server/debian: changelog patches/68_deadlock_thread-leak.patch

joss at users.alioth.debian.org joss at users.alioth.debian.org
Wed Apr 1 10:19:01 UTC 2009


Author: joss
Date: Wed Apr  1 10:19:00 2009
New Revision: 1154

URL: http://svn.debian.org/wsvn/pkg-evolution/?sc=1&rev=1154
Log:
68_deadlock_thread-leak.patch: new patch, stolen upstream. Fixes 
thread leak leading to a deadlock. Closes: #520132, #520771.

Added:
    unstable/evolution-data-server/debian/patches/68_deadlock_thread-leak.patch
Modified:
    unstable/evolution-data-server/debian/changelog

Modified: unstable/evolution-data-server/debian/changelog
URL: http://svn.debian.org/wsvn/pkg-evolution/unstable/evolution-data-server/debian/changelog?rev=1154&op=diff
==============================================================================
--- unstable/evolution-data-server/debian/changelog (original)
+++ unstable/evolution-data-server/debian/changelog Wed Apr  1 10:19:00 2009
@@ -1,8 +1,10 @@
-evolution-data-server (2.24.5-4) UNRELEASED; urgency=low
+evolution-data-server (2.24.5-4) unstable; urgency=low
 
   * Fix -dbg package section to match the override.
-
- -- Josselin Mouette <joss at debian.org>  Tue, 24 Mar 2009 18:42:05 +0100
+  * 68_deadlock_thread-leak.patch: new patch, stolen upstream. Fixes 
+    thread leak leading to a deadlock. Closes: #520132, #520771.
+
+ -- Josselin Mouette <joss at debian.org>  Wed, 01 Apr 2009 12:19:03 +0200
 
 evolution-data-server (2.24.5-3) unstable; urgency=low
 

Added: unstable/evolution-data-server/debian/patches/68_deadlock_thread-leak.patch
URL: http://svn.debian.org/wsvn/pkg-evolution/unstable/evolution-data-server/debian/patches/68_deadlock_thread-leak.patch?rev=1154&op=file
==============================================================================
--- unstable/evolution-data-server/debian/patches/68_deadlock_thread-leak.patch (added)
+++ unstable/evolution-data-server/debian/patches/68_deadlock_thread-leak.patch Wed Apr  1 10:19:00 2009
@@ -1,0 +1,463 @@
+Index: camel/camel-db.c
+===================================================================
+--- camel/camel-db.c	(revision 10145)
++++ camel/camel-db.c	(working copy)
+@@ -36,239 +36,112 @@
+ 
+ #include "camel-debug.h"
+ 
+-/* how long to wait before invoking sync on the file; in miliseconds */
+-#define SYNC_TIMEOUT 5000
++/* how long to wait before invoking sync on the file */
++#define SYNC_TIMEOUT_SECONDS 5
+ 
+ static sqlite3_vfs *old_vfs = NULL;
+ 
+-GStaticRecMutex only_once_lock = G_STATIC_REC_MUTEX_INIT;
+-GStaticRecMutex sync_queue_lock = G_STATIC_REC_MUTEX_INIT;
+-#define LockQueue()   g_static_rec_mutex_lock   (&sync_queue_lock)
+-#define UnlockQueue() g_static_rec_mutex_unlock (&sync_queue_lock)
+-
+-/* 'sync_queue' is using keys sqlite3_file to sync_queue_data structures.
+-   Access to this is guarded with LockQueue/UnlockQueue function. */
+-static GHashTable *sync_queue = NULL;
+-
+-typedef struct _sync_queue_data {
+-	guint timeout_source; /* id of the source */
+-	GThread *running_thread;
+-
+-	int sync_flags; 
+-} sync_queue_data;
+-
+-struct CamelSqlite3File
+-{
++typedef struct {
+ 	sqlite3_file parent;
+ 	sqlite3_file *old_vfs_file; /* pointer to old_vfs' file */
+-};
++	GAsyncQueue *queue;
++	GThread *thread;
++	guint timeout_id;
++	gint flags;
++} CamelSqlite3File;
+ 
+ static int
+-call_old_file_Sync (sqlite3_file *pFile, int flags)
++call_old_file_Sync (CamelSqlite3File *cFile, int flags)
+ {
+-	struct CamelSqlite3File *cFile;
+-
+ 	g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);
+-	g_return_val_if_fail (pFile != NULL, SQLITE_ERROR);
++	g_return_val_if_fail (cFile != NULL, SQLITE_ERROR);
+ 
+-	cFile = (struct CamelSqlite3File *)pFile;
+ 	g_return_val_if_fail (cFile->old_vfs_file->pMethods != NULL, SQLITE_ERROR);
+ 	return cFile->old_vfs_file->pMethods->xSync (cFile->old_vfs_file, flags);
+ }
+ 
+-static gboolean prepare_to_run_sync_in_thread (gpointer pFile);
++/* This special flag tells the sync request thread to exit.
++ * Just have to make sure it does not collide with SQLite's
++ * own synchronization flags (SQLITE_SYNC_xxx). */
++#define SYNC_THREAD_EXIT 0x100000
+ 
+ static gpointer
+-run_sync_in_thread (gpointer pFile)
++sync_request_thread_cb (CamelSqlite3File *cFile)
+ {
+-	int sync_flags = 0;
+-	sync_queue_data *data;
++	gpointer data;
++	gint flags = 0;
+ 
+-	g_return_val_if_fail (pFile != NULL, NULL);
+-	g_return_val_if_fail (sync_queue != NULL, NULL);
++	g_async_queue_ref (cFile->queue);
+ 
+-	LockQueue ();
+-	data = g_hash_table_lookup (sync_queue, pFile);
+-	if (data) {
+-		/* sync_flags can change while we are running */
+-		sync_flags = data->sync_flags;
+-		data->sync_flags = 0;
+-	}
+-	UnlockQueue ();
+-
+-	/* this should not happen, once we are in a thread, the datas are ours */
+-	g_return_val_if_fail (data != NULL, NULL);
+-
+-	/* do the sync itself, but do not block the sync_queue;
+-	   any error here is silently ignored. */
+-	call_old_file_Sync (/*sqlite3_file*/pFile, sync_flags);
+-
+-	LockQueue ();
+-	if (data->timeout_source == -1) {
+-		/* new sync request arrived meanwhile, indicate thread finished... */
+-		data->running_thread = NULL;
+-		/* ...and reschedule */
+-		data->timeout_source = g_timeout_add (SYNC_TIMEOUT, prepare_to_run_sync_in_thread, pFile);
+-	} else {
+-		/* remove it from a sync_queue and free memory */
+-		g_hash_table_remove (sync_queue, pFile);
+-		g_free (data);
+-	}
+-	UnlockQueue ();
+-
+-	return NULL;
+-}
++	while (TRUE) {
++		/* Block until a request arrives. */
++		data = g_async_queue_pop (cFile->queue);
+ 
+-static gboolean
+-prepare_to_run_sync_in_thread (gpointer pFile)
+-{
+-	sync_queue_data *data;
++		/* Make sure we can safely deference. */
++		if (data == NULL)
++			continue;
+ 
+-	g_return_val_if_fail (pFile != NULL, FALSE);
+-	g_return_val_if_fail (sync_queue != NULL, FALSE);
++		/* Extract flags and discard request. */
++		flags = *((gint *) data);
++		g_slice_free (gint, data);
+ 
+-	LockQueue ();
++		/* Check for exit request. */
++		if (flags & SYNC_THREAD_EXIT)
++			break;
+ 
+-	data = g_hash_table_lookup (sync_queue, pFile);
+-	/* check if still tracking this file and if didn't get rescheduled */
+-	if (data && data->timeout_source == g_source_get_id (g_main_current_source ())) {
+-		/* run the thread */
+-		data->running_thread = g_thread_create (run_sync_in_thread, pFile, TRUE, NULL);
+-		data->timeout_source = 0;
++		/* Got a boneafide sync request.
++		 * Do it, but ignore errors. */
++		call_old_file_Sync (cFile, flags);
+ 	}
+ 
+-	UnlockQueue ();
++	/* Clear the exit flag. */
++	flags &= ~SYNC_THREAD_EXIT;
+ 
+-	return FALSE;
+-}
+-
+-/*
+-   Adds sync on this file to the queue. Flags are just bit-OR-ed,
+-   which will not hopefully hurt. In case the file is waiting for
+-   it's sync, we just postpone it once again.
+-   In case file is syncing just in call of this, we schedule other
+-   sync after that.
+- */
+-static void
+-queue_sync (sqlite3_file *pFile, int flags)
+-{
+-	sync_queue_data *data;
+-
+-	g_return_if_fail (pFile != NULL);
+-	g_return_if_fail (flags != 0);
+-
+-	LockQueue ();
+-
+-	if (!sync_queue)
+-		sync_queue = g_hash_table_new (g_direct_hash, g_direct_equal);
+-
+-	data = g_hash_table_lookup (sync_queue, pFile);
+-	if (data) {
+-		/* There is a sync request for this file already. */
+-		if (data->running_thread) {
+-			/* -1 indicates to reschedule after the actual sync finishes */
+-			data->timeout_source = -1;
+-			/* start with new flags - thread set it to 0; next time just add others */
+-			data->sync_flags = data->sync_flags | flags;
+-		} else {
+-			data->sync_flags = data->sync_flags | flags;
+-
+-			/* reschedule */
+-			g_source_remove (data->timeout_source);
+-			data->timeout_source = g_timeout_add (SYNC_TIMEOUT, prepare_to_run_sync_in_thread, pFile);
+-		}
+-	} else {
+-		data = g_malloc0 (sizeof (sync_queue_data));
+-		data->sync_flags = flags;
+-		data->running_thread = NULL;
+-		data->timeout_source = g_timeout_add (SYNC_TIMEOUT, prepare_to_run_sync_in_thread, pFile);
++	/* One more for the road? */
++	if (flags != 0 && getenv ("CAMEL_NO_SYNC_ON_CLOSE") == NULL)
++		call_old_file_Sync (cFile, flags);
+ 
+-		g_hash_table_insert (sync_queue, pFile, data);
+-	}
++	g_async_queue_unref (cFile->queue);
+ 
+-	UnlockQueue ();
++	return NULL;
+ }
+ 
+-/*
+-   If file is not in a queue, it does nothing, otherwise it removes
+-   it from a queue, and calls sync immediately.
+-   If file is syncing at the moment, it waits until the previous sync finishes.
+- */
+-static void
+-dequeue_sync (sqlite3_file *pFile)
+-{
+-	sync_queue_data *data;
+-
+-	g_return_if_fail (pFile != NULL);
+-
+-	LockQueue ();
+-
+-	if (!sync_queue) {
+-		/* closing file which wasn't requested to sync, and none
+-		   before it too. */
+-		UnlockQueue ();
+-		return;
+-	}
+-
+-	data = g_hash_table_lookup (sync_queue, pFile);
+-	if (data) {
+-		int sync_flags = data->sync_flags;
+-
+-		if (data->timeout_source) {
+-			if (data->timeout_source != -1)
+-				g_source_remove (data->timeout_source);
+-			data->timeout_source = 0;
+-		}
+-
+-		if (data->running_thread) {
+-			GThread *thread = data->running_thread;
++static gboolean
++sync_push_request (CamelSqlite3File *cFile)
++{
++	gint *data;
+ 
+-			/* do not do anything later */
+-			data = NULL;
++	/* The queue itself does not need to be locked yet,
++	 * but we use its mutex to safely manipulate flags. */
++	g_async_queue_lock (cFile->queue);
+ 
+-			/* unlock here, thus the thread can hold the lock again */
+-			UnlockQueue ();
++	/* We can't just cast the flags to a pointer because
++	 * g_async_queue_push() won't take NULLs, and flags
++	 * may be zero.  So we have to allocate memory to
++	 * send an integer.  Bother. */
++	data = g_slice_new (gint);
++	*data = cFile->flags;
++	cFile->flags = 0;
+ 
+-			/* it's running at the moment, wait for a finish.
+-			   it'll remove structure from a sync_queue too. */
+-			g_thread_join (thread);
+-		} else {
+-			g_hash_table_remove (sync_queue, pFile);
+-		}
++	g_async_queue_push_unlocked (cFile->queue, data);
+ 
+-		if (data) {
+-			static gboolean no_sync_on_close = FALSE, iKnow = FALSE;
++	cFile->timeout_id = 0;
+ 
+-			if (!iKnow) {
+-				iKnow = TRUE;
+-				no_sync_on_close = getenv ("CAMEL_NO_SYNC_ON_CLOSE") != NULL;
+-			}
+-
+-			/* do not block queue on while syncing */
+-			UnlockQueue ();
+-
+-			if (!no_sync_on_close) {
+-				/* sync on close */
+-				call_old_file_Sync (pFile, sync_flags);
+-			}
++	g_async_queue_unlock (cFile->queue);
+ 
+-			g_free (data);
+-		}
+-	} else {
+-		UnlockQueue ();
+-	}
++	return FALSE;
+ }
+ 
+ #define def_subclassed(_nm, _params, _call)			\
+ static int							\
+ camel_sqlite3_file_ ## _nm _params				\
+ {								\
+-	struct CamelSqlite3File *cFile;				\
++	CamelSqlite3File *cFile;				\
+ 								\
+ 	g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);	\
+ 	g_return_val_if_fail (pFile != NULL, SQLITE_ERROR);	\
+ 								\
+-	cFile = (struct CamelSqlite3File *) pFile;		\
++	cFile = (CamelSqlite3File *) pFile;		\
+ 	g_return_val_if_fail (cFile->old_vfs_file->pMethods != NULL, SQLITE_ERROR);	\
+ 	return cFile->old_vfs_file->pMethods->_nm _call;	\
+ }
+@@ -293,15 +166,41 @@ def_subclassed (xDeviceCharacteristics, 
+ static int
+ camel_sqlite3_file_xClose (sqlite3_file *pFile)
+ {
+-	struct CamelSqlite3File *cFile;
++	CamelSqlite3File *cFile;
+ 	int res;
+ 
+ 	g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);
+ 	g_return_val_if_fail (pFile != NULL, SQLITE_ERROR);
+ 
+-	dequeue_sync (pFile);
++	cFile = (CamelSqlite3File *) pFile;
++
++	/* The queue itself does not need to be locked yet,
++	 * but we use its mutex to safely manipulate flags. */
++	g_async_queue_lock (cFile->queue);
++
++	/* Tell the sync request thread to exit.  It may do
++	 * one last sync before exiting, so preserve any sync
++	 * flags that have accumulated. */
++	cFile->flags |= SYNC_THREAD_EXIT;
++
++	/* Cancel any pending sync requests. */
++	if (cFile->timeout_id > 0)
++		g_source_remove (cFile->timeout_id);
++
++	/* Unlock the queue before pushing the exit request. */
++	g_async_queue_unlock (cFile->queue);
++
++	/* Push the exit request. */
++	sync_push_request (cFile);
++
++	/* Wait for the thread to exit. */
++	g_thread_join (cFile->thread);
++	cFile->thread = NULL;
++
++	/* Now we can safely destroy the queue. */
++	g_async_queue_unref (cFile->queue);
++	cFile->queue = NULL;
+ 
+-	cFile = (struct CamelSqlite3File *) pFile;
+ 	if (cFile->old_vfs_file->pMethods)
+ 		res = cFile->old_vfs_file->pMethods->xClose (cFile->old_vfs_file);
+ 	else
+@@ -316,10 +215,30 @@ camel_sqlite3_file_xClose (sqlite3_file 
+ static int 
+ camel_sqlite3_file_xSync (sqlite3_file *pFile, int flags)
+ {
++	CamelSqlite3File *cFile;
++
+ 	g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);
+ 	g_return_val_if_fail (pFile != NULL, SQLITE_ERROR);
+ 
+-	queue_sync (pFile, flags);
++	cFile = (CamelSqlite3File *) pFile;
++
++	/* The queue itself does not need to be locked yet,
++	 * but we use its mutex to safely manipulate flags. */
++	g_async_queue_lock (cFile->queue);
++
++	/* If a sync request is already scheduled, accumulate flags. */
++	cFile->flags |= flags;
++
++	/* Cancel any pending sync requests. */
++	if (cFile->timeout_id > 0)
++		g_source_remove (cFile->timeout_id);
++
++	/* Wait SYNC_TIMEOUT_SECONDS before we actually sync. */
++	cFile->timeout_id = g_timeout_add_seconds (
++		SYNC_TIMEOUT_SECONDS, (GSourceFunc)
++		sync_push_request, cFile);
++
++	g_async_queue_unlock (cFile->queue);
+ 
+ 	return SQLITE_OK;
+ }
+@@ -327,15 +246,26 @@ camel_sqlite3_file_xSync (sqlite3_file *
+ static int
+ camel_sqlite3_vfs_xOpen (sqlite3_vfs *pVfs, const char *zPath, sqlite3_file *pFile, int flags, int *pOutFlags)
+ {
++	static GStaticRecMutex only_once_lock = G_STATIC_REC_MUTEX_INIT;
+ 	static sqlite3_io_methods io_methods = {0};
+-	struct CamelSqlite3File *cFile;
++	CamelSqlite3File *cFile;
++	GError *error = NULL;
+ 	int res;
+ 
+ 	g_return_val_if_fail (old_vfs != NULL, -1);
+ 	g_return_val_if_fail (pFile != NULL, -1);
+ 
+-	cFile = (struct CamelSqlite3File *)pFile;
++	cFile = (CamelSqlite3File *)pFile;
+ 	cFile->old_vfs_file = g_malloc0 (old_vfs->szOsFile);
++	cFile->queue = g_async_queue_new ();
++
++	/* Spawn a joinable thread to listen for sync requests. */
++	cFile->thread = g_thread_create (
++		(GThreadFunc) sync_request_thread_cb, cFile, TRUE, &error);
++	if (error != NULL) {
++		g_warning ("%s", error->message);
++		g_error_free (error);
++	}
+ 
+ 	res = old_vfs->xOpen (old_vfs, zPath, cFile->old_vfs_file, flags, pOutFlags);
+ 
+@@ -370,33 +300,23 @@ camel_sqlite3_vfs_xOpen (sqlite3_vfs *pV
+ 	return res;
+ }
+ 
+-static void
++static gpointer
+ init_sqlite_vfs (void)
+ {
+ 	static sqlite3_vfs vfs = { 0 };
+ 
+-	g_static_rec_mutex_lock (&only_once_lock);
+-	if (old_vfs) {
+-		g_static_rec_mutex_unlock (&only_once_lock);
+-		return;
+-	}
+-
+ 	old_vfs = sqlite3_vfs_find (NULL);
+-	if (!old_vfs) {
+-		g_static_rec_mutex_unlock (&only_once_lock);
+-		g_return_if_fail (old_vfs != NULL);
+-		return;
+-	}
++	g_return_val_if_fail (old_vfs != NULL, NULL);
+ 
+ 	memcpy (&vfs, old_vfs, sizeof (sqlite3_vfs));
+ 
+-	vfs.szOsFile = sizeof (struct CamelSqlite3File);
++	vfs.szOsFile = sizeof (CamelSqlite3File);
+ 	vfs.zName = "camel_sqlite3_vfs";
+ 	vfs.xOpen = camel_sqlite3_vfs_xOpen;
+ 
+ 	sqlite3_vfs_register (&vfs, 1);
+ 
+-	g_static_rec_mutex_unlock (&only_once_lock);
++	return NULL;
+ }
+ 
+ #define d(x) if (camel_debug("sqlite")) x
+@@ -453,12 +373,13 @@ cdb_sql_exec (sqlite3 *db, const char* s
+ CamelDB *
+ camel_db_open (const char *path, CamelException *ex)
+ {
++	static GOnce vfs_once = G_ONCE_INIT;
+ 	CamelDB *cdb;
+ 	sqlite3 *db;
+ 	char *cache;
+ 	int ret;
+ 
+-	init_sqlite_vfs ();
++	g_once (&vfs_once, (GThreadFunc) init_sqlite_vfs, NULL);
+ 
+ 	CAMEL_DB_USE_SHARED_CACHE;
+ 	




More information about the pkg-evolution-commits mailing list