chr
2026-04-05 fe750b791d5b517cc4e9bc8e99a9a75139a0cfba
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
 
namespace OpenTap.Package.UnitTests
{
    [TestFixture]
    public class NamedMutexTest
    {
        public class MutexUsingTestStep : TestStep
        {
            public string MutexName { get; set; }
            public bool AlreadyLocked { get; set; }
            public override void Run()
            {
                using var mut = FileLock.Create(MutexName);
                if (AlreadyLocked)
                {
                    if (mut.WaitOne(0))
                        UpgradeVerdict(Verdict.Fail);
                    else
                        UpgradeVerdict(Verdict.Pass);
                }
                else
                {
                    if (mut.WaitOne(0) == false)
                        UpgradeVerdict(Verdict.Fail);
                    else 
                        UpgradeVerdict(Verdict.Pass);
                }
            }
        }
 
        [TestCase(true)]
        [TestCase(false)]
        public void TestExclusiveAccessAcrossProcesses(bool alreadyLocked)
        {
            var mutexName = Path.GetTempFileName();
            File.Delete(mutexName);
            using (var mut = FileLock.Create(mutexName))
            {
                var step = new MutexUsingTestStep() { MutexName = mutexName, AlreadyLocked = alreadyLocked };
                var processRunner = new SubProcessHost();
 
                if (alreadyLocked)
                {
                    Assert.IsTrue(mut.WaitOne(0), "Failed initially getting the mutex.");
                }
 
                var result = processRunner.Run(step, false, CancellationToken.None);
                var msg = alreadyLocked ? "Expected the mutex to be locked." : "Expected the mutex to be available.";
                Assert.AreEqual(Verdict.Pass, result, msg);
            }
        }
 
        [Test]
        public void TestExclusiveAccess()
        {
            var outerEvent = new ManualResetEventSlim(false);
            var innerEvent = new ManualResetEventSlim(false);
            var mutexName = Path.GetTempFileName();
            File.Delete(mutexName);
 
            using var innerMutex = FileLock.Create(mutexName);
            // Manually disposed later
            var outerMutex = FileLock.Create(mutexName);
 
            { // Verify the outer mutex works
                Assert.IsTrue(outerMutex.WaitOne(0));
                outerMutex.Release();
                Assert.IsTrue(outerMutex.WaitOne(0));
                outerMutex.Release();
            }
 
            Task.Run(() =>
            {
                Assert.IsTrue(innerMutex.WaitOne());
                innerEvent.Set();
                outerEvent.Wait();
                // Sleep for a "long" time before releasing
                TapThread.Sleep(TimeSpan.FromSeconds(1));
                innerMutex.Release();
            });
 
            // Wait for the inner mutex to be acquired
            innerEvent.Wait();
            // Verify this locks the outer mutex
            Assert.IsFalse(outerMutex.WaitOne(0));
            // Signal the thread to release the inner mutex
            var sw = Stopwatch.StartNew();
            var limit = TimeSpan.FromSeconds(2);
            outerEvent.Set();
            // Wait for the inner mutex to be released. Also test that timeouts work
            Assert.IsTrue(outerMutex.WaitOne(limit));
            Assert.IsTrue(sw.Elapsed < limit, "Timeout took longer than expected!");
            // Verify that disposing the mutex releases the lock
            outerMutex.Dispose();
            Assert.IsTrue(innerMutex.WaitOne(0));
        }
    }
}