From b0a8a7fceaf0d926506a3d4c700d5dda43a0932c Mon Sep 17 00:00:00 2001 From: Nathan Westfall Date: Thu, 31 May 2018 16:00:58 -0400 Subject: [PATCH 1/8] check the current count of the semaphore in "AsyncLock" to verify it should be released --- src/Akavache.Sqlite3/AsyncLock.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Akavache.Sqlite3/AsyncLock.cs b/src/Akavache.Sqlite3/AsyncLock.cs index 57751a2e1..0ad21b972 100644 --- a/src/Akavache.Sqlite3/AsyncLock.cs +++ b/src/Akavache.Sqlite3/AsyncLock.cs @@ -7,7 +7,8 @@ namespace Akavache.Sqlite3.Internal // Straight-up thieved from http://www.hanselman.com/blog/ComparingTwoTechniquesInNETAsynchronousCoordinationPrimitives.aspx public sealed class AsyncLock { - readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); + const int semaphoreMaxCount = 1; + readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, semaphoreMaxCount); readonly Task m_releaser; public AsyncLock() @@ -33,7 +34,11 @@ sealed class Releaser : IDisposable { readonly AsyncLock m_toRelease; internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; } - public void Dispose() { m_toRelease.m_semaphore.Release(); } + public void Dispose() + { + if(m_toRelease.m_semaphore.CurrentCount != semaphoreMaxCount) + m_toRelease.m_semaphore.Release(); + } } } } \ No newline at end of file From 31d7166ec1c7b2128ec53a34601e7f4e913b2ae3 Mon Sep 17 00:00:00 2001 From: Nathan Westfall Date: Thu, 31 May 2018 16:08:04 -0400 Subject: [PATCH 2/8] formatting fix --- src/Akavache.Sqlite3/AsyncLock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Akavache.Sqlite3/AsyncLock.cs b/src/Akavache.Sqlite3/AsyncLock.cs index 0ad21b972..0123ecdde 100644 --- a/src/Akavache.Sqlite3/AsyncLock.cs +++ b/src/Akavache.Sqlite3/AsyncLock.cs @@ -36,7 +36,7 @@ sealed class Releaser : IDisposable internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; } public void Dispose() { - if(m_toRelease.m_semaphore.CurrentCount != semaphoreMaxCount) + if (m_toRelease.m_semaphore.CurrentCount != semaphoreMaxCount) m_toRelease.m_semaphore.Release(); } } From 329327c8e36f61c141da818f17ac0e6d0f617dba Mon Sep 17 00:00:00 2001 From: Nathan Westfall Date: Thu, 31 May 2018 21:39:59 -0400 Subject: [PATCH 3/8] instead of checking semaphore count, use Disposable.Create --- src/Akavache.Sqlite3/AsyncLock.cs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/Akavache.Sqlite3/AsyncLock.cs b/src/Akavache.Sqlite3/AsyncLock.cs index 0123ecdde..79f7d0535 100644 --- a/src/Akavache.Sqlite3/AsyncLock.cs +++ b/src/Akavache.Sqlite3/AsyncLock.cs @@ -1,19 +1,19 @@ using System; using System.Threading; using System.Threading.Tasks; +using System.Reactive.Disposables; namespace Akavache.Sqlite3.Internal { // Straight-up thieved from http://www.hanselman.com/blog/ComparingTwoTechniquesInNETAsynchronousCoordinationPrimitives.aspx public sealed class AsyncLock { - const int semaphoreMaxCount = 1; - readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, semaphoreMaxCount); + readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); readonly Task m_releaser; public AsyncLock() { - m_releaser = Task.FromResult((IDisposable)new Releaser(this)); + m_releaser = Task.FromResult(Disposable.Create(() => m_semaphore.Release())); } public Task LockAsync(CancellationToken ct = default(CancellationToken)) @@ -29,16 +29,5 @@ public AsyncLock() m_releaser.Result, ct, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } - - sealed class Releaser : IDisposable - { - readonly AsyncLock m_toRelease; - internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; } - public void Dispose() - { - if (m_toRelease.m_semaphore.CurrentCount != semaphoreMaxCount) - m_toRelease.m_semaphore.Release(); - } - } } } \ No newline at end of file From 18f111d83144e916bc774dc6b60c6ce8d23db990 Mon Sep 17 00:00:00 2001 From: Nathan Westfall Date: Wed, 6 Jun 2018 14:06:05 -0400 Subject: [PATCH 4/8] async lock to now handle if task was cancelled, catch operation cancelled exceptions elsewhere to support this --- src/Akavache.Sqlite3/AsyncLock.cs | 15 +++++++++++---- src/Akavache.Sqlite3/OperationQueue.cs | 18 +++++++++++++++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/Akavache.Sqlite3/AsyncLock.cs b/src/Akavache.Sqlite3/AsyncLock.cs index 79f7d0535..daa08210f 100644 --- a/src/Akavache.Sqlite3/AsyncLock.cs +++ b/src/Akavache.Sqlite3/AsyncLock.cs @@ -13,7 +13,7 @@ public sealed class AsyncLock public AsyncLock() { - m_releaser = Task.FromResult(Disposable.Create(() => m_semaphore.Release())); + m_releaser = Task.FromResult((IDisposable)new Releaser(this)); } public Task LockAsync(CancellationToken ct = default(CancellationToken)) @@ -21,13 +21,20 @@ public AsyncLock() var wait = m_semaphore.WaitAsync(ct); // Happy path. We synchronously acquired the lock. - if (wait.IsCompleted && !wait.IsFaulted) + if (wait.IsCompleted && !wait.IsFaulted && !wait.IsCanceled) return m_releaser; - + return wait - .ContinueWith((_, state) => (IDisposable)state, + .ContinueWith((_, state) => (_.IsCanceled) ? null : (IDisposable)state, m_releaser.Result, ct, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } + + sealed class Releaser : IDisposable + { + readonly AsyncLock m_toRelease; + internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; } + public void Dispose() { m_toRelease.m_semaphore.Release(); } + } } } \ No newline at end of file diff --git a/src/Akavache.Sqlite3/OperationQueue.cs b/src/Akavache.Sqlite3/OperationQueue.cs index 5d68839aa..2d91f5fab 100644 --- a/src/Akavache.Sqlite3/OperationQueue.cs +++ b/src/Akavache.Sqlite3/OperationQueue.cs @@ -89,6 +89,10 @@ public IDisposable Start() break; } + //Verify lock was acquired + if(@lock == null) + break; + using (@lock) { // NB: We special-case the first item because we want to @@ -141,10 +145,14 @@ public IDisposable Start() } catch (OperationCanceledException) { } - using (flushLock.LockAsync().Result) + try { - FlushInternal(); + using(flushLock.LockAsync().Result) + { + FlushInternal(); + } } + catch(OperationCanceledException) { } start = null; })); @@ -240,7 +248,11 @@ public AsyncSubject Vacuum() var lockTask = flushLock.LockAsync(shouldQuit.Token); operationQueue.Add(OperationQueueItem.CreateUnit(OperationType.DoNothing)); - @lock = await lockTask; + try + { + @lock = await lockTask; + } + catch(OperationCanceledException) { } var deleteOp = OperationQueueItem.CreateUnit(OperationType.DeleteExpiredSqliteOperation); operationQueue.Add(deleteOp); From 4b455c9a9009f8ec97c4ef678541c68118009f64 Mon Sep 17 00:00:00 2001 From: Nathan Westfall Date: Wed, 29 Aug 2018 12:18:12 -0400 Subject: [PATCH 5/8] fixed arguement name and added spacing --- src/Akavache.Sqlite3/AsyncLock.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Akavache.Sqlite3/AsyncLock.cs b/src/Akavache.Sqlite3/AsyncLock.cs index daa08210f..cb75209cc 100644 --- a/src/Akavache.Sqlite3/AsyncLock.cs +++ b/src/Akavache.Sqlite3/AsyncLock.cs @@ -25,16 +25,16 @@ public AsyncLock() return m_releaser; return wait - .ContinueWith((_, state) => (_.IsCanceled) ? null : (IDisposable)state, + .ContinueWith((task, state) => (task.IsCanceled) ? null : (IDisposable)state, m_releaser.Result, ct, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } - sealed class Releaser : IDisposable - { - readonly AsyncLock m_toRelease; - internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; } - public void Dispose() { m_toRelease.m_semaphore.Release(); } + sealed class Releaser : IDisposable + { + readonly AsyncLock m_toRelease; + internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; } + public void Dispose() { m_toRelease.m_semaphore.Release(); } } } } \ No newline at end of file From 1cc3bab5206ed68c5cee3bbddbf9801aa5bdb754 Mon Sep 17 00:00:00 2001 From: Nathan Westfall Date: Wed, 26 Sep 2018 09:37:17 -0400 Subject: [PATCH 6/8] removed brackets --- src/Akavache.Sqlite3/AsyncLock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Akavache.Sqlite3/AsyncLock.cs b/src/Akavache.Sqlite3/AsyncLock.cs index cb75209cc..949775cee 100644 --- a/src/Akavache.Sqlite3/AsyncLock.cs +++ b/src/Akavache.Sqlite3/AsyncLock.cs @@ -25,7 +25,7 @@ public AsyncLock() return m_releaser; return wait - .ContinueWith((task, state) => (task.IsCanceled) ? null : (IDisposable)state, + .ContinueWith((task, state) => task.IsCanceled ? null : (IDisposable)state, m_releaser.Result, ct, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } From cbdfa80512d7cb6483be39d8996f03a022144103 Mon Sep 17 00:00:00 2001 From: Nathan Westfall Date: Wed, 26 Sep 2018 16:15:13 -0400 Subject: [PATCH 7/8] configureawait(false) before getting result --- src/Akavache.Sqlite3/OperationQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Akavache.Sqlite3/OperationQueue.cs b/src/Akavache.Sqlite3/OperationQueue.cs index 2d91f5fab..1eaa02185 100644 --- a/src/Akavache.Sqlite3/OperationQueue.cs +++ b/src/Akavache.Sqlite3/OperationQueue.cs @@ -147,7 +147,7 @@ public IDisposable Start() try { - using(flushLock.LockAsync().Result) + using(flushLock.LockAsync().ConfigureAwait(false).GetAwaiter().GetResult()) { FlushInternal(); } From d6c95d6743faae4e6d5b7b8231e36db48d3b71ad Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Sun, 30 Sep 2018 23:35:31 -0600 Subject: [PATCH 8/8] Set back to just using Result --- src/Akavache.Sqlite3/OperationQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Akavache.Sqlite3/OperationQueue.cs b/src/Akavache.Sqlite3/OperationQueue.cs index 1eaa02185..2d91f5fab 100644 --- a/src/Akavache.Sqlite3/OperationQueue.cs +++ b/src/Akavache.Sqlite3/OperationQueue.cs @@ -147,7 +147,7 @@ public IDisposable Start() try { - using(flushLock.LockAsync().ConfigureAwait(false).GetAwaiter().GetResult()) + using(flushLock.LockAsync().Result) { FlushInternal(); }