Exemplo n.º 1
0
        private static async Task TestMutexConcurrencyAsync(AsyncMutex asyncMutex, AsyncMutex asyncMutex2)
        {
            // Concurrency test with the same AsyncMutex object.
            using var phase1 = new AutoResetEvent(false);
            using var phase2 = new AutoResetEvent(false);
            using var phase3 = new AutoResetEvent(false);
            // Acquire the Mutex with a background thread.
            var myTask = Task.Run(async() =>
            {
                using (await asyncMutex.LockAsync())
                {
                    // Phase 1: signal that the mutex has been acquired.
                    phase1.Set();

                    // Phase 2: wait for exclusion.
                    Assert.True(phase2.WaitOne(TimeSpan.FromSeconds(20)));
                }
                // Phase 3: release the mutex.
                phase3.Set();
            });

            // Phase 1: wait for the first Task to acquire the mutex.
            Assert.True(phase1.WaitOne(TimeSpan.FromSeconds(20)));

            // Phase 2: check mutual exclusion.
            using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
            {
                await Assert.ThrowsAsync <IOException>(async() =>
                {
                    using (await asyncMutex2.LockAsync(cancellationToken: cts.Token))
                    {
                        throw new InvalidOperationException("Mutex should not be acquired here.");
                    };
                });
            }
            phase2.Set();

            // Phase 3: wait for release and acquire the mutex
            Assert.True(phase3.WaitOne(TimeSpan.FromSeconds(20)));

            using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
            {
                // We should get this immediately.
                using (await asyncMutex2.LockAsync())
                {
                };
            }

            Assert.True(myTask.IsCompletedSuccessfully);
        }
Exemplo n.º 2
0
        public async Task LockAsync()
        {
            var mutex = new AsyncMutex();
            await mutex.LockAsync();

            mutex.Unlock();
        }
Exemplo n.º 3
0
        public void CancelStressTest()
        {
            foreach (bool sync in new[] { false, true })
            {
                const int N       = 64;
                long      counter = 0;
                var       mutex   = new AsyncMutex();
                Task[]    tasks   = Enumerable.Range(0, N).Select(_ => Inc()).ToArray();
                Task.WaitAll(tasks);
                Assert.AreEqual(N, counter);

                async Task Inc()
                {
                    while (true)
                    {
                        using (var cancel = new CancellationTokenSource()) {
                            Task t = mutex.LockAsync(cancel.Token);
                            await Task.Yield();

                            cancel.Cancel();
                            if (!t.IsCanceled)
                            {
                                await t;
                                break;
                            }
                        }
                    }
                    long next = counter + 1;
                    await Task.Yield();

                    counter = next;
                    mutex.Unlock(runNextSynchronously: sync);
                }
            }
        }
Exemplo n.º 4
0
 public void FairnessTest()
 {
     foreach (bool sync in new[] { false, true })
     {
         var mutex = new AsyncMutex();
         var tasks = new Queue <Task>(Enumerable.Range(0, 1024).Select(_ => mutex.LockAsync()));
         while (tasks.Count > 0)
         {
             tasks.Enqueue(mutex.LockAsync());
             for (int i = 0; i != 2; ++i)
             {
                 tasks.Dequeue().Wait();
                 mutex.Unlock(sync);
             }
         }
     }
 }
Exemplo n.º 5
0
        public async Task LockAndUnlockAsync()
        {
            var mutex = new AsyncMutex();

            using (await mutex.LockAndUnlockAsync())
            {
                Assert.False(await mutex.LockAsync(0));
            }
        }
