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

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

Overview
Comment:Attempt to invalidate all native delegates from a connection during its disposal. Pursuant to [0e48e80333]. Still needs tests.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | tkt-0e48e80333
Files: files | file ages | folders
SHA1: aa991525681a68d2bf2ed86014ad0ba1934f5cc8
User & Date: mistachkin 2017-11-09 08:04:19
Context
2017-11-09
08:37
Add another error message to the UnhookNativeCallbacks method. check-in: df1cca89d1 user: mistachkin tags: tkt-0e48e80333
08:04
Attempt to invalidate all native delegates from a connection during its disposal. Pursuant to [0e48e80333]. Still needs tests. check-in: aa99152568 user: mistachkin tags: tkt-0e48e80333
2017-11-07
03:38
Update a doc comment. check-in: 67190e9b52 user: mistachkin tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

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

    63     63   
    64     64       /// <summary>
    65     65       /// The opaque pointer returned to us by the sqlite provider
    66     66       /// </summary>
    67     67       protected internal SQLiteConnectionHandle _sql;
    68     68       protected string _fileName;
    69     69       protected SQLiteConnectionFlags _flags;
           70  +    private bool _setLogCallback;
    70     71       protected bool _usePool;
    71     72       protected int _poolVersion;
    72     73       private int _cancelCount;
    73     74   
    74     75   #if (NET_35 || NET_40 || NET_45 || NET_451 || NET_452 || NET_46 || NET_461 || NET_462 || NET_47) && !PLATFORM_COMPACTFRAMEWORK
    75     76       private bool _buildingSchema;
    76     77   #endif
