System.Data.SQLite
Check-in [cb270f5cd8]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Experimental code to use SafeHandle instead of CriticalHandle for connection handles because they should not be closed until their statements and backup objects are all finalized.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | safeHandle
Files: files | file ages | folders
SHA1: cb270f5cd8a0423da68a4a4be30dac39cc4a7107
User & Date: mistachkin 2012-05-03 06:35:13
Context
2012-05-03
06:35
Experimental code to use SafeHandle instead of CriticalHandle for connection handles because they should not be closed until their statements and backup objects are all finalized. Closed-Leaf check-in: cb270f5cd8 user: mistachkin tags: safeHandle
2012-05-02
22:29
Update version history with all the latest changes. check-in: c063096071 user: mistachkin tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to System.Data.SQLite/SQLite3.cs.

   188    188         }
   189    189       }
   190    190   
   191    191       internal override bool AutoCommit
   192    192       {
   193    193         get
   194    194         {
   195         -        return IsAutocommit(_sql);
          195  +        return IsAutocommit(_sql, _sql);
   196    196         }
   197    197       }
   198    198   
   199    199       internal override long LastInsertRowId
   200    200       {
   201    201         get
   202    202         {
................................................................................
   272    272   
   273    273   #if DEBUG && !NET_COMPACT_20
   274    274           Trace.WriteLine(String.Format("Open: {0}", db));
   275    275   #endif
   276    276   
   277    277           if (n > 0) throw new SQLiteException(n, null);
   278    278   
   279         -        _sql = db;
          279  +        _sql = new SQLiteConnectionHandle(db);
          280  +
          281  +        lock (_sql)
          282  +        {
          283  +            // do nothing.
          284  +        }
   280    285         }
   281    286         // Bind functions to this connection.  If any previous functions of the same name
   282    287         // were already bound, then the new bindings replace the old.
   283    288         _functionsArray = SQLiteFunction.BindFunctions(this, connectionFlags);
   284    289         SetTimeout(0);
   285    290       }
   286    291   
................................................................................
   529    534             }
   530    535           }
   531    536   
   532    537           if (n > 0) throw new SQLiteException(n, GetLastError());
   533    538   
   534    539           strRemain = UTF8ToString(ptr, len);
   535    540   
   536         -        if (stmt != IntPtr.Zero) cmd = new SQLiteStatement(this, flags, stmt, strSql.Substring(0, strSql.Length - strRemain.Length), previous);
          541  +        if (stmt != IntPtr.Zero) cmd = new SQLiteStatement(this, flags, new SQLiteStatementHandle(_sql, stmt), strSql.Substring(0, strSql.Length - strRemain.Length), previous);
   537    542   
   538    543           return cmd;
   539    544         }
   540    545         finally
   541    546         {
   542    547           handle.Free();
   543    548         }
................................................................................
  1471   1476           IntPtr backup = UnsafeNativeMethods.sqlite3_backup_init(
  1472   1477               destHandle, zDestName, sourceHandle, zSourceName);
  1473   1478   
  1474   1479           if (backup == IntPtr.Zero)
  1475   1480               throw new SQLiteException(ResultCode(), GetLastError());
  1476   1481   
  1477   1482           return new SQLiteBackup(
  1478         -            this, backup, destHandle, zDestName, sourceHandle, zSourceName);
         1483  +            this, new SQLiteBackupHandle(destHandle, backup),
         1484  +            destHandle, zDestName, sourceHandle, zSourceName);
  1479   1485       }
  1480   1486   
  1481   1487       /// <summary>
  1482   1488       /// Copies up to N pages from the source database to the destination
  1483   1489       /// database associated with the specified backup object.
  1484   1490       /// </summary>
  1485   1491       /// <param name="backup">The backup object to use.</param>

