-
Notifications
You must be signed in to change notification settings - Fork 26
/
Interop.cs
163 lines (138 loc) · 6.96 KB
/
Interop.cs
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Threading;
using Cloudtoid.Interprocess.Semaphore.Posix;
namespace Cloudtoid.Interprocess.Semaphore.Linux
{
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Matching the exact names in Linux/MacOS")]
internal static partial class Interop
{
private const string Lib = "librt.so.1";
private const uint SEMVALUEMAX = 32767;
private const int OCREAT = 0x040; // Create the semaphore if it does not exist
private const int ENOENT = 2; // The named semaphore does not exist.
private const int EINTR = 4; // Semaphore operation was interrupted by a signal.
private const int EAGAIN = 11; // Couldn't be acquired (sem_trywait)
private const int ENOMEM = 12; // Out of memory
private const int EACCES = 13; // Semaphore exists, but the caller does not have permission to open it.
private const int EEXIST = 17; // O_CREAT and O_EXCL were specified and the semaphore exists.
private const int EINVAL = 22; // Invalid semaphore or operation on a semaphore
private const int ENFILE = 23; // Too many semaphores or file descriptors are open on the system.
private const int EMFILE = 24; // The process has already reached its limit for semaphores or file descriptors in use.
private const int ENAMETOOLONG = 36; // The specified semaphore name is too long
private const int EOVERFLOW = 75; // The maximum allowable value for a semaphore would be exceeded.
private const int ETIMEDOUT = 110; // The call timed out before the semaphore could be locked.
private static unsafe int Error => Marshal.GetLastWin32Error();
[LibraryImport(Lib, EntryPoint = "sem_open", SetLastError = true, StringMarshalling = StringMarshalling.Utf8)]
private static partial IntPtr SemaphoreOpen(string name, int oflag, uint mode, uint value);
[LibraryImport(Lib, EntryPoint = "sem_post", SetLastError = true)]
private static partial int SemaphorePost(IntPtr handle);
[LibraryImport(Lib, EntryPoint = "sem_wait", SetLastError = true)]
private static partial int SemaphoreWait(IntPtr handle);
[LibraryImport(Lib, EntryPoint = "sem_trywait", SetLastError = true)]
private static partial int SemaphoreTryWait(IntPtr handle);
[LibraryImport(Lib, EntryPoint = "sem_timedwait", SetLastError = true)]
private static partial int SemaphoreTimedWait(IntPtr handle, ref PosixTimespec abs_timeout);
[LibraryImport(Lib, EntryPoint = "sem_unlink", SetLastError = true, StringMarshalling = StringMarshalling.Utf8)]
private static partial int SemaphoreUnlink(string name);
[LibraryImport(Lib, EntryPoint = "sem_close", SetLastError = true)]
private static partial int SemaphoreClose(IntPtr handle);
internal static IntPtr CreateOrOpenSemaphore(string name, uint initialCount)
{
var handle = SemaphoreOpen(name, OCREAT, (uint)PosixFilePermissions.ACCESSPERMS, initialCount);
if (handle != IntPtr.Zero)
return handle;
throw Error switch
{
EINVAL => new ArgumentException($"The initial count cannot be greater than {SEMVALUEMAX}.", nameof(initialCount)),
ENAMETOOLONG => new ArgumentException($"The specified semaphore name is too long.", nameof(name)),
EACCES => new PosixSemaphoreUnauthorizedAccessException(),
EEXIST => new PosixSemaphoreExistsException(),
EINTR => new OperationCanceledException(),
ENFILE => new PosixSemaphoreException("Too many semaphores or file descriptors are open on the system."),
EMFILE => new PosixSemaphoreException("Too many semaphores or file descriptors are open by this process."),
ENOMEM => new InsufficientMemoryException(),
_ => new PosixSemaphoreException(Error),
};
}
internal static void Release(IntPtr handle)
{
if (SemaphorePost(handle) == 0)
return;
throw Error switch
{
EINVAL => new InvalidPosixSemaphoreException(),
EOVERFLOW => new SemaphoreFullException(),
_ => new PosixSemaphoreException(Error),
};
}
internal static bool Wait(IntPtr handle, int millisecondsTimeout)
{
if (millisecondsTimeout == Timeout.Infinite)
{
Wait(handle);
return true;
}
else if (millisecondsTimeout == 0)
{
if (SemaphoreTryWait(handle) == 0)
return true;
return Error switch
{
EAGAIN => false,
EINVAL => throw new InvalidPosixSemaphoreException(),
EINTR => throw new OperationCanceledException(),
_ => throw new PosixSemaphoreException(Error),
};
}
var timeout = DateTimeOffset.UtcNow.AddMilliseconds(millisecondsTimeout);
return Wait(handle, timeout);
}
private static void Wait(IntPtr handle)
{
if (SemaphoreWait(handle) == 0)
return;
throw Error switch
{
EINVAL => new InvalidPosixSemaphoreException(),
EINTR => new OperationCanceledException(),
_ => new PosixSemaphoreException(Error),
};
}
private static bool Wait(IntPtr handle, PosixTimespec timeout)
{
if (SemaphoreTimedWait(handle, ref timeout) == 0)
return true;
return Error switch
{
ETIMEDOUT => false,
EINVAL => throw new InvalidPosixSemaphoreException(),
EINTR => throw new OperationCanceledException(),
_ => throw new PosixSemaphoreException(Error),
};
}
internal static void Close(IntPtr handle)
{
if (SemaphoreClose(handle) == 0)
return;
throw Error switch
{
EINVAL => new InvalidPosixSemaphoreException(),
_ => new PosixSemaphoreException(Error),
};
}
internal static void Unlink(string name)
{
if (SemaphoreUnlink(name) == 0)
return;
throw Error switch
{
ENAMETOOLONG => new ArgumentException($"The specified semaphore name is too long.", nameof(name)),
EACCES => new PosixSemaphoreUnauthorizedAccessException(),
ENOENT => new PosixSemaphoreNotExistsException(),
_ => new PosixSemaphoreException(Error),
};
}
}
}