System.Data.SQLite
Check-in [3054af47e2]
Not logged in

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

Overview
Comment:Prevent the SQLiteLog.DomainUnload method from being added multiple times to the AppDomain.DomainUnload event, ticket [0d5b1ef362]. Stop trying to configure the SQLite logging interface in non-default AppDomains, ticket [ac47dd230a].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3054af47e2d8d92a8f9b22fabe23fd5ea156c57c
User & Date: mistachkin 2011-10-04 08:05:22
References
2011-10-04
09:19 Ticket [ac47dd230a] Not Compatible with MSTest (concurrent App Domains) status still Closed with 2 other changes artifact: 8ead5f22bd user: mistachkin
08:10 Closed ticket [ac47dd230a]. artifact: 35e01feb19 user: mistachkin
08:06 Closed ticket [0d5b1ef362]: SQLiteLog.DomainUnload registered many times plus 4 other changes artifact: b809c056ec user: mistachkin
Context
2011-10-04
09:03
The unit test infrastructure should make sure the native SQLite library is actually available. Also, update all version and release information for 1.0.76.0. check-in: 374035c537 user: mistachkin tags: trunk
08:05
Prevent the SQLiteLog.DomainUnload method from being added multiple times to the AppDomain.DomainUnload event, ticket [0d5b1ef362]. Stop trying to configure the SQLite logging interface in non-default AppDomains, ticket [ac47dd230a]. check-in: 3054af47e2 user: mistachkin tags: trunk
2011-10-03
22:54
Update downloads page for 1.0.75.0 release. check-in: fdf8786803 user: mistachkin tags: trunk, release
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

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

71
72
73
74
75
76
77





78
79
80
81
82
83
84
...
102
103
104
105
106
107
108
















109
110
111
112
113
114
115
116



117

118


119
120
121
122
123
124
125
...
158
159
160
161
162
163
164


165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182