Changes to System.Data.SQLite/SQLite3_UTF16.cs.

   119    119   
   120    120   #if DEBUG && !NET_COMPACT_20
   121    121           Trace.WriteLine(String.Format("Open: {0}", db));
   122    122   #endif
   123    123   
   124    124           if (n > 0) throw new SQLiteException(n, null);
   125    125   
   126         -        _sql = db;
          126  +        _sql = new SQLiteConnectionHandle(db);
          127  +
          128  +        lock (_sql)
          129  +        {
          130  +            // do nothing.
          131  +        }
   127    132         }
   128    133         _functionsArray = SQLiteFunction.BindFunctions(this, connectionFlags);
   129    134         SetTimeout(0);
   130    135       }
   131    136   
   132    137       internal override void Bind_DateTime(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, DateTime dt)
   133    138       {

Changes to System.Data.SQLite/SQLiteBase.cs.

   343    343       // They exist here because they are called during the finalization of
   344    344       // a SQLiteStatementHandle, SQLiteConnectionHandle, and SQLiteFunctionCookieHandle.
   345    345       // Therefore these functions have to be static, and have to be low-level.
   346    346   
   347    347       internal static string GetLastError(SQLiteConnectionHandle hdl, IntPtr db)
   348    348       {
   349    349           if ((hdl == null) || (db == IntPtr.Zero))
   350         -            return "invalid connection or database handle";
          350  +            return "null connection or database handle";
          351  +
          352  +        if (hdl.IsClosed || hdl.IsInvalid)
          353  +            return "closed or invalid connection handle";
   351    354   
   352    355           lock (hdl)
   353    356           {
   354    357   #if !SQLITE_STANDARD
   355    358               int len;
   356    359               return UTF8ToString(UnsafeNativeMethods.sqlite3_errmsg_interop(db, out len), len);
   357    360   #else
   358    361               return UTF8ToString(UnsafeNativeMethods.sqlite3_errmsg(db), -1);
   359    362   #endif
   360    363           }
   361    364       }
   362    365   
   363         -    internal static void FinishBackup(IntPtr backup)
          366  +    internal static void FinishBackup(SQLiteConnectionHandle hdl, IntPtr backup)
   364    367       {
   365         -        if (backup == IntPtr.Zero) return;
   366         -        int n = UnsafeNativeMethods.sqlite3_backup_finish(backup);
   367         -        if (n > 0) throw new SQLiteException(n, null);
          368  +        if ((hdl == null) || (backup == IntPtr.Zero)) return;
          369  +        if (hdl.IsClosed || hdl.IsInvalid) return;
          370  +        lock (hdl)
          371  +        {
          372  +            int n = UnsafeNativeMethods.sqlite3_backup_finish(backup);
          373  +            if (n > 0) throw new SQLiteException(n, null);
          374  +        }
   368    375       }
   369    376   
   370         -    internal static void FinalizeStatement(IntPtr stmt)
          377  +    internal static void FinalizeStatement(SQLiteConnectionHandle hdl, IntPtr stmt)
   371    378       {
   372         -        if (stmt == IntPtr.Zero) return;
          379  +        if ((hdl == null) || (stmt == IntPtr.Zero)) return;
          380  +        if (hdl.IsClosed || hdl.IsInvalid) return;
          381  +        lock (hdl)
          382  +        {
   373    383   #if !SQLITE_STANDARD
   374         -        int n = UnsafeNativeMethods.sqlite3_finalize_interop(stmt);
          384  +            int n = UnsafeNativeMethods.sqlite3_finalize_interop(stmt);
   375    385   #else
   376         -        int n = UnsafeNativeMethods.sqlite3_finalize(stmt);
          386  +            int n = UnsafeNativeMethods.sqlite3_finalize(stmt);
   377    387   #endif
   378         -        if (n > 0) throw new SQLiteException(n, null);
          388  +            if (n > 0) throw new SQLiteException(n, null);
          389  +        }
   379    390       }
   380    391   
   381    392       internal static void CloseConnection(SQLiteConnectionHandle hdl, IntPtr db)
   382    393       {
   383    394           if ((hdl == null) || (db == IntPtr.Zero)) return;
   384    395           lock (hdl)
   385    396           {
................................................................................
   392    403               if (n > 0) throw new SQLiteException(n, GetLastError(hdl, db));
   393    404           }
   394    405       }
   395    406   
   396    407       internal static void ResetConnection(SQLiteConnectionHandle hdl, IntPtr db)
   397    408       {
   398    409           if ((hdl == null) || (db == IntPtr.Zero)) return;
          410  +        if (hdl.IsClosed || hdl.IsInvalid) return;
   399    411           lock (hdl)
   400    412           {
   401    413               IntPtr stmt = IntPtr.Zero;
   402    414               int n;
   403    415               do
   404    416               {
   405    417                   stmt = UnsafeNativeMethods.sqlite3_next_stmt(db, stmt);
................................................................................
   409    421                       n = UnsafeNativeMethods.sqlite3_reset_interop(stmt);
   410    422   #else
   411    423                       n = UnsafeNativeMethods.sqlite3_reset(stmt);
   412    424   #endif
   413    425                   }
   414    426               } while (stmt != IntPtr.Zero);
   415    427   
   416         -            if (IsAutocommit(db) == false) // a transaction is pending on the connection
          428  +            if (IsAutocommit(hdl, db) == false) // a transaction is pending on the connection
   417    429               {
   418    430                   n = UnsafeNativeMethods.sqlite3_exec(db, ToUTF8("ROLLBACK"), IntPtr.Zero, IntPtr.Zero, out stmt);
   419    431                   if (n > 0) throw new SQLiteException(n, GetLastError(hdl, db));
   420    432               }
   421    433           }
   422    434       }
   423    435   
   424         -    internal static bool IsAutocommit(IntPtr db)
          436  +    internal static bool IsAutocommit(SQLiteConnectionHandle hdl, IntPtr db)
   425    437       {
   426         -      if (db == IntPtr.Zero) return false;
   427         -      return (UnsafeNativeMethods.sqlite3_get_autocommit(db) == 1);
          438  +        if ((hdl == null) || (db == IntPtr.Zero)) return false;
          439  +        if (hdl.IsClosed || hdl.IsInvalid) return false;
          440  +        lock (hdl)
          441  +        {
          442  +            return (UnsafeNativeMethods.sqlite3_get_autocommit(db) == 1);
          443  +        }
   428    444       }
   429         -
   430    445     }
   431    446   
   432    447     internal interface ISQLiteSchemaExtensions
   433    448     {
   434    449       void BuildTempSchema(SQLiteConnection cnn);
   435    450     }
   436    451   

