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