/////////////////////////////////////////////////////////////////////// /// <summary> /// Disposes of all pooled connections. /// </summary> internal static void ClearAllPools() { ISQLiteConnectionPool connectionPool = GetConnectionPool(); if (connectionPool != null) { connectionPool.ClearAllPools(); } else { lock (_syncRoot) { foreach (KeyValuePair <string, PoolQueue> pair in _queueList) { if (pair.Value == null) { continue; } Queue <WeakReference> poolQueue = pair.Value.Queue; while (poolQueue.Count > 0) { WeakReference connection = poolQueue.Dequeue(); if (connection == null) { continue; } SQLiteConnectionHandle handle = connection.Target as SQLiteConnectionHandle; if (handle != null) { handle.Dispose(); } GC.KeepAlive(handle); } // // NOTE: Keep track of the highest revision so we can // go one higher when we are finished. // if (_poolVersion <= pair.Value.PoolVersion) { _poolVersion = pair.Value.PoolVersion + 1; } } // // NOTE: All pools are cleared and we have a new highest // version number to force all old version active // items to get discarded instead of going back to // the queue when they are closed. We can get away // with this because we have pumped up the pool // version out of range of all active connections, // so they will all get discarded when they try to // put themselves back into their pools. // _queueList.Clear(); } } }
/////////////////////////////////////////////////////////////////////// /// <summary> /// Removes a connection from the pool of those associated with the /// specified database file name with the intent of using it to /// interact with the database. /// </summary> /// <param name="fileName"> /// The database file name. /// </param> /// <param name="maxPoolSize"> /// The new maximum size of the connection pool for the specified /// database file name. /// </param> /// <param name="version"> /// The connection pool version associated with the returned database /// connection handle, if any. /// </param> /// <returns> /// The database connection handle associated with the specified /// database file name or null if it cannot be obtained. /// </returns> internal static SQLiteConnectionHandle Remove( string fileName, int maxPoolSize, out int version ) { ISQLiteConnectionPool connectionPool = GetConnectionPool(); if (connectionPool != null) { return(connectionPool.Remove(fileName, maxPoolSize, out version) as SQLiteConnectionHandle); } else { int localVersion; Queue <WeakReference> poolQueue; // // NOTE: This lock cannot be held while checking the queue for // available connections because other methods of this // class are called from the GC finalizer thread and we // use the WaitForPendingFinalizers method (below). // Holding this lock while calling that method would // therefore result in a deadlock. Instead, this lock // is held only while a temporary copy of the queue is // created, and if necessary, when committing changes // back to that original queue prior to returning from // this method. // lock (_syncRoot) { PoolQueue queue; // // NOTE: Default to the highest pool version. // version = _poolVersion; // // NOTE: If we didn't find a pool for this file, create one // even though it will be empty. We have to do this // here because otherwise calling ClearPool() on the // file will not work for active connections that have // never seen the pool yet. // if (!_queueList.TryGetValue(fileName, out queue)) { queue = new PoolQueue(_poolVersion, maxPoolSize); _queueList.Add(fileName, queue); return(null); } // // NOTE: We found a pool for this file, so use its version // number. // version = localVersion = queue.PoolVersion; queue.MaxPoolSize = maxPoolSize; // // NOTE: Now, resize the pool to the new maximum size, if // necessary. // ResizePool(queue, false); // // NOTE: Try and get a pooled connection from the queue. // poolQueue = queue.Queue; if (poolQueue == null) { return(null); } // // NOTE: Temporarily tranfer the queue for this file into // a local variable. The queue for this file will // be modified and then committed back to the real // pool list (below) prior to returning from this // method. // _queueList.Remove(fileName); poolQueue = new Queue <WeakReference>(poolQueue); } try { while (poolQueue.Count > 0) { WeakReference connection = poolQueue.Dequeue(); if (connection == null) { continue; } SQLiteConnectionHandle handle = connection.Target as SQLiteConnectionHandle; if (handle == null) { continue; } // // BUGFIX: For ticket [996d13cd87], step #1. After // this point, make sure that the finalizer for // the connection handle just obtained from the // queue cannot START running (i.e. it may // still be pending but it will no longer start // after this point). // GC.SuppressFinalize(handle); try { // // BUGFIX: For ticket [996d13cd87], step #2. Now, // we must wait for all pending finalizers // which have STARTED running and have not // yet COMPLETED. This must be done just // in case the finalizer for the connection // handle just obtained from the queue has // STARTED running at some point before // SuppressFinalize was called on it. // // After this point, checking properties of // the connection handle (e.g. IsClosed) // should work reliably without having to // worry that they will (due to the // finalizer) change out from under us. // GC.WaitForPendingFinalizers(); // // BUGFIX: For ticket [996d13cd87], step #3. Next, // verify that the connection handle is // actually valid and [still?] not closed // prior to actually returning it to our // caller. // if (!handle.IsInvalid && !handle.IsClosed) { Interlocked.Increment(ref _poolOpened); return(handle); } } finally { // // BUGFIX: For ticket [996d13cd87], step #4. Next, // we must re-register the connection // handle for finalization now that we have // a strong reference to it (i.e. the // finalizer will not run at least until // the connection is subsequently closed). // GC.ReRegisterForFinalize(handle); } GC.KeepAlive(handle); } } finally { // // BUGFIX: For ticket [996d13cd87], step #5. Finally, // commit any changes to the pool/queue for this // database file. // lock (_syncRoot) { // // NOTE: We must check [again] if a pool exists for // this file because one may have been added // while the search for an available connection // was in progress (above). // PoolQueue queue; Queue <WeakReference> newPoolQueue; bool addPool; if (_queueList.TryGetValue(fileName, out queue)) { addPool = false; } else { addPool = true; queue = new PoolQueue(localVersion, maxPoolSize); } newPoolQueue = queue.Queue; while (poolQueue.Count > 0) { newPoolQueue.Enqueue(poolQueue.Dequeue()); } ResizePool(queue, false); if (addPool) { _queueList.Add(fileName, queue); } } } return(null); } }
internal override void Open(string strFilename, SQLiteConnectionFlags connectionFlags, SQLiteOpenFlagsEnum openFlags, int maxPoolSize, bool usePool) { // // NOTE: If the database connection is currently open, attempt to // close it now. This must be done because the file name or // other parameters that may impact the underlying database // connection may have changed. // if (_sql != null) { Close(true); } // // NOTE: If the connection was not closed successfully, throw an // exception now. // if (_sql != null) { throw new SQLiteException("connection handle is still active"); } _usePool = usePool; _fileName = strFilename; if (usePool) { _sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion); #if !NET_COMPACT_20 && TRACE_CONNECTION Trace.WriteLine(String.Format("Open16 (Pool): {0}", (_sql != null) ? _sql.ToString() : "<null>")); #endif } if (_sql == null) { try { // do nothing. } finally /* NOTE: Thread.Abort() protection. */ { IntPtr db; SQLiteErrorCode n; #if !SQLITE_STANDARD if ((connectionFlags & SQLiteConnectionFlags.NoExtensionFunctions) != SQLiteConnectionFlags.NoExtensionFunctions) { n = UnsafeNativeMethods.sqlite3_open16_interop(ToUTF8(strFilename), openFlags, out db); } else #endif { // // NOTE: This flag check is designed to enforce the constraint that opening // a database file that does not already exist requires specifying the // "Create" flag, even when a native API is used that does not accept // a flags parameter. // if (((openFlags & SQLiteOpenFlagsEnum.Create) != SQLiteOpenFlagsEnum.Create) && !File.Exists(strFilename)) { throw new SQLiteException(SQLiteErrorCode.CantOpen, strFilename); } n = UnsafeNativeMethods.sqlite3_open16(strFilename, out db); } #if !NET_COMPACT_20 && TRACE_CONNECTION Trace.WriteLine(String.Format("Open16: {0}", db)); #endif if (n != SQLiteErrorCode.Ok) { throw new SQLiteException(n, null); } _sql = new SQLiteConnectionHandle(db, true); } lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ } SQLiteConnection.OnChanged(null, new ConnectionEventArgs( SQLiteConnectionEventType.NewCriticalHandle, null, null, null, null, _sql, strFilename, new object[] { strFilename, connectionFlags, openFlags, maxPoolSize, usePool })); } // Bind functions to this connection. If any previous functions of the same name // were already bound, then the new bindings replace the old. if ((connectionFlags & SQLiteConnectionFlags.NoBindFunctions) != SQLiteConnectionFlags.NoBindFunctions) { if (_functions == null) { _functions = new List <SQLiteFunction>(); } _functions.AddRange(new List <SQLiteFunction>(SQLiteFunction.BindFunctions(this, connectionFlags))); } SetTimeout(0); GC.KeepAlive(_sql); }