................................................................................
   188    189                   // release unmanaged resources here...
   189    190                   //////////////////////////////////////
   190    191   
   191    192   #if INTEROP_VIRTUAL_TABLE
   192    193                   DisposeModules();
   193    194   #endif
   194    195   
   195         -                Close(false); /* Disposing, cannot throw. */
          196  +                Close(true); /* Disposing, cannot throw. */
   196    197               }
   197    198           }
   198    199           finally
   199    200           {
   200    201               base.Dispose(disposing);
   201    202   
   202    203               //
................................................................................
   241    242   
   242    243       ///////////////////////////////////////////////////////////////////////////////////////////////
   243    244   
   244    245       // It isn't necessary to cleanup any functions we've registered.  If the connection
   245    246       // goes to the pool and is resurrected later, re-registered functions will overwrite the
   246    247       // previous functions.  The SQLiteFunctionCookieHandle will take care of freeing unmanaged
   247    248       // resources belonging to the previously-registered functions.
   248         -    internal override void Close(bool canThrow)
          249  +    internal override void Close(bool disposing)
   249    250       {
   250    251         if (_sql != null)
   251    252         {
   252    253             if (!_sql.OwnHandle)
   253    254             {
   254    255                 _sql = null;
   255    256                 return;
   256    257             }
   257    258   
   258    259             bool unbindFunctions = ((_flags & SQLiteConnectionFlags.UnbindFunctionsOnClose)
   259    260                   == SQLiteConnectionFlags.UnbindFunctionsOnClose);
          261  +
          262  +      retry:
   260    263   
   261    264             if (_usePool)
   262    265             {
   263         -              if (SQLiteBase.ResetConnection(_sql, _sql, canThrow))
          266  +              if (SQLiteBase.ResetConnection(_sql, _sql, !disposing) &&
          267  +                  UnhookNativeCallbacks(true, !disposing))
   264    268                 {
   265    269                     if (unbindFunctions)
   266    270                     {
   267    271                         if (SQLiteFunction.UnbindAllFunctions(this, _flags, false))
   268    272                         {
   269    273   #if !NET_COMPACT_20 && TRACE_CONNECTION
   270    274                             Trace.WriteLine(HelperMethods.StringFormat(
................................................................................
   289    293   #endif
   290    294   
   291    295                     SQLiteConnectionPool.Add(_fileName, _sql, _poolVersion);
   292    296   
   293    297                     SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
   294    298                         SQLiteConnectionEventType.ClosedToPool, null, null,
   295    299                         null, null, _sql, _fileName, new object[] {
   296         -                      typeof(SQLite3), canThrow, _fileName, _poolVersion }));
          300  +                      typeof(SQLite3), !disposing, _fileName, _poolVersion }));
   297    301   
   298    302   #if !NET_COMPACT_20 && TRACE_CONNECTION
   299    303                     Trace.WriteLine(HelperMethods.StringFormat(
   300    304                         CultureInfo.CurrentCulture,
   301    305                         "Close (Pool) Success: {0}",
   302    306                         HandleToString()));
   303    307   #endif
   304    308                 }
   305         -#if !NET_COMPACT_20 && TRACE_CONNECTION
   306    309                 else
   307    310                 {
          311  +#if !NET_COMPACT_20 && TRACE_CONNECTION
   308    312                     Trace.WriteLine(HelperMethods.StringFormat(
   309    313                         CultureInfo.CurrentCulture,
   310    314                         "Close (Pool) Failure: {0}",
   311    315                         HandleToString()));
          316  +#endif
          317  +
          318  +                  //
          319  +                  // NOTE: This connection cannot be added to the pool;
          320  +                  //       therefore, just use the normal disposal
          321  +                  //       procedure on it.
          322  +                  //
          323  +                  _usePool = false;
          324  +                  goto retry;
   312    325                 }
   313         -#endif
   314    326             }
   315    327             else
   316    328             {
          329  +              /* IGNORED */
          330  +              UnhookNativeCallbacks(disposing, !disposing);
          331  +
   317    332                 if (unbindFunctions)
   318    333                 {
   319    334                     if (SQLiteFunction.UnbindAllFunctions(this, _flags, false))
   320    335                     {
   321    336   #if !NET_COMPACT_20 && TRACE_CONNECTION
   322    337                         Trace.WriteLine(HelperMethods.StringFormat(
   323    338                             CultureInfo.CurrentCulture,
................................................................................
   943    958       {
   944    959         //
   945    960         // NOTE: If the database connection is currently open, attempt to
   946    961         //       close it now.  This must be done because the file name or
   947    962         //       other parameters that may impact the underlying database
   948    963         //       connection may have changed.
   949    964         //
   950         -      if (_sql != null) Close(true);
          965  +      if (_sql != null) Close(false);
   951    966   
   952    967         //
   953    968         // NOTE: If the connection was not closed successfully, throw an
   954    969         //       exception now.
   955    970         //
   956    971         if (_sql != null)
   957    972             throw new SQLiteException("connection handle is still active");
................................................................................
  2940   2955       /// <param name="func">The callback function to invoke.</param>
  2941   2956       /// <returns>Returns a result code</returns>
  2942   2957       internal override SQLiteErrorCode SetLogCallback(SQLiteLogCallback func)
  2943   2958       {
  2944   2959           SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_config_log(
  2945   2960               SQLiteConfigOpsEnum.SQLITE_CONFIG_LOG, func, IntPtr.Zero);
  2946   2961   
         2962  +        if (rc == SQLiteErrorCode.Ok)
         2963  +            _setLogCallback = (func != null);
         2964  +
  2947   2965           return rc;
  2948   2966       }
         2967  +
         2968  +    ///////////////////////////////////////////////////////////////////////////////////////////////
         2969  +
         2970  +    /// <summary>
         2971  +    /// Appends an error message and an appropriate line-ending to a <see cref="StringBuilder" />
         2972  +    /// instance.  This is useful because the .NET Compact Framework has a slightly different set
         2973  +    /// of supported methods for the <see cref="StringBuilder" /> class.
         2974  +    /// </summary>
         2975  +    /// <param name="builder">
         2976  +    /// The <see cref="StringBuilder" /> instance to append to.
         2977  +    /// </param>
         2978  +    /// <param name="message">
         2979  +    /// The message to append.  It will be followed by an appropriate line-ending.
         2980  +    /// </param>
         2981  +    private static void AppendError(
         2982  +        StringBuilder builder,
         2983  +        string message
         2984  +        )
         2985  +    {
         2986  +        if (builder == null)
         2987  +            return;
         2988  +
         2989  +#if !PLATFORM_COMPACTFRAMEWORK
         2990  +        builder.AppendLine(message);
         2991  +#else
         2992  +        builder.Append(message);
         2993  +        builder.Append("\r\n");
         2994  +#endif
         2995  +    }
         2996  +
         2997  +    ///////////////////////////////////////////////////////////////////////////////////////////////
         2998  +
         2999  +    /// <summary>
         3000  +    /// This method attempts to cause the SQLite native library to invalidate
         3001  +    /// its function pointers that refer to this instance.  This is necessary
         3002  +    /// to prevent calls from native code into delegates that may have been
         3003  +    /// garbage collected.  Normally, these types of issues can only arise for
         3004  +    /// connections that are added to the pool; howver, it is good practice to
         3005  +    /// unconditionally invalidate function pointers that may refer to objects
         3006  +    /// being disposed.
         3007  +    /// <param name="includeGlobal">
         3008  +    /// Non-zero to also invalidate global function pointers (i.e. those that
         3009  +    /// are not directly associated with this connection on the native side).
         3010  +    /// </param>
         3011  +    /// <param name="canThrow">
         3012  +    /// Non-zero if this method is being executed within a context where it can
         3013  +    /// throw an exception in the event of failure; otherwise, zero.
         3014  +    /// </param>
         3015  +    /// </summary>
         3016  +    /// <returns>
         3017  +    /// Non-zero if this method was successful; otherwise, zero.
         3018  +    /// </returns>
         3019  +    private bool UnhookNativeCallbacks(
         3020  +        bool includeGlobal,
         3021  +        bool canThrow
         3022  +        )
         3023  +    {
         3024  +        //
         3025  +        // NOTE: Initially, this method assumes success.  Then, if any attempt
         3026  +        //       to invalidate a function pointer fails, the overall result is
         3027  +        //       set to failure.  However, this will not prevent further
         3028  +        //       attempts, if any, to invalidate subsequent function pointers.
         3029  +        //
         3030  +        bool result = true;
         3031  +        SQLiteErrorCode rc = SQLiteErrorCode.Ok;
         3032  +        StringBuilder builder = new StringBuilder();
         3033  +
         3034  +        ///////////////////////////////////////////////////////////////////////////////////////////
         3035  +
         3036  +        #region Rollback Hook (Per-Connection)
         3037  +        try
         3038  +        {
         3039  +            SetRollbackHook(null); /* throw */
         3040  +        }
         3041  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3042  +        catch (Exception e)
         3043  +#else
         3044  +        catch (Exception)
         3045  +#endif
         3046  +        {
         3047  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3048  +            try
         3049  +            {
         3050  +                Trace.WriteLine(HelperMethods.StringFormat(
         3051  +                    CultureInfo.CurrentCulture,
         3052  +                    "Failed to unset rollback hook: {0}",
         3053  +                    e)); /* throw */
         3054  +            }
         3055  +            catch
         3056  +            {
         3057  +                // do nothing.
         3058  +            }
         3059  +#endif
         3060  +
         3061  +            AppendError(builder, "failed to unset rollback hook");
         3062  +            rc = SQLiteErrorCode.Error;
         3063  +
         3064  +            result = false;
         3065  +        }
         3066  +        #endregion
         3067  +
         3068  +        ///////////////////////////////////////////////////////////////////////////////////////////
         3069  +
         3070  +        #region Trace Callback (Per-Connection)
         3071  +        try
         3072  +        {
         3073  +            SetTraceCallback(null); /* throw */
         3074  +        }
         3075  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3076  +        catch (Exception e)
         3077  +#else
         3078  +        catch (Exception)
         3079  +#endif
         3080  +        {
         3081  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3082  +            try
         3083  +            {
         3084  +                Trace.WriteLine(HelperMethods.StringFormat(
         3085  +                    CultureInfo.CurrentCulture,
         3086  +                    "Failed to unset trace callback: {0}",
         3087  +                    e)); /* throw */
         3088  +            }
         3089  +            catch
         3090  +            {
         3091  +                // do nothing.
         3092  +            }
         3093  +#endif
         3094  +
         3095  +            AppendError(builder, "failed to unset trace callback");
         3096  +            rc = SQLiteErrorCode.Error;
         3097  +
         3098  +            result = false;
         3099  +        }
         3100  +        #endregion
         3101  +
         3102  +        ///////////////////////////////////////////////////////////////////////////////////////////
         3103  +
         3104  +        #region Commit Hook (Per-Connection)
         3105  +        try
         3106  +        {
         3107  +            SetCommitHook(null); /* throw */
         3108  +        }
         3109  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3110  +        catch (Exception e)
         3111  +#else
         3112  +        catch (Exception)
         3113  +#endif
         3114  +        {
         3115  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3116  +            try
         3117  +            {
         3118  +                Trace.WriteLine(HelperMethods.StringFormat(
         3119  +                    CultureInfo.CurrentCulture,
         3120  +                    "Failed to unset commit hook: {0}",
         3121  +                    e)); /* throw */
         3122  +            }
         3123  +            catch
         3124  +            {
         3125  +                // do nothing.
         3126  +            }
         3127  +#endif
         3128  +
         3129  +            AppendError(builder, "failed to unset commit hook");
         3130  +            rc = SQLiteErrorCode.Error;
         3131  +
         3132  +            result = false;
         3133  +        }
         3134  +        #endregion
         3135  +
         3136  +        ///////////////////////////////////////////////////////////////////////////////////////////
         3137  +
         3138  +        #region Update Hook (Per-Connection)
         3139  +        try
         3140  +        {
         3141  +            SetUpdateHook(null); /* throw */
         3142  +        }
         3143  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3144  +        catch (Exception e)
         3145  +#else
         3146  +        catch (Exception)
         3147  +#endif
         3148  +        {
         3149  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3150  +            try
         3151  +            {
         3152  +                Trace.WriteLine(HelperMethods.StringFormat(
         3153  +                    CultureInfo.CurrentCulture,
         3154  +                    "Failed to unset update hook: {0}",
         3155  +                    e)); /* throw */
         3156  +            }
         3157  +            catch
         3158  +            {
         3159  +                // do nothing.
         3160  +            }
         3161  +#endif
         3162  +
         3163  +            AppendError(builder, "failed to unset update hook");
         3164  +            rc = SQLiteErrorCode.Error;
         3165  +
         3166  +            result = false;
         3167  +        }
         3168  +        #endregion
         3169  +
         3170  +        ///////////////////////////////////////////////////////////////////////////////////////////
         3171  +
         3172  +        #region Authorizer Hook (Per-Connection)
         3173  +        try
         3174  +        {
         3175  +            SetAuthorizerHook(null); /* throw */
         3176  +        }
         3177  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3178  +        catch (Exception e)
         3179  +#else
         3180  +        catch (Exception)
         3181  +#endif
         3182  +        {
         3183  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3184  +            try
         3185  +            {
         3186  +                Trace.WriteLine(HelperMethods.StringFormat(
         3187  +                    CultureInfo.CurrentCulture,
         3188  +                    "Failed to unset authorizer hook: {0}",
         3189  +                    e)); /* throw */
         3190  +            }
         3191  +            catch
         3192  +            {
         3193  +                // do nothing.
         3194  +            }
         3195  +#endif
         3196  +
         3197  +            AppendError(builder, "failed to unset authorizer hook");
         3198  +            rc = SQLiteErrorCode.Error;
         3199  +
         3200  +            result = false;
         3201  +        }
         3202  +        #endregion
         3203  +
         3204  +        ///////////////////////////////////////////////////////////////////////////////////////////
         3205  +
         3206  +        #region Progress Hook (Per-Connection)
         3207  +        try
         3208  +        {
         3209  +            SetProgressHook(0, null); /* throw */
         3210  +        }
         3211  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3212  +        catch (Exception e)
         3213  +#else
         3214  +        catch (Exception)
         3215  +#endif
         3216  +        {
         3217  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3218  +            try
         3219  +            {
         3220  +                Trace.WriteLine(HelperMethods.StringFormat(
         3221  +                    CultureInfo.CurrentCulture,
         3222  +                    "Failed to unset progress hook: {0}",
         3223  +                    e)); /* throw */
         3224  +            }
         3225  +            catch
         3226  +            {
         3227  +                // do nothing.
         3228  +            }
         3229  +#endif
         3230  +
         3231  +            AppendError(builder, "failed to unset progress hook");
         3232  +            rc = SQLiteErrorCode.Error;
         3233  +
         3234  +            result = false;
         3235  +        }
         3236  +        #endregion
         3237  +
         3238  +        ///////////////////////////////////////////////////////////////////////////////////////////
         3239  +
         3240  +        #region Log Callback (Global)
         3241  +        //
         3242  +        // NOTE: We have to be careful here because the log callback
         3243  +        //       is not per-connection on the native side.  It should
         3244  +        //       only be unset by this method if this instance was
         3245  +        //       responsible for setting it.
         3246  +        //
         3247  +        if (includeGlobal && _setLogCallback)
         3248  +        {
         3249  +            try
         3250  +            {
         3251  +                SQLiteErrorCode rc2 = SetLogCallback(null); /* throw */
         3252  +
         3253  +                if (rc2 != SQLiteErrorCode.Ok)
         3254  +                {
         3255  +                    rc = rc2;
         3256  +                    result = false;
         3257  +                }
         3258  +            }
         3259  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3260  +            catch (Exception e)
         3261  +#else
         3262  +            catch (Exception)
         3263  +#endif
         3264  +            {
         3265  +#if !NET_COMPACT_20 && TRACE_CONNECTION
         3266  +                try
         3267  +                {
         3268  +                    Trace.WriteLine(HelperMethods.StringFormat(
         3269  +                        CultureInfo.CurrentCulture,
         3270  +                        "Failed to unset log callback: {0}",
         3271  +                        e)); /* throw */
         3272  +                }
         3273  +                catch
         3274  +                {
         3275  +                    // do nothing.
         3276  +                }
         3277  +#endif
         3278  +
         3279  +                AppendError(builder, "failed to unset log callback");
         3280  +                rc = SQLiteErrorCode.Error;
         3281  +
         3282  +                result = false;
         3283  +            }
         3284  +        }
         3285  +        #endregion
         3286  +
         3287  +        ///////////////////////////////////////////////////////////////////////////////////////////
         3288  +
         3289  +        if (!result && canThrow)
         3290  +            throw new SQLiteException(rc, builder.ToString());
         3291  +
         3292  +        return result;
         3293  +    }
  2949   3294   
  2950   3295       ///////////////////////////////////////////////////////////////////////////////////////////////
  2951   3296   
  2952   3297       /// <summary>
  2953   3298       /// Creates a new SQLite backup object based on the provided destination
  2954   3299       /// database connection.  The source database connection is the one
  2955   3300       /// associated with this object.  The source and destination database

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

   134    134       {
   135    135         //
   136    136         // NOTE: If the database connection is currently open, attempt to
   137    137         //       close it now.  This must be done because the file name or
   138    138         //       other parameters that may impact the underlying database
   139    139         //       connection may have changed.
   140    140         //
   141         -      if (_sql != null) Close(true);
          141  +      if (_sql != null) Close(false);
   142    142   
   143    143         //
   144    144         // NOTE: If the connection was not closed successfully, throw an
   145    145         //       exception now.
   146    146         //
   147    147         if (_sql != null)
   148    148             throw new SQLiteException("connection handle is still active");

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

   125    125       /// <summary>
   126    126       /// Closes the currently-open database.
   127    127       /// </summary>
   128    128       /// <remarks>
   129    129       /// After the database has been closed implemeters should call SQLiteFunction.UnbindFunctions() to deallocate all interop allocated
   130    130       /// memory associated with the user-defined functions and collating sequences tied to the closed connection.
   131    131       /// </remarks>
   132         -    /// <param name="canThrow">Non-zero if the operation is allowed to throw exceptions, zero otherwise.</param>
   133         -    internal abstract void Close(bool canThrow);
          132  +    /// <param name="disposing">Non-zero if connection is being disposed, zero otherwise.</param>
          133  +    internal abstract void Close(bool disposing);
   134    134       /// <summary>
   135    135       /// Sets the busy timeout on the connection.  SQLiteCommand will call this before executing any command.
   136    136       /// </summary>
   137    137       /// <param name="nTimeoutMS">The number of milliseconds to wait before returning SQLITE_BUSY</param>
   138    138       internal abstract void SetTimeout(int nTimeoutMS);
   139    139       /// <summary>
   140    140       /// Returns the text of the last error issued by SQLite

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

  2917   2917   
  2918   2918             _sql = null;
  2919   2919             _enlistment = null;
  2920   2920           }
  2921   2921   #endif
  2922   2922           if (_sql != null)
  2923   2923           {
  2924         -          _sql.Close(!_disposing);
         2924  +          _sql.Close(_disposing);
  2925   2925             _sql = null;
  2926   2926           }
  2927   2927           _transactionLevel = 0;
  2928   2928           _transactionSequence = 0;
  2929   2929         }
  2930   2930   
  2931   2931         StateChangeEventArgs eventArgs = null;
................................................................................
  4843   4843       public SQLiteErrorCode Shutdown()
  4844   4844       {
  4845   4845           CheckDisposed();
  4846   4846   
  4847   4847           if (_sql == null)
  4848   4848               throw new InvalidOperationException("Database connection not valid for shutdown.");
  4849   4849   
  4850         -        _sql.Close(true); /* NOTE: MUST be closed before shutdown. */
         4850  +        _sql.Close(false); /* NOTE: MUST be closed before shutdown. */
  4851   4851           SQLiteErrorCode rc = _sql.Shutdown();
  4852   4852   
  4853   4853   #if !NET_COMPACT_20 && TRACE_CONNECTION
  4854   4854           if (rc != SQLiteErrorCode.Ok)
  4855   4855               System.Diagnostics.Trace.WriteLine(HelperMethods.StringFormat(
  4856   4856                   CultureInfo.CurrentCulture,
  4857   4857                   "Shutdown (Instance) Failed: {0}", rc));