Changes to System.Data.SQLite/UnsafeNativeMethods.cs.

  1351   1351         get { return _isClosed; }
  1352   1352       }
  1353   1353   
  1354   1354       public abstract bool IsInvalid
  1355   1355       {
  1356   1356         get;
  1357   1357       }
  1358         -
  1359   1358     }
  1360         -
  1361   1359   #endif
  1362   1360   
  1363   1361     // Handles the unmanaged database pointer, and provides finalization support for it.
  1364         -  internal class SQLiteConnectionHandle : CriticalHandle
         1362  +  internal class SQLiteConnectionHandle : SafeHandle
  1365   1363     {
  1366   1364       public static implicit operator IntPtr(SQLiteConnectionHandle db)
  1367   1365       {
  1368   1366         return (db != null) ? db.handle : IntPtr.Zero;
  1369   1367       }
  1370   1368   
  1371         -    public static implicit operator SQLiteConnectionHandle(IntPtr db)
         1369  +    private SQLiteConnectionHandle()
         1370  +        : base(IntPtr.Zero, true)
  1372   1371       {
  1373         -      return new SQLiteConnectionHandle(db);
  1374   1372       }
  1375   1373   
  1376         -    private SQLiteConnectionHandle(IntPtr db)
         1374  +    internal SQLiteConnectionHandle(IntPtr db)
  1377   1375         : this()
  1378   1376       {
  1379   1377         SetHandle(db);
  1380         -    }
  1381         -
  1382         -    internal SQLiteConnectionHandle()
  1383         -      : base(IntPtr.Zero)
  1384         -    {
  1385   1378       }
  1386   1379   
  1387   1380       protected override bool ReleaseHandle()
  1388   1381       {
  1389   1382         try
  1390   1383         {
  1391   1384   #if !PLATFORM_COMPACTFRAMEWORK
................................................................................
  1458   1451       }
  1459   1452   #endif
  1460   1453     }
  1461   1454   
  1462   1455     // Provides finalization support for unmanaged SQLite statements.
  1463   1456     internal class SQLiteStatementHandle : CriticalHandle
  1464   1457     {
         1458  +    internal SQLiteConnectionHandle cnn;
         1459  +    private bool releaseNeeded;
         1460  +
  1465   1461       public static implicit operator IntPtr(SQLiteStatementHandle stmt)
  1466   1462       {
  1467   1463         return (stmt != null) ? stmt.handle : IntPtr.Zero;
  1468   1464       }
  1469   1465   
  1470         -    public static implicit operator SQLiteStatementHandle(IntPtr stmt)
         1466  +    private SQLiteStatementHandle()
         1467  +        : base(IntPtr.Zero)
  1471   1468       {
  1472         -      return new SQLiteStatementHandle(stmt);
  1473   1469       }
  1474   1470   
  1475   1471       private SQLiteStatementHandle(IntPtr stmt)
  1476   1472         : this()
  1477   1473       {
  1478   1474         SetHandle(stmt);
  1479   1475       }
  1480   1476   
  1481         -    internal SQLiteStatementHandle()
  1482         -      : base(IntPtr.Zero)
         1477  +    internal SQLiteStatementHandle(SQLiteConnectionHandle cnn, IntPtr stmt)
         1478  +        : this(stmt)
  1483   1479       {
         1480  +      if (cnn != null)
         1481  +        cnn.DangerousAddRef(ref releaseNeeded);
         1482  +
         1483  +      this.cnn = cnn;
  1484   1484       }
  1485   1485   
  1486   1486       protected override bool ReleaseHandle()
  1487   1487       {
  1488   1488         try
  1489   1489         {
  1490   1490   #if !PLATFORM_COMPACTFRAMEWORK
  1491   1491           IntPtr localHandle = Interlocked.Exchange(
  1492   1492             ref handle, IntPtr.Zero);
  1493   1493   
  1494   1494           if (localHandle != IntPtr.Zero)
  1495         -          SQLiteBase.FinalizeStatement(localHandle);
         1495  +          SQLiteBase.FinalizeStatement(cnn, localHandle);
         1496  +
         1497  +        if ((cnn != null) && releaseNeeded)
         1498  +          cnn.DangerousRelease();
  1496   1499   
  1497   1500   #if DEBUG && !NET_COMPACT_20
  1498   1501           try
  1499   1502           {
  1500   1503             Trace.WriteLine(String.Format(
  1501   1504                 "FinalizeStatement: {0}", localHandle));
  1502   1505           }
................................................................................
  1503   1506           catch
  1504   1507           {
  1505   1508           }
  1506   1509   #endif
  1507   1510   #else
  1508   1511           if (handle != IntPtr.Zero)
  1509   1512           {
  1510         -          SQLiteBase.FinalizeStatement(handle);
         1513  +          SQLiteBase.FinalizeStatement(cnn, handle);
  1511   1514             SetHandle(IntPtr.Zero);
  1512   1515           }
  1513   1516   #endif
  1514   1517   
  1515   1518   #if DEBUG
  1516   1519           return true;
  1517   1520   #endif
................................................................................
  1557   1560       }
  1558   1561   #endif
  1559   1562     }
  1560   1563   
  1561   1564     // Provides finalization support for unmanaged SQLite backup objects.
  1562   1565     internal class SQLiteBackupHandle : CriticalHandle
  1563   1566     {
         1567  +      internal SQLiteConnectionHandle cnn;
         1568  +      private bool releaseNeeded;
         1569  +
  1564   1570         public static implicit operator IntPtr(SQLiteBackupHandle backup)
  1565   1571         {
  1566   1572             return (backup != null) ? backup.handle : IntPtr.Zero;
  1567   1573         }
  1568   1574   
  1569         -      public static implicit operator SQLiteBackupHandle(IntPtr backup)
         1575  +      private SQLiteBackupHandle()
         1576  +          : base(IntPtr.Zero)
  1570   1577         {
  1571         -          return new SQLiteBackupHandle(backup);
  1572   1578         }
  1573   1579   
  1574   1580         private SQLiteBackupHandle(IntPtr backup)
  1575   1581             : this()
  1576   1582         {
  1577   1583             SetHandle(backup);
  1578   1584         }
  1579   1585   
  1580         -      internal SQLiteBackupHandle()
  1581         -          : base(IntPtr.Zero)
         1586  +      internal SQLiteBackupHandle(SQLiteConnectionHandle cnn, IntPtr backup)
         1587  +          : this(backup)
  1582   1588         {
         1589  +          if (cnn != null)
         1590  +              cnn.DangerousAddRef(ref releaseNeeded);
         1591  +
         1592  +          this.cnn = cnn;
  1583   1593         }
  1584   1594   
  1585   1595         protected override bool ReleaseHandle()
  1586   1596         {
  1587   1597             try
  1588   1598             {
  1589   1599   #if !PLATFORM_COMPACTFRAMEWORK
  1590   1600                 IntPtr localHandle = Interlocked.Exchange(
  1591   1601                     ref handle, IntPtr.Zero);
  1592   1602   
  1593   1603                 if (localHandle != IntPtr.Zero)
  1594         -                  SQLiteBase.FinishBackup(localHandle);
         1604  +                  SQLiteBase.FinishBackup(cnn, localHandle);
         1605  +
         1606  +              if ((cnn != null) && releaseNeeded)
         1607  +                  cnn.DangerousRelease();
  1595   1608   
  1596   1609   #if DEBUG && !NET_COMPACT_20
  1597   1610                 try
  1598   1611                 {
  1599   1612                     Trace.WriteLine(String.Format(
  1600   1613                         "FinishBackup: {0}", localHandle));
  1601   1614                 }
................................................................................
  1602   1615                 catch
  1603   1616                 {
  1604   1617                 }
  1605   1618   #endif
  1606   1619   #else
  1607   1620                 if (handle != IntPtr.Zero)
  1608   1621                 {
  1609         -                SQLiteBase.FinishBackup(handle);
         1622  +                SQLiteBase.FinishBackup(cnn, handle);
  1610   1623                   SetHandle(IntPtr.Zero);
  1611   1624                 }
  1612   1625   #endif
  1613   1626   
  1614   1627   #if DEBUG
  1615   1628                 return true;
  1616   1629   #endif