|
5 | 5 | using System.Diagnostics; |
6 | 6 | using System.IO; |
7 | 7 | using System.Runtime.InteropServices; |
| 8 | +using System.Runtime.Versioning; |
8 | 9 | using System.Threading.Tasks; |
9 | 10 | using Microsoft.DotNet.RemoteExecutor; |
10 | 11 | using Xunit; |
@@ -250,6 +251,11 @@ public void MutualExclusionTest() |
250 | 251 | public void Ctor_InvalidNames_Unix() |
251 | 252 | { |
252 | 253 | AssertExtensions.Throws<ArgumentException>("name", null, () => new Mutex(new string('a', 1000), options: default)); |
| 254 | + Assert.Throws<IOException>(() => new Mutex("Foo/Bar", options: default)); |
| 255 | + AssertExtensions.Throws<ArgumentException>("name", null, () => new Mutex("Foo\\Bar", options: default)); |
| 256 | + AssertExtensions.Throws<ArgumentException>("name", null, () => new Mutex("Foo\\Bar", options: new NamedWaitHandleOptions { CurrentSessionOnly = false })); |
| 257 | + Assert.Throws<IOException>(() => new Mutex("Global\\Foo/Bar", options: new NamedWaitHandleOptions { CurrentSessionOnly = false })); |
| 258 | + Assert.Throws<IOException>(() => new Mutex("Global\\Foo\\Bar", options: new NamedWaitHandleOptions { CurrentSessionOnly = false })); |
253 | 259 | } |
254 | 260 |
|
255 | 261 | [Theory] |
@@ -971,9 +977,118 @@ public void NamedMutex_DisposeWhenLockedRaceTest() |
971 | 977 | } |
972 | 978 | } |
973 | 979 |
|
| 980 | + [ConditionalFact(nameof(IsCrossProcessNamedMutexSupported))] |
| 981 | + [PlatformSpecific(TestPlatforms.AnyUnix)] |
| 982 | + public void NamedMutex_OtherEvent_NotCompatible() |
| 983 | + { |
| 984 | + using Mutex m = new Mutex(Guid.NewGuid().ToString("N"), options: default); |
| 985 | + using ManualResetEvent mre = new(false); |
| 986 | + |
| 987 | + Assert.Throws<PlatformNotSupportedException>(() => WaitHandle.WaitAny(new WaitHandle[] { m, mre }, 0)); |
| 988 | + } |
| 989 | + |
| 990 | + private const string GlobalSharedMemoryDirectory = $"/tmp/.dotnet/shm/global"; |
| 991 | + private const UnixFileMode AllUsersRwx = |
| 992 | + UnixFileMode.UserRead |
| 993 | + | UnixFileMode.UserWrite |
| 994 | + | UnixFileMode.UserExecute |
| 995 | + | UnixFileMode.GroupRead |
| 996 | + | UnixFileMode.GroupWrite |
| 997 | + | UnixFileMode.GroupExecute |
| 998 | + | UnixFileMode.OtherRead |
| 999 | + | UnixFileMode.OtherWrite |
| 1000 | + | UnixFileMode.OtherExecute; |
| 1001 | + |
| 1002 | + [ConditionalFact(nameof(IsCrossProcessNamedMutexSupported))] |
| 1003 | + [PlatformSpecific(TestPlatforms.AnyUnix)] |
| 1004 | + [UnsupportedOSPlatform("windows")] |
| 1005 | + public void NamedMutex_InvalidSharedMemoryHeaderVersion() |
| 1006 | + { |
| 1007 | + string name = Guid.NewGuid().ToString("N"); |
| 1008 | + string path = $"{GlobalSharedMemoryDirectory}/{name}"; |
| 1009 | + |
| 1010 | + Directory.CreateDirectory(GlobalSharedMemoryDirectory, AllUsersRwx); |
| 1011 | + using (FileStream fs = new(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, 4096)) |
| 1012 | + using (BinaryWriter bw = new(fs)) |
| 1013 | + { |
| 1014 | + bw.Write((byte)1); // Write the shared memory type (mutex) |
| 1015 | + bw.Write((byte)2); // Write an invalid version number |
| 1016 | + // Make the file large enough for a valid named mutex file and divisible by page size (it should always be under one page). |
| 1017 | + fs.SetLength(Environment.SystemPageSize); |
| 1018 | + |
| 1019 | + // Try opening a mutex when we still have the file locked. |
| 1020 | + Assert.Throws<WaitHandleCannotBeOpenedException>(() => new Mutex($"Global\\{name}", new NamedWaitHandleOptions { CurrentSessionOnly = false, CurrentUserOnly = false })); |
| 1021 | + } |
| 1022 | + } |
| 1023 | + |
| 1024 | + [ConditionalFact(nameof(IsCrossProcessNamedMutexSupported))] |
| 1025 | + [PlatformSpecific(TestPlatforms.AnyUnix)] |
| 1026 | + [UnsupportedOSPlatform("windows")] |
| 1027 | + public void NamedMutex_SharedMemoryFileAlreadyOpen() |
| 1028 | + { |
| 1029 | + string name = Guid.NewGuid().ToString("N"); |
| 1030 | + string path = $"{GlobalSharedMemoryDirectory}/{name}"; |
| 1031 | + |
| 1032 | + Directory.CreateDirectory(GlobalSharedMemoryDirectory, AllUsersRwx); |
| 1033 | + // Take an exclusive file lock of the global shared memory file. |
| 1034 | + using (FileStream fs = new(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096)) |
| 1035 | + using (BinaryWriter bw = new(fs)) |
| 1036 | + { |
| 1037 | + bw.Write((byte)1); // Write the shared memory type (mutex) |
| 1038 | + bw.Write((byte)1); // Write valid version number |
| 1039 | + // Make the file large enough for a valid named mutex file and divisible by page size (it should always be under one page). |
| 1040 | + fs.SetLength(Environment.SystemPageSize); |
| 1041 | + |
| 1042 | + // Try opening a mutex when we still have the file locked. |
| 1043 | + Assert.Throws<IOException>(() => new Mutex($"Global\\{name}", new NamedWaitHandleOptions { CurrentSessionOnly = false, CurrentUserOnly = false })); |
| 1044 | + } |
| 1045 | + } |
| 1046 | + |
| 1047 | + [ConditionalFact(nameof(IsCrossProcessNamedMutexSupported))] |
| 1048 | + [PlatformSpecific(TestPlatforms.AnyUnix)] |
| 1049 | + [UnsupportedOSPlatform("windows")] |
| 1050 | + public void NamedMutex_InvalidSharedMemoryHeaderKind() |
| 1051 | + { |
| 1052 | + string name = Guid.NewGuid().ToString("N"); |
| 1053 | + string path = $"{GlobalSharedMemoryDirectory}/{name}"; |
| 1054 | + |
| 1055 | + Directory.CreateDirectory(GlobalSharedMemoryDirectory, AllUsersRwx); |
| 1056 | + using (FileStream fs = new(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, 4096)) |
| 1057 | + using (BinaryWriter bw = new(fs)) |
| 1058 | + { |
| 1059 | + bw.Write((byte)2); // Write the shared memory type (invalid) |
| 1060 | + bw.Write((byte)1); // Write a version number |
| 1061 | + // Make the file large enough for a valid named mutex file and divisible by page size (it should always be under one page). |
| 1062 | + fs.SetLength(Environment.SystemPageSize); |
| 1063 | + // Try opening a mutex when we still have the file locked. |
| 1064 | + Assert.Throws<WaitHandleCannotBeOpenedException>(() => new Mutex($"Global\\{name}", new NamedWaitHandleOptions { CurrentSessionOnly = false, CurrentUserOnly = false })); |
| 1065 | + } |
| 1066 | + } |
| 1067 | + |
| 1068 | + [ConditionalFact(nameof(IsCrossProcessNamedMutexSupported))] |
| 1069 | + [PlatformSpecific(TestPlatforms.AnyUnix)] |
| 1070 | + [UnsupportedOSPlatform("windows")] |
| 1071 | + public void NamedMutex_TooSmallSharedMemoryFile() |
| 1072 | + { |
| 1073 | + string name = Guid.NewGuid().ToString("N"); |
| 1074 | + string path = $"{GlobalSharedMemoryDirectory}/{name}"; |
| 1075 | + |
| 1076 | + Directory.CreateDirectory(GlobalSharedMemoryDirectory, AllUsersRwx); |
| 1077 | + using (FileStream fs = new(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, 4096)) |
| 1078 | + using (BinaryWriter bw = new(fs)) |
| 1079 | + { |
| 1080 | + bw.Write((byte)1); // Write the shared memory type (mutex) |
| 1081 | + bw.Write((byte)1); // Write a valid version number |
| 1082 | + // Make the file large enough for a valid named mutex file but not divisible by page size. |
| 1083 | + fs.SetLength(Environment.SystemPageSize - 1); |
| 1084 | + // Try opening a mutex when we still have the file locked. |
| 1085 | + Assert.Throws<WaitHandleCannotBeOpenedException>(() => new Mutex($"Global\\{name}", new NamedWaitHandleOptions { CurrentSessionOnly = false, CurrentUserOnly = false })); |
| 1086 | + } |
| 1087 | + } |
| 1088 | + |
974 | 1089 | public static TheoryData<string> GetValidNames() |
975 | 1090 | { |
976 | | - var names = new TheoryData<string>() { Guid.NewGuid().ToString("N") }; |
| 1091 | + var names = new TheoryData<string>() { Guid.NewGuid().ToString("N") }; |
977 | 1092 |
|
978 | 1093 | // Windows native named mutexes and in-proc named mutexes support very long (1000+ char) names. |
979 | 1094 | // Non-Windows cross-process named mutexes are emulated using file system. It imposes limit |
|
0 commit comments