diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9c88c2d0..620f8d6b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,11 +45,11 @@ engine/api/geary-special-folder-type.vala engine/common/common-message-data.vala -engine/db/database-error.vala engine/db/db.vala engine/db/db-connection.vala engine/db/db-context.vala engine/db/db-database.vala +engine/db/db-database-error.vala engine/db/db-result.vala engine/db/db-statement.vala engine/db/db-synchronous-mode.vala diff --git a/src/engine/db/database-error.vala b/src/engine/db/db-database-error.vala similarity index 88% rename from src/engine/db/database-error.vala rename to src/engine/db/db-database-error.vala index 6c27d4ef..a19e7c99 100755 --- a/src/engine/db/database-error.vala +++ b/src/engine/db/db-database-error.vala @@ -4,7 +4,7 @@ * (version 2.1 or later). See the COPYING file in this distribution. */ -public errordomain DatabaseError { +public errordomain Geary.DatabaseError { GENERAL, OPEN_REQUIRED, BUSY, diff --git a/src/engine/db/db-database.vala b/src/engine/db/db-database.vala index d2fcd0e9..70758d6f 100644 --- a/src/engine/db/db-database.vala +++ b/src/engine/db/db-database.vala @@ -40,6 +40,7 @@ public class Geary.Db.Database : Geary.Db.Context { private Connection? master_connection = null; private int outstanding_async_jobs = 0; private ThreadPool? thread_pool = null; + private Gee.LinkedList? cx_pool = null; private unowned PrepareConnection? prepare_cb = null; public Database(File db_file) { @@ -76,9 +77,13 @@ public class Geary.Db.Database : Geary.Db.Context { } if (threadsafe()) { - assert(thread_pool == null); - thread_pool = new ThreadPool.with_owned_data(on_async_job, - DEFAULT_MAX_CONCURRENCY, true); + if (thread_pool == null) { + thread_pool = new ThreadPool.with_owned_data(on_async_job, + DEFAULT_MAX_CONCURRENCY, true); + } + + if (cx_pool == null) + cx_pool = new Gee.LinkedList(); } else { warning("SQLite not thread-safe: asynchronous queries will not be available"); } @@ -101,6 +106,9 @@ public class Geary.Db.Database : Geary.Db.Context { // drop the master connection, which holds a ref back to this object master_connection = null; + // As per the contract above, can't simply drop the thread and connection pools; that would + // be bad. + is_open = false; } @@ -220,23 +228,37 @@ public class Geary.Db.Database : Geary.Db.Context { // This method must be thread-safe. private void on_async_job(owned TransactionAsyncJob job) { - // create connection for this thread - // TODO: Use connection pool -- *never* use master connection + // go to connection pool before creating a connection -- *never* use master connection for + // threaded operations Connection? cx = null; - try { - cx = open_connection(); - } catch (Error err) { - debug("Warning: unable to open database connection to %s, cancelling AsyncJob: %s", - db_file.get_path(), err.message); + lock (cx_pool) { + cx = cx_pool.poll(); + } + + Error? open_err = null; + if (cx == null) { + try { + cx = open_connection(); + } catch (Error err) { + open_err = err; + debug("Warning: unable to open database connection to %s, cancelling AsyncJob: %s", + db_file.get_path(), err.message); + } } if (cx != null) job.execute(cx); + else + job.failed(open_err); lock (outstanding_async_jobs) { assert(outstanding_async_jobs > 0); --outstanding_async_jobs; } + + lock (cx_pool) { + cx_pool.offer(cx); + } } public override Database? get_database() { diff --git a/src/engine/db/db-transaction-async-job.vala b/src/engine/db/db-transaction-async-job.vala index b3fffecb..cda9e420 100755 --- a/src/engine/db/db-transaction-async-job.vala +++ b/src/engine/db/db-transaction-async-job.vala @@ -42,6 +42,18 @@ private class Geary.Db.TransactionAsyncJob : Object { caught_err = err; } + schedule_completion(); + } + + // Called in background thread context + internal void failed(Error err) { + // store as a caught thread to report to original caller + caught_err = err; + + schedule_completion(); + } + + private void schedule_completion() { // notify foreground thread of completion // because Idle doesn't hold a ref, manually keep this object alive ref();