Exemplo n.º 6
0
        public void LockCancelBenchmark()
        {
            Console.WriteLine("Warmup:");
            Benchmark(32).Wait();

            Console.WriteLine("\nThe real thing:");
            foreach (int threads in new[] { 1, 2, 4, 32, 1024 })
            {
                Benchmark(threads).Wait();
            }

            async Task Benchmark(int threads)
            {
                long counter = 0;
                var  mutex   = new AsyncMutex();
                await mutex.LockAsync();

                TimeSpan  duration  = TimeSpan.FromSeconds(1);
                Stopwatch stopwatch = Stopwatch.StartNew();
                await Task.WhenAll(Enumerable.Range(0, threads).Select(_ => Run()));

                double ns = 1e9 * stopwatch.Elapsed.TotalSeconds / counter;

                Console.WriteLine("  Benchmark(threads: {0,5:N0}): {1,7:N1} ns/call", threads, ns);

                async Task Run()
                {
                    await Task.Yield();

                    while (stopwatch.Elapsed < duration)
                    {
                        for (int i = 0; i != 64; ++i)
                        {
                            using (var c = new CancellationTokenSource()) {
                                Task t = mutex.LockAsync(c.Token);
                                c.Cancel();
                                Debug.Assert(t.IsCanceled);
                            }
                            ++counter;
                        }
                    }
                }
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Tries to retrieve a valid instance from the cache, and uses the provided factory if an existing item is not found
        /// </summary>
        /// <param name="factory">A <see cref="Func{TResult}"/> used when the requested value is not present in the cache</param>
        public async Task <T> TryGetInstanceAsync(Func <T> factory)
        {
            using (await Mutex.LockAsync())
            {
                // Try to retrieve an valid instance from the cache
                foreach (WeakReference <T> value in Cache)
                {
                    if (value.TryGetTarget(out T instance) && instance.TryGetDispatcher(out CoreDispatcher dispatcher) && dispatcher.HasThreadAccess)
                    {
                        return(instance);
                    }
                }

                // Create a new instance when needed
                T fallback = factory();
                Cache.Add(new WeakReference <T>(fallback));
                return(fallback);
            }
        }
Exemplo n.º 8
0
        public void CancelTest()
        {
            var mutex = new AsyncMutex();

            using (var cancel = new CancellationTokenSource()) {
                mutex.LockAsync().Wait();
                Task t = mutex.LockAsync(cancel.Token);
                Assert.IsFalse(t.IsCompleted);
                mutex.Unlock(runNextSynchronously: true);
                t.Wait();

                t = mutex.LockAsync(cancel.Token);
                cancel.Cancel();
                Assert.AreEqual(TaskStatus.Canceled, t.Status);

                mutex.Unlock(runNextSynchronously: true);
                t = mutex.LockAsync(cancel.Token);
                Assert.AreEqual(TaskStatus.RanToCompletion, t.Status);
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Sends command using the hwi process.
        /// </summary>
        /// <param name="cancellationToken">Cancel the current operation.</param>
        /// <param name="command">Command to send in string format.</param>
        /// <param name="isMutexPriority">You can add priority to your command among hwi calls. For example if multiply wasabi instances are running both use the same hwi process with isMutexPriority you will more likely to get it.</param>
        /// <returns>JToken the answer recevied from hwi process.</returns>
        /// <exception cref="TimeoutException">Thrown when the process cannot be finished in time.</exception>
        /// <exception cref="IOException">Thrown when Mutex on hwi process could not be acquired.</exception>
        public static async Task <JToken> SendCommandAsync(string command, CancellationToken cancellationToken, bool isMutexPriority = false)
        {
            try
            {
                var rand  = Random.Next(1, 100);
                var delay = isMutexPriority ? (100 + rand) : (1000 + rand);
                using (await AsyncMutex.LockAsync(cancellationToken, delay))                 // It could be even improved more if this Mutex would also look at which hardware wallet the operation is going towards (enumerate sends to all.)
                {
                    if (!File.Exists(HwiPath))
                    {
                        var exeName = Path.GetFileName(HwiPath);
                        throw new FileNotFoundException($"{exeName} not found at `{HwiPath}`. Maybe it was removed by an antivirus software!");
                    }

                    using (var process = Process.Start(
                               new ProcessStartInfo
                    {
                        FileName = HwiPath,
                        Arguments = command,
                        RedirectStandardOutput = true,
                        UseShellExecute = false,
                        CreateNoWindow = true,
                        WindowStyle = ProcessWindowStyle.Hidden
                    }
                               ))
                    {
                        await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);                         // TODO: https://github.com/zkSNACKs/WalletWasabi/issues/1452;

                        if (process.ExitCode != 0)
                        {
                            throw new IOException($"Command: {command} exited with exit code: {process.ExitCode}, instead of 0.");
                        }

                        string response = await process.StandardOutput.ReadToEndAsync();

                        var jToken = JToken.Parse(response);
                        if (jToken is JObject json)
                        {
                            if (json.TryGetValue("error", out JToken err))
                            {
                                var errString = err.ToString();
                                throw new IOException(errString);
                            }
                        }

                        return(jToken);
                    }
                }
            }
            catch (Exception ex) when(ex is OperationCanceledException || ex is TaskCanceledException || ex is TimeoutException)
            {
                throw new TimeoutException("Timeout occurred during the hwi operation.", ex);
            }
        }
Exemplo n.º 10
0
        public async Task LockAsync_Timeout()
        {
            var mutex = new AsyncMutex();

            mutex.Lock();

            var start        = Environment.TickCount;
            var capturedLock = await mutex.LockAsync(100);

            Assert.False(capturedLock);
            Assert.True(Environment.TickCount - start >= 90);
        }
Exemplo n.º 11
0
        public void ExampleTest()
        {
            var expected = new[] {
                "Worker #0: 0",
                "Worker #1: 0",
                "Worker #1: 1",
                "Worker #1: 2",
                "Worker #0: 1",
                "Worker #0: 2",
            };

            CollectionAssert.AreEqual(expected, Example().Result);

            async Task <List <string> > Example()
            {
                var res   = new List <string>();
                var mutex = new AsyncMutex();
                await mutex.LockAsync();

                Task[] tasks = Enumerable.Range(0, 2).Select(Work).ToArray();
                mutex.Unlock(runNextSynchronously: true);
                await Task.WhenAll(tasks);

                return(res);

                async Task Work(int worker)
                {
                    for (int i = 0; i != 3; ++i)
                    {
                        await mutex.LockAsync();

                        res.Add($"Worker #{worker}: {i}");
                        mutex.Unlock(runNextSynchronously: true);
                    }
                }
            }
        }
Exemplo n.º 12
0
        public async Task LockAsync_CancellationToken()
        {
            var mutex = new AsyncMutex();

            mutex.Lock();

            var start = Environment.TickCount;

            using (var cancellationTokenSource = new CancellationTokenSource(100))
            {
                await Assert.ThrowsAsync <OperationCanceledException>(()
                                                                      => mutex.LockAsync(cancellationTokenSource.Token));

                Assert.True(Environment.TickCount - start >= 90);
            }
        }
        public async Task CheckAsync()
        {
            if (_disposedValue)
            {
                throw new ObjectDisposedException(nameof(SingleInstanceChecker));
            }

            // The disposal of this mutex handled by AsyncMutex.WaitForAllMutexToCloseAsync().
            var mutex = new AsyncMutex(_lockName);

            try
            {
                using CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(200));
                SingleApplicationLockHolder       = await mutex.LockAsync(cancellationToken : cts.Token).ConfigureAwait(false);
            }
            catch (IOException ex)
            {
                throw new InvalidOperationException($"Wasabi is already running on {Network}!", ex);
            }
        }
Exemplo n.º 14
0
        /// <summary>
        /// Loads a <see cref="CompositionBrush"/> instance with the target image from the shared <see cref="CanvasDevice"/> instance
        /// </summary>
        /// <param name="uri">The path to the image to load</param>
        /// <param name="dpiMode">Indicates the desired DPI mode to use when loading the image</param>
        /// <param name="cacheMode">Indicates the cache option to use to load the image</param>
        /// <returns>A <see cref="Task{T}"/> that returns the loaded <see cref="CompositionBrush"/> instance</returns>
        public static async Task <CompositionBrush> LoadImageAsync(Uri uri, DpiMode dpiMode, CacheMode cacheMode = CacheMode.Default)
        {
            var compositor = Window.Current.Compositor;

            // Lock and check the cache first
            using (await Win2DMutex.LockAsync())
            {
                uri = uri.ToAppxUri();

                if (cacheMode == CacheMode.Default &&
                    Cache.TryGetValue(compositor, uri, out var cached))
                {
                    return(cached);
                }

                // Load the image
                CompositionBrush brush;
                try
                {
                    // This will throw and the canvas will re-initialize the Win2D device if needed
                    var sharedDevice = CanvasDevice.GetSharedDevice();
                    brush = await LoadSurfaceBrushAsync(sharedDevice, compositor, uri, dpiMode);
                }
                catch
                {
                    // Device error
                    brush = null;
                }

                // Cache when needed and return the result
                if (brush != null &&
                    cacheMode != CacheMode.Disabled)
                {
                    Cache.AddOrUpdate(compositor, uri, brush);
                }

                return(brush);
            }
        }
Exemplo n.º 15
0
        public void LockUnlockStressTest()
        {
            foreach (bool sync in new[] { false, true })
            {
                const int N       = 64 << 10;
                long      counter = 0;
                var       mutex   = new AsyncMutex();
                Task.WhenAll(Enumerable.Range(0, N).Select(_ => Inc())).Wait();
                Assert.AreEqual(N, counter);

                async Task Inc()
                {
                    await mutex.LockAsync();

                    await Task.Yield();

                    long next = counter + 1;
                    await Task.Yield();

                    counter = next;
                    mutex.Unlock(runNextSynchronously: sync);
                }
            }
        }
Exemplo n.º 16
0
        public async Task AsyncMutexTestsAsync()
        {
            AsyncMutex asyncMutex = new AsyncMutex("mutex1");

            // Cannot be IDisposable because the pattern is like Nito's AsyncLock.
            Assert.False(asyncMutex is IDisposable);

            // Use the mutex two times after each other.
            using (await asyncMutex.LockAsync())
            {
                await Task.Delay(1);
            }

            using (await asyncMutex.LockAsync())
            {
                await Task.Delay(1);
            }

            // Release the Mutex from another thread.

            var disposable = await asyncMutex.LockAsync();

            var myThread = new Thread(new ThreadStart(() =>
            {
                disposable.Dispose();
            }));

            myThread.Start();
            myThread.Join();

            using (await asyncMutex.LockAsync())
            {
                await Task.Delay(1);
            }

            // Acquire the Mutex with a background thread.

            var myTask = Task.Run(async() =>
            {
                using (await asyncMutex.LockAsync())
                {
                    await Task.Delay(3000);
                }
            });

            // Wait for the Task.Run to Acquire the Mutex.
            await Task.Delay(100);

            // Try to get the Mutex and save the time.
            DateTime timeOfstart    = DateTime.Now;
            DateTime timeOfAcquired = default;

            using (await asyncMutex.LockAsync())
            {
                timeOfAcquired = DateTime.Now;
            };

            Assert.True(myTask.IsCompletedSuccessfully);

            var elapsed = timeOfAcquired - timeOfstart;

            Assert.InRange(elapsed, TimeSpan.FromMilliseconds(2000), TimeSpan.FromMilliseconds(4000));

            // Standard Mutex test.
            int        cnt     = 0;
            List <int> numbers = new List <int>();
            var        rand    = new Random();

            async Task TestLockAsync()
            {
                using (await asyncMutex.LockAsync())
                {
                    cnt++;

                    await Task.Delay(rand.Next(5));

                    numbers.Add(cnt);
                }
            }

            var tasks = new List <Task>();

            for (int i = 0; i < 100; i++)
            {
                var task = TestLockAsync();

                tasks.Add(task);
            }

            await Task.WhenAll(tasks);

            Assert.Equal(100, numbers.Count);
            for (int i = 1; i < 100; i++)
            {
                var prevnum = numbers[i - 1];
                var num     = numbers[i];
                Assert.Equal(prevnum + 1, num);
            }

            // Test that asynclock cancellation is going to throw IOException.
            var mutex = new AsyncMutex("foo");

            using (await mutex.LockAsync())
            {
                using (var cts = new CancellationTokenSource(100))
                {
                    await Assert.ThrowsAsync <IOException>(async() =>
                    {
                        using (await mutex.LockAsync(cts.Token))
                        {
                        }
                    });
                }
            }

            // Test same mutex gets same asynclock.
            var mutex1 = new AsyncMutex("foo");

            using (await mutex1.LockAsync())
            {
                using (var cts = new CancellationTokenSource(100))
                {
                    var mutex2 = new AsyncMutex("foo");
                    await Assert.ThrowsAsync <IOException>(async() =>
                    {
                        using (await mutex2.LockAsync(cts.Token))
                        {
                        }
                    });
                }
            }

            // Different AsyncMutex object but same name.
            AsyncMutex asyncMutex2 = new AsyncMutex("mutex1");

            // Acquire the first mutex with a background thread and hold it for a while.
            var myTask2 = Task.Run(async() =>
            {
                using (await asyncMutex.LockAsync())
                {
                    await Task.Delay(3000);
                }
            });

            // Make sure the task started.
            await Task.Delay(100);

            timeOfstart    = DateTime.Now;
            timeOfAcquired = default;
            // Now try to acquire another AsyncMutex object but with the same name!
            using (await asyncMutex2.LockAsync())
            {
                timeOfAcquired = DateTime.Now;
            }

            await myTask2;

            Assert.True(myTask2.IsCompletedSuccessfully);

            elapsed = timeOfAcquired - timeOfstart;
            Assert.InRange(elapsed, TimeSpan.FromMilliseconds(2000), TimeSpan.FromMilliseconds(4000));
        }
Exemplo n.º 17
0
        /// <summary>
        /// Clears the internal cache of Win2D images
        /// </summary>
        /// <returns>A sequence of the <see cref="CompositionBrush"/> objects that were present in the cache</returns>
        /// <remarks>The returned items should be manually disposed after checking that they are no longer in use in other effect pipelines</remarks>

        public static async Task <IReadOnlyList <CompositionBrush> > ClearCacheAsync()
        {
            using (await Win2DMutex.LockAsync())
                return(Cache.Clear());
        }
Exemplo n.º 18
0
        public async Task AsyncMutexTestsAsync()
        {
            var mutexName1 = $"mutex1-{DateTime.Now.Ticks}";             // Randomize the name to avoid system wide collisions.

            AsyncMutex asyncMutex = new AsyncMutex(mutexName1);

            // Cannot be IDisposable because the pattern is like Nito's AsyncLock.
            Assert.False(asyncMutex is IDisposable);

            // Use the mutex two times after each other.
            using (await asyncMutex.LockAsync())
            {
                await Task.Delay(1);
            }

            using (await asyncMutex.LockAsync())
            {
                await Task.Delay(1);
            }

            // Release the Mutex from another thread.

            var disposable = await asyncMutex.LockAsync();

            var myThread = new Thread(new ThreadStart(() => disposable.Dispose()));

            myThread.Start();
            myThread.Join();

            using (await asyncMutex.LockAsync())
            {
                await Task.Delay(1);
            }

            // Standard Mutex test.
            int        cnt     = 0;
            List <int> numbers = new List <int>();
            var        rand    = new Random();

            async Task TestLockAsync()
            {
                using (await asyncMutex.LockAsync())
                {
                    cnt++;

                    await Task.Delay(rand.Next(5));

                    numbers.Add(cnt);
                }
            }

            var tasks = new List <Task>();

            for (int i = 0; i < 100; i++)
            {
                var task = TestLockAsync();

                tasks.Add(task);
            }

            await Task.WhenAll(tasks);

            Assert.Equal(100, numbers.Count);
            for (int i = 1; i < 100; i++)
            {
                var prevnum = numbers[i - 1];
                var num     = numbers[i];
                Assert.Equal(prevnum + 1, num);
            }

            var mutexName2 = $"mutex2-{DateTime.Now.Ticks}";

            // Test that asynclock cancellation is going to throw IOException.
            var mutex = new AsyncMutex(mutexName2);

            using (await mutex.LockAsync())
            {
                using var cts = new CancellationTokenSource(100);
                await Assert.ThrowsAsync <IOException>(async() =>
                {
                    using (await mutex.LockAsync(cancellationToken: cts.Token))
                    {
                    }
                });
            }

            // Test same mutex gets same asynclock.
            var mutex1 = new AsyncMutex(mutexName2);

            using (await mutex1.LockAsync())
            {
                using var cts = new CancellationTokenSource(100);
                var mutex2 = new AsyncMutex(mutexName2);
                await Assert.ThrowsAsync <IOException>(async() =>
                {
                    using (await mutex2.LockAsync(cancellationToken: cts.Token))
                    {
                    }
                });
            }

            // Concurrency test with the same AsyncMutex object.
            await TestMutexConcurrencyAsync(asyncMutex, asyncMutex);

            // Concurrency test with different AsyncMutex object but same name.
            AsyncMutex asyncMutex2 = new AsyncMutex(mutexName1);

            await TestMutexConcurrencyAsync(asyncMutex, asyncMutex2);
        }
Exemplo n.º 19
0
        public void LockUnlockBenchmark()
        {
            using (var cancel = new CancellationTokenSource()) {
                Console.WriteLine("Warmup:");
                Benchmark(runNextSynchronously: false, cancelable: false, threads: 1024).Wait();
                Benchmark(runNextSynchronously: false, cancelable: true, threads: 1024).Wait();
                Benchmark(runNextSynchronously: true, cancelable: false, threads: 1024).Wait();
                Benchmark(runNextSynchronously: true, cancelable: true, threads: 1024).Wait();

                Console.WriteLine("\nThe real thing:");
                foreach (bool sync in new[] { true, false })
                {
                    foreach (bool c in new[] { false, true })
                    {
                        foreach (int threads in new[] { 1, 2, 4, 32, 1024 })
                        {
                            Benchmark(runNextSynchronously: sync, cancelable: c, threads: threads).Wait();
                        }
                    }
                }

                async Task Benchmark(bool runNextSynchronously, bool cancelable, int threads)
                {
                    long      counter   = 0;
                    var       mutex     = new AsyncMutex();
                    TimeSpan  duration  = TimeSpan.FromSeconds(1);
                    Stopwatch stopwatch = Stopwatch.StartNew();
                    await Task.WhenAll(Enumerable.Range(0, threads).Select(_ => Inc()));

                    double ns = 1e9 * stopwatch.Elapsed.TotalSeconds / counter;

                    Console.WriteLine(
                        "  Benchmark(runNextSynchronously: {0,5}, cancelable: {1,5}, threads: {2,5:N0}): {3,7:N1} ns/call",
                        runNextSynchronously, cancelable, threads, ns);

                    async Task Inc()
                    {
                        await Task.Yield();

                        while (stopwatch.Elapsed < duration)
                        {
                            if (cancelable)
                            {
                                for (int i = 0; i != 64; ++i)
                                {
                                    await mutex.LockAsync(cancel.Token);

                                    ++counter;
                                    mutex.Unlock(runNextSynchronously: runNextSynchronously);
                                }
                            }
                            else
                            {
                                for (int i = 0; i != 64; ++i)
                                {
                                    await mutex.LockAsync();

                                    ++counter;
                                    mutex.Unlock(runNextSynchronously: runNextSynchronously);
                                }
                            }
                        }
                    }
                }
            }
        }