@@ -25,89 +25,71 @@ limitations under the License.
2525using System . Runtime . CompilerServices ;
2626using System . Threading ;
2727using System . Threading . Tasks ;
28+ using System . Reactive . Subjects ;
2829
2930namespace SQLitePCL . pretty
3031{
31- /// <summary>
32- /// Extensions methods for <see cref="IDatabaseConnection"/>
33- /// </summary>
34- public static partial class DatabaseConnection
32+ internal class WriteLockedRef < T >
3533 {
36- private const string AsyncDatabaseConnectionKey = "AsyncDatabaseConnection" ;
34+ private readonly object gate = new object ( ) ;
35+ private T value ;
3736
38- // FIXME: I picked this number fairly randomly. It would be good to do some experimentation
39- // to determine if its a good default. The goal should be supporting cancelling of queries that are
40- // actually blocking use of the database for a measurable period of time.
41- private static readonly int defaultInterruptInstructionCount = 100 ;
42-
43- private static volatile IScheduler defaultScheduler = TaskPoolScheduler . Default ;
37+ public WriteLockedRef ( T defaultValue )
38+ {
39+ this . value = defaultValue ;
40+ }
4441
45- /// <summary>
46- /// Allows an application to set a default scheduler for <see cref="IAsyncDatabaseConnection"/>
47- /// instances created with <see cref="DatabaseConnection.AsAsyncDatabaseConnection(SQLiteDatabaseConnection)"/>.
48- /// </summary>
49- /// <remarks>This is a convenience feature that allows an application to set a global
50- /// <see cref="IScheduler"/> instance, instead of supplying it with each call to
51- /// <see cref="DatabaseConnection.AsAsyncDatabaseConnection(SQLiteDatabaseConnection)"/>.
52- /// </remarks>
53- /// <threadsafety static="false">This setter sets global state and should not be
54- /// used after application initialization.</threadsafety>
55- public static IScheduler DefaultScheduler
42+ public T Value
5643 {
57- set
44+ get { return value ; }
45+
46+ set
5847 {
59- Contract . Requires ( value != null ) ;
60- defaultScheduler = value ;
48+ lock ( gate )
49+ {
50+ this . value = value ;
51+ }
6152 }
6253 }
63-
64- internal static IAsyncDatabaseConnection AsAsyncDatabaseConnection ( this SQLiteDatabaseConnection This , IScheduler scheduler , int interruptInstructionCount )
54+
55+ }
56+
57+ /// <summary>
58+ /// SQLiteDatabaseConnectionBuilder extension functions.
59+ /// </summary>
60+ public static class SQLiteDatabaseConnectionBuilderExtensions
61+ {
62+ /// <summary>
63+ /// Builds an IAsyncDatabaseConnection using the specified scheduler.
64+ /// </summary>
65+ /// <returns>An IAsyncDatabaseConnection using the specified scheduler.</returns>
66+ /// <param name="This">A SQLiteDatabaseConnectionBuilder instance.</param>
67+ /// <param name="scheduler">An RX scheduler</param>
68+ public static IAsyncDatabaseConnection BuildAsyncDatabaseConnection (
69+ this SQLiteDatabaseConnectionBuilder This ,
70+ IScheduler scheduler )
6571 {
6672 Contract . Requires ( This != null ) ;
6773 Contract . Requires ( scheduler != null ) ;
6874
69- IAsyncDatabaseConnection target ;
70- if ( DatabaseConnectionExpando . Instance . GetOrAddValue ( This , AsyncDatabaseConnectionKey , _ =>
71- {
72- // Store a WeakReference to the async connection so that we don't end up with a circular reference that prevents the
73- // SQLiteDatabaseConnection from being freed.
74- var asyncConnection = new AsyncDatabaseConnectionImpl ( This , scheduler , interruptInstructionCount ) ;
75- return new WeakReference < IAsyncDatabaseConnection > ( asyncConnection ) ;
76- } ) . TryGetTarget ( out target ) )
77- {
78- return target ;
79- }
75+ var builder = This . Clone ( ) ;
8076
81- // This can't really happen.
82- throw new InvalidOperationException ( ) ;
83- }
77+ var progressHandlerResult = new WriteLockedRef < bool > ( false ) ;
78+ builder . ProgressHandler = ( ) => progressHandlerResult . Value ;
79+ var db = This . Build ( ) ;
8480
85- /// <summary>
86- /// Returns an <see cref="IAsyncDatabaseConnection"/> instance that delegates database requests
87- /// to the provided <see cref="IDatabaseConnection"/>.
88- /// </summary>
89- /// <remarks>Note, once this method is called the provided <see cref="IDatabaseConnection"/>
90- /// is owned by the returned <see cref="IAsyncDatabaseConnection"/>, and may no longer be
91- /// safely used directly.</remarks>
92- /// <param name="This">The database connection.</param>
93- /// <param name="scheduler">A scheduler used to schedule asynchronous database use on.</param>
94- /// <returns>An <see cref="IAsyncDatabaseConnection"/> instance.</returns>
95- public static IAsyncDatabaseConnection AsAsyncDatabaseConnection ( this SQLiteDatabaseConnection This , IScheduler scheduler ) =>
96- AsAsyncDatabaseConnection ( This , scheduler , defaultInterruptInstructionCount ) ;
81+ return new AsyncDatabaseConnectionImpl ( db , scheduler , progressHandlerResult ) ;
82+ }
9783
9884 /// <summary>
99- /// Returns an <see cref="IAsyncDatabaseConnection"/> instance that delegates database requests
100- /// to the provided <see cref="IDatabaseConnection"/>.
85+ /// Builds an IAsyncDatabaseConnection using the default TaskPool scheduler.
10186 /// </summary>
102- /// <remarks>Note, once this method is called the provided <see cref="IDatabaseConnection"/>
103- /// is owned by the returned <see cref="IAsyncDatabaseConnection"/>, and may no longer be
104- /// safely used directly.</remarks>
105- /// <param name="This">The database connection.</param>
106- /// <returns>An <see cref="IAsyncDatabaseConnection"/> instance.</returns>
107- public static IAsyncDatabaseConnection AsAsyncDatabaseConnection ( this SQLiteDatabaseConnection This ) =>
108- AsAsyncDatabaseConnection ( This , defaultScheduler ) ;
87+ /// <returns>An IAsyncDatabaseConnection using the default TaskPool scheduler.</returns>
88+ /// <param name="This">A SQLiteDatabaseConnectionBuilder instance.</param>
89+ public static IAsyncDatabaseConnection BuildAsyncDatabaseConnection ( this SQLiteDatabaseConnectionBuilder This ) =>
90+ This . BuildAsyncDatabaseConnection ( TaskPoolScheduler . Default ) ;
10991 }
110-
92+
11193 /// <summary>
11294 /// Extensions methods for <see cref="IAsyncDatabaseConnection"/>.
11395 /// </summary>
@@ -491,19 +473,28 @@ internal sealed class AsyncDatabaseConnectionImpl : IAsyncDatabaseConnection
491473 {
492474 private readonly OperationsQueue queue = new OperationsQueue ( ) ;
493475 private readonly IScheduler scheduler ;
494- private readonly int interruptInstructionCount ;
495-
496476 private readonly SQLiteDatabaseConnection conn ;
477+ private readonly WriteLockedRef < bool > progressHandlerResult ;
497478
498479 private bool disposed = false ;
499480
500- internal AsyncDatabaseConnectionImpl ( SQLiteDatabaseConnection conn , IScheduler scheduler , int interruptInstructionCount )
481+ internal AsyncDatabaseConnectionImpl ( SQLiteDatabaseConnection conn , IScheduler scheduler , WriteLockedRef < bool > progressHandlerResult )
501482 {
502483 this . conn = conn ;
503484 this . scheduler = scheduler ;
504- this . interruptInstructionCount = interruptInstructionCount ;
485+ this . progressHandlerResult = progressHandlerResult ;
486+
487+ this . Trace = Observable . FromEventPattern < DatabaseTraceEventArgs > ( conn , "Trace" ) . Select ( e => e . EventArgs ) ;
488+ this . Profile = Observable . FromEventPattern < DatabaseProfileEventArgs > ( conn , "Profile" ) . Select ( e => e . EventArgs ) ;
489+ this . Update = Observable . FromEventPattern < DatabaseUpdateEventArgs > ( conn , "Update" ) . Select ( e => e . EventArgs ) ;
505490 }
506491
492+ public IObservable < DatabaseTraceEventArgs > Trace { get ; }
493+
494+ public IObservable < DatabaseProfileEventArgs > Profile { get ; }
495+
496+ public IObservable < DatabaseUpdateEventArgs > Update { get ; }
497+
507498 public async Task DisposeAsync ( )
508499 {
509500 if ( disposed )
@@ -576,7 +567,9 @@ public IObservable<T> Use<T>(Func<IDatabaseConnection, CancellationToken, IEnume
576567
577568 return queue . EnqueueOperation ( ct =>
578569 {
579- this . conn . RegisterProgressHandler ( interruptInstructionCount , ( ) => ct . IsCancellationRequested ) ;
570+ this . progressHandlerResult . Value = false ;
571+ var ctSubscription = ct . Register ( ( ) => this . progressHandlerResult . Value = true ) ;
572+
580573 try
581574 {
582575 ct . ThrowIfCancellationRequested ( ) ;
@@ -597,7 +590,8 @@ public IObservable<T> Use<T>(Func<IDatabaseConnection, CancellationToken, IEnume
597590 }
598591 finally
599592 {
600- this . conn . RemoveProgressHandler ( ) ;
593+ ctSubscription . Dispose ( ) ;
594+ this . progressHandlerResult . Value = false ;
601595 }
602596 } , scheduler , cancellationToken ) ;
603597 } ) ;
0 commit comments