Beispiel #1
0
        public AsyncSubject <Unit> Vacuum()
        {
            // Vacuum is a special snowflake. We want to delete all the expired rows before
            // actually vacuuming. Unfortunately vacuum can't be run in a transaction so we'll
            // claim an exclusive lock on the queue, drain it and run the delete first before
            // running our vacuum op without any transactions.
            var ret = new AsyncSubject <Unit>();

            Task.Run(async() =>
            {
                IDisposable @lock = null;
                try
                {
                    // NB. While the documentation for SemaphoreSlim (which powers AsyncLock)
                    // doesn't guarantee ordering the actual (current) implementation[1]
                    // uses a linked list to queue incoming requests so by adding ourselves
                    // to the queue first and then sending a no-op to the main queue to
                    // force it to finish up and release the lock we avoid any potential
                    // race condition where the main queue reclaims the lock before we
                    // have had a chance to acquire it.
                    //
                    // 1. http://referencesource.microsoft.com/#mscorlib/system/threading/SemaphoreSlim.cs,d57f52e0341a581f
                    var lockTask = flushLock.LockAsync(shouldQuit.Token);
                    operationQueue.Add(OperationQueueItem.CreateUnit(OperationType.DoNothing));

                    @lock = await lockTask;

                    var deleteOp = OperationQueueItem.CreateUnit(OperationType.DeleteExpiredSqliteOperation);
                    operationQueue.Add(deleteOp);

                    FlushInternal();

                    await deleteOp.CompletionAsUnit;

                    var vacuumOp = OperationQueueItem.CreateUnit(OperationType.VacuumSqliteOperation);

                    MarshalCompletion(vacuumOp.Completion, vacuum.PrepareToExecute(), Observable.Return(Unit.Default));

                    await vacuumOp.CompletionAsUnit;
                }
                finally
                {
                    if (@lock != null)
                    {
                        @lock.Dispose();
                    }
                }
            })
            .ToObservable()
            .ObserveOn(scheduler)
            .Multicast(ret)
            .PermaRef();

            return(ret);
        }
Beispiel #2
0
        public AsyncSubject <Unit> Vacuum()
        {
            // Vacuum is a special snowflake. We want to delete all the expired rows before
            // actually vacuuming. Unfortunately vacuum can't be run in a transaction so we'll
            // claim an exclusive lock on the queue, drain it and run the delete first before
            // running our vacuum op without any transactions.
            var ret = new AsyncSubject <Unit>();

            Task.Run(async() =>
            {
                // NB: We add a "DoNothing" operation so that the thread waiting
                // on an item will always have one instead of waiting the full timeout
                // before we can run the flush
                operationQueue.Add(OperationQueueItem.CreateUnit(OperationType.DoNothing));

                using (await flushLock.LockAsync())
                {
                    var deleteOp = OperationQueueItem.CreateUnit(OperationType.DeleteExpiredSqliteOperation);
                    operationQueue.Add(deleteOp);

                    FlushInternal();

                    await deleteOp.CompletionAsUnit;

                    var vacuumOp = OperationQueueItem.CreateUnit(OperationType.VacuumSqliteOperation);

                    MarshalCompletion(vacuumOp.Completion, vacuum.PrepareToExecute(), Observable.Return(Unit.Default));

                    await vacuumOp.CompletionAsUnit;
                }
            })
            .ToObservable()
            .ObserveOn(scheduler)
            .Multicast(ret)
            .PermaRef();

            return(ret);
        }
Beispiel #3
0
        void ProcessItems(List <OperationQueueItem> toProcess)
        {
            var commitResult = new AsyncSubject <Unit>();

            begin.PrepareToExecute()();

            foreach (var item in toProcess)
            {
                switch (item.OperationType)
                {
                case OperationType.DoNothing:
                    break;

                case OperationType.BulkInsertSqliteOperation:
                    MarshalCompletion(item.Completion, bulkInsertKey.PrepareToExecute(item.ParametersAsElements), commitResult);
                    break;

                case OperationType.BulkInvalidateByTypeSqliteOperation:
                    MarshalCompletion(item.Completion, bulkInvalidateType.PrepareToExecute(item.ParametersAsKeys), commitResult);
                    break;

                case OperationType.BulkInvalidateSqliteOperation:
                    MarshalCompletion(item.Completion, bulkInvalidateKey.PrepareToExecute(item.ParametersAsKeys), commitResult);
                    break;

                case OperationType.BulkSelectByTypeSqliteOperation:
                    MarshalCompletion(item.Completion, bulkSelectType.PrepareToExecute(item.ParametersAsKeys), commitResult);
                    break;

                case OperationType.BulkSelectSqliteOperation:
                    MarshalCompletion(item.Completion, bulkSelectKey.PrepareToExecute(item.ParametersAsKeys), commitResult);
                    break;

                case OperationType.GetKeysSqliteOperation:
                    MarshalCompletion(item.Completion, getAllKeys.PrepareToExecute(), commitResult);
                    break;

                case OperationType.InvalidateAllSqliteOperation:
                    MarshalCompletion(item.Completion, invalidateAll.PrepareToExecute(), commitResult);
                    break;

                case OperationType.VacuumSqliteOperation:
                    MarshalCompletion(item.Completion, vacuum.PrepareToExecute(), commitResult);
                    break;

                default:
                    throw new ArgumentException("Unknown operation");
                }
            }

            try
            {
                commit.PrepareToExecute()();

                // NB: We do this in a scheduled result to stop a deadlock in
                // First and friends
                scheduler.Schedule(() =>
                {
                    commitResult.OnNext(Unit.Default);
                    commitResult.OnCompleted();
                });
            }
            catch (Exception ex)
            {
                scheduler.Schedule(() => commitResult.OnError(ex));
            }
        }