183
184
185
186
187
188
189
    {
        /// <summary>
        /// Object used to synchronize access to the static instance data
        /// for this class.
        /// </summary>
        private static object syncRoot = new object();






        /// <summary>
        /// Member variable to store the application log handler to call.
        /// </summary>
        private static event SQLiteLogEventHandler _handlers;

        /// <summary>
        /// The default log event handler.
................................................................................
        private static bool _enabled;

        /// <summary>
        /// Initializes the SQLite logging facilities.
        /// </summary>
        public static void Initialize()
        {
















            lock (syncRoot)
            {
                //
                // NOTE: Add an event handler for the DomainUnload event so
                //       that we can unhook our logging managed function
                //       pointer from the native SQLite code prior to it
                //       being invalidated.
                //



                AppDomain.CurrentDomain.DomainUnload +=

                    new EventHandler(DomainUnload);



                //
                // NOTE: Create an instance of the SQLite wrapper class.
                //
                if (_sql == null)
                    _sql = new SQLite3(SQLiteDateFormats.Default);

................................................................................
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void DomainUnload(
            object sender,
            EventArgs e
            )
        {


            //
            // BUGBUG: This will cause serious problems if other AppDomains
            //         have any open SQLite connections; however, there is
            //         currently no way around this limitation.
            //
            if (_sql != null)
            {
                int rc = _sql.Shutdown();

                if (rc != 0)
                    throw new SQLiteException(rc,
                        "Failed to shutdown interface.");

                rc = _sql.SetLogCallback(null);

                if (rc != 0)
                    throw new SQLiteException(rc,
                        "Failed to shutdown logging.");











            }
        }

        /// <summary>
        /// This event is raised whenever SQLite raises a logging event.
        /// Note that this should be set as one of the first things in the
        /// application.







>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








>
>
>
|
>
|
>
>







 







>
>
|
|
|
|
|
|
|
|

|
|
|

|

|
|
|
>
>
>
>
>
>
>
>
>
>
>







71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
...
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
    {
        /// <summary>
        /// Object used to synchronize access to the static instance data
        /// for this class.
        /// </summary>
        private static object syncRoot = new object();

        /// <summary>
        /// Member variable to store the AppDomain.DomainUnload event handler.
        /// </summary>
        private static EventHandler _domainUnload;

        /// <summary>
        /// Member variable to store the application log handler to call.
        /// </summary>
        private static event SQLiteLogEventHandler _handlers;

        /// <summary>
        /// The default log event handler.
................................................................................
        private static bool _enabled;

        /// <summary>
        /// Initializes the SQLite logging facilities.
        /// </summary>
        public static void Initialize()
        {
            //
            // BUGFIX: To avoid nasty situations where multiple AppDomains are
            //         attempting to initialize and/or shutdown what is really
            //         a shared native resource (i.e. the SQLite core library
            //         is loaded per-process and has only one logging callback,
            //         not one per-AppDomain, which it knows nothing about),
            //         prevent all non-default AppDomains from registering a
            //         log handler unless the "Force_SQLiteLog" environment
            //         variable is used to manually override this safety check.
            //
            if (!AppDomain.CurrentDomain.IsDefaultAppDomain() &&
                Environment.GetEnvironmentVariable("Force_SQLiteLog") == null)
            {
                return;
            }

            lock (syncRoot)
            {
                //
                // NOTE: Add an event handler for the DomainUnload event so
                //       that we can unhook our logging managed function
                //       pointer from the native SQLite code prior to it
                //       being invalidated.
                //
                // BUGFIX: Make sure this event handler is only added one
                //         time (per-AppDomain).
                //
                if (_domainUnload == null)
                {
                    _domainUnload = new EventHandler(DomainUnload);
                    AppDomain.CurrentDomain.DomainUnload += _domainUnload;
                }

                //
                // NOTE: Create an instance of the SQLite wrapper class.
                //
                if (_sql == null)
                    _sql = new SQLite3(SQLiteDateFormats.Default);

................................................................................
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void DomainUnload(
            object sender,
            EventArgs e
            )
        {
            lock (syncRoot)
            {
                //
                // BUGBUG: This will cause serious problems if other AppDomains
                //         have any open SQLite connections; however, there is
                //         currently no way around this limitation.
                //
                if (_sql != null)
                {
                    int rc = _sql.Shutdown();

                    if (rc != 0)
                        throw new SQLiteException(rc,
                            "Failed to shutdown interface.");

                    rc = _sql.SetLogCallback(null);

                    if (rc != 0)
                        throw new SQLiteException(rc,
                            "Failed to shutdown logging.");
                }

                //
                // NOTE: Remove the event handler for the DomainUnload event
                //       that we added earlier.
                //
                if (_domainUnload != null)
                {
                    AppDomain.CurrentDomain.DomainUnload -= _domainUnload;
                    _domainUnload = null;
                }
            }
        }

        /// <summary>
        /// This event is raised whenever SQLite raises a logging event.
        /// Note that this should be set as one of the first things in the
        /// application.

Changes to Tests/common.eagle.

134
135
136
137
138
139
140












141
142
143
144
145
146
147
      return [info binary]
    }
 
    proc getBinaryFileName { fileName } {
      return [file nativename \
          [file join [getBinaryDirectory] [file tail $fileName]]]
    }












 
    proc tryCopyBuildFile { fileName } {
      #
      # NOTE: If we cannot copy the assembly then it is probably already loaded.
      #
      set sourceFileName [getBuildFileName $fileName]








>
>
>
>
>
>
>
>
>
>
>
>







134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
      return [info binary]
    }
 
    proc getBinaryFileName { fileName } {
      return [file nativename \
          [file join [getBinaryDirectory] [file tail $fileName]]]
    }
 
    proc getAppDomainPreamble { {prefix ""} {suffix ""} } {
      return [uplevel 1 [list subst [appendArgs $prefix {
        if {[hasRuntimeOption native]} then {
          object invoke Interpreter.GetActive AddRuntimeOption native
        }

        set ::path {$::path}
        set ::test_year {[getBuildYear]}
        set ::test_configuration {[getBuildConfiguration]}
      } $suffix]]]
    }
 
    proc tryCopyBuildFile { fileName } {
      #
      # NOTE: If we cannot copy the assembly then it is probably already loaded.
      #
      set sourceFileName [getBuildFileName $fileName]

Added Tests/tkt-0d5b1ef362.eagle.













































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
###############################################################################
#
# tkt-0d5b1ef362.eagle --
#
# Written by Joe Mistachkin.
# Released to the public domain, use at your own risk!
#
###############################################################################

package require Eagle
package require EagleLibrary
package require EagleTest

runTestPrologue

###############################################################################

package require System.Data.SQLite.Test
runSQLiteTestPrologue

###############################################################################

#
# HACK: This test reads the private "_domainUnload" field of the AppDomain
#       class.  This is non-portable and will not work on Mono.  Also, this
#       may not work on versions of the .NET Framework after 4.0.
#
runTest {test tkt-0d5b1ef362-1.1 {SQLiteLog.DomainUnload event} -setup \
    [getAppDomainPreamble] -body {
  #
  # NOTE: Grab the number of DomainUnload handlers prior to doing anything
  #       else.
  #
  set x [object invoke -flags +NonPublic \
      AppDomain.CurrentDomain._domainUnload.GetInvocationList Length]

  package require EagleLibrary
  package require EagleTest
  package require System.Data.SQLite.Test

  object load -loadtype File [file join [getBinaryDirectory] \
      System.Data.SQLite.dll]

  for {set i 1} {$i < 3} {incr i} {
    set connection($i) [object create System.Data.SQLite.SQLiteConnection ""]
  }

  #
  # NOTE: Now, grab the number of DomainUnload handlers after creating the
  #       connections.
  #
  set y [object invoke -flags +NonPublic \
      AppDomain.CurrentDomain._domainUnload.GetInvocationList Length]

  #
  # NOTE: Make sure that no DomainUnload handlers were actually added while we
  #       created the two connections (i.e. because we are not in the default
  #       application domain).
  #
  expr {$x == $y}
} -cleanup {
  unset -nocomplain connection i x y
} -constraints {eagle dotNet monoBug28 command.sql compile.DATA\
compile.ISOLATED_INTERPRETERS System.Data.SQLite} -isolationLevel AppDomain \
-result {True}}

###############################################################################

runSQLiteTestEpilogue
runTestEpilogue

Added Tests/tkt-ac47dd230a.eagle.

































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
###############################################################################
#
# tkt-ac47dd230a.eagle --
#
# Written by Joe Mistachkin.
# Released to the public domain, use at your own risk!
#
###############################################################################

package require Eagle
package require EagleLibrary
package require EagleTest

runTestPrologue

###############################################################################

package require System.Data.SQLite.Test
runSQLiteTestPrologue

###############################################################################

runTest {test tkt-ac47dd230a-1.1 {multiple AppDomains} -setup {
  for {set i 1} {$i < 3} {incr i} {
    set appDomain($i) [object invoke AppDomain CreateDomain \
        tkt-ac47dd230a-1.1.$i]

    set result null
    set interpreterHelper($i) [object invoke -alias InterpreterHelper \
        Create $appDomain($i) null Default null null null result]

    if {[string length $interpreterHelper($i)] == 0} then {
      error [object invoke $result ToString]
    }

    set interpreter($i) [$interpreterHelper($i) -alias Interpreter]

    set result null
    set code [$interpreter($i) EvaluateScript [getAppDomainPreamble] result]

    if {$code ne "Ok"} then {
      error [object invoke $result ToString]
    }
  }
} -body {
  set results [list]

  for {set i 1} {$i < 3} {incr i} {
    set result null
    set code [$interpreter($i) EvaluateScript {
      package require EagleLibrary
      package require EagleTest
      package require System.Data.SQLite.Test

      object load -loadtype File [file join [getBinaryDirectory] \
          System.Data.SQLite.dll]

      setupDb tkt-ac47dd230a-1.1; cleanupDb tkt-ac47dd230a-1.1
    } result]

    lappend results $code [expr {[string length $result] > 0 ? \
        [object invoke $result ToString] : ""}]
  }

  set results
} -cleanup {
  unset -nocomplain results code result interpreter interpreterHelper

  for {set i 1} {$i < 3} {incr i} {
    object invoke AppDomain Unload $appDomain($i)
  }

  unset -nocomplain appDomain i
} -constraints {eagle monoBug28 command.sql compile.DATA\
compile.ISOLATED_INTERPRETERS System.Data.SQLite} -result {Ok 0 Ok 0}}

###############################################################################

runSQLiteTestEpilogue
runTestEpilogue

Changes to Tests/tkt-b4a7ddc83f.eagle.

26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#
object invoke -flags +NonPublic System.Data.SQLite.UnsafeNativeMethods \
    sqlite3_shutdown

###############################################################################

for {set i 1} {$i < 3} {incr i} {
  runTest {test tkt-b4a7ddc83f-1.$i {logging shutdown} -setup [subst {

    set appDomainId(1) {[object invoke AppDomain.CurrentDomain Id]}
    set fileName {tkt-b4a7ddc83f-1.$i.db}

    if {[hasRuntimeOption native]} then {
      object invoke Interpreter.GetActive AddRuntimeOption native
    }

    set ::path {$::path}
    set ::test_year {[getBuildYear]}
    set ::test_configuration {[getBuildConfiguration]}
  }] -body {
    set appDomainId(2) [object invoke AppDomain.CurrentDomain Id]

    package require EagleLibrary
    package require EagleTest
    package require System.Data.SQLite.Test








|
>


<
<
<
<
<
<
<
<







26
27
28
29
30
31
32
33
34
35
36








37
38
39
40
41
42
43
#
object invoke -flags +NonPublic System.Data.SQLite.UnsafeNativeMethods \
    sqlite3_shutdown

###############################################################################

for {set i 1} {$i < 3} {incr i} {
  runTest {test tkt-b4a7ddc83f-1.$i {logging shutdown} -setup \
      [getAppDomainPreamble {
    set appDomainId(1) {[object invoke AppDomain.CurrentDomain Id]}
    set fileName {tkt-b4a7ddc83f-1.$i.db}








  }] -body {
    set appDomainId(2) [object invoke AppDomain.CurrentDomain Id]

    package require EagleLibrary
    package require EagleTest
    package require System.Data.SQLite.Test

Changes to test/TestCases.cs.

1619
1620
1621
1622
1623
1624
1625
1626

1627
1628
1629
1630
1631
1632
1633
            cnn.Open();

            logevents = 0;

            cnn.LogMessage(1, "test log event");

            if (logevents != 1)
                throw new Exception("Log event count incorrect.");


            cnn.Close();

            // remove the log handler before the connection is closed.
            sqlite_fact.Log -= logHandler;

        }







|
>







1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
            cnn.Open();

            logevents = 0;

            cnn.LogMessage(1, "test log event");

            if (logevents != 1)
                throw new Exception(String.Format(
                    "Log event count {0} incorrect.", logevents));

            cnn.Close();

            // remove the log handler before the connection is closed.
            sqlite_fact.Log -= logHandler;

        }

Changes to www/test.wiki.

107
108
109
110
111
112
113
114
115
116
117
118
119
  </li>

  <li>
    Make sure all tests pass; the log file
    &quot;%TEMP%\EagleShell.exe.test.&lt;pid&gt;.log&quot; may be checked if any
    errors should occur.  EagleTest should produce &quot;success&quot; messages
    very similar to the following:<br /><br />
    PASSED: 46<br />
    TOTAL: 46<br />
    PASS PERCENTAGE: 100%<br />
    OVERALL RESULT: SUCCESS<br />
  </li>
</ol>







|
|




107
108
109
110
111
112
113
114
115
116
117
118
119
  </li>

  <li>
    Make sure all tests pass; the log file
    &quot;%TEMP%\EagleShell.exe.test.&lt;pid&gt;.log&quot; may be checked if any
    errors should occur.  EagleTest should produce &quot;success&quot; messages
    very similar to the following:<br /><br />
    PASSED: 48<br />
    TOTAL: 48<br />
    PASS PERCENTAGE: 100%<br />
    OVERALL RESULT: SUCCESS<br />
  </li>
</ol>