mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2026-01-14 23:50:41 +00:00
Use file-scoped namespaces
This commit is contained in:
@@ -27,180 +27,180 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.Core;
|
||||
|
||||
namespace ArchiSteamFarm.Helpers {
|
||||
internal sealed class CrossProcessFileBasedSemaphore : ICrossProcessSemaphore, IDisposable {
|
||||
private const ushort SpinLockDelay = 1000; // In milliseconds
|
||||
namespace ArchiSteamFarm.Helpers;
|
||||
|
||||
private readonly string FilePath;
|
||||
private readonly SemaphoreSlim LocalSemaphore = new(1, 1);
|
||||
internal sealed class CrossProcessFileBasedSemaphore : ICrossProcessSemaphore, IDisposable {
|
||||
private const ushort SpinLockDelay = 1000; // In milliseconds
|
||||
|
||||
private FileStream? FileLock;
|
||||
private readonly string FilePath;
|
||||
private readonly SemaphoreSlim LocalSemaphore = new(1, 1);
|
||||
|
||||
internal CrossProcessFileBasedSemaphore(string name) {
|
||||
if (string.IsNullOrEmpty(name)) {
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
private FileStream? FileLock;
|
||||
|
||||
FilePath = Path.Combine(Path.GetTempPath(), SharedInfo.ASF, name);
|
||||
|
||||
EnsureFileExists();
|
||||
internal CrossProcessFileBasedSemaphore(string name) {
|
||||
if (string.IsNullOrEmpty(name)) {
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
LocalSemaphore.Dispose();
|
||||
FilePath = Path.Combine(Path.GetTempPath(), SharedInfo.ASF, name);
|
||||
|
||||
FileLock?.Dispose();
|
||||
}
|
||||
EnsureFileExists();
|
||||
}
|
||||
|
||||
void ICrossProcessSemaphore.Release() {
|
||||
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
|
||||
lock (LocalSemaphore) {
|
||||
if (FileLock == null) {
|
||||
throw new InvalidOperationException(nameof(FileLock));
|
||||
}
|
||||
public void Dispose() {
|
||||
LocalSemaphore.Dispose();
|
||||
|
||||
FileLock.Dispose();
|
||||
FileLock = null;
|
||||
FileLock?.Dispose();
|
||||
}
|
||||
|
||||
void ICrossProcessSemaphore.Release() {
|
||||
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
|
||||
lock (LocalSemaphore) {
|
||||
if (FileLock == null) {
|
||||
throw new InvalidOperationException(nameof(FileLock));
|
||||
}
|
||||
|
||||
LocalSemaphore.Release();
|
||||
FileLock.Dispose();
|
||||
FileLock = null;
|
||||
}
|
||||
|
||||
async Task ICrossProcessSemaphore.WaitAsync() {
|
||||
await LocalSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
LocalSemaphore.Release();
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
async Task ICrossProcessSemaphore.WaitAsync() {
|
||||
await LocalSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
try {
|
||||
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
|
||||
lock (LocalSemaphore) {
|
||||
if (FileLock != null) {
|
||||
throw new InvalidOperationException(nameof(FileLock));
|
||||
}
|
||||
bool success = false;
|
||||
|
||||
EnsureFileExists();
|
||||
|
||||
FileLock = new FileStream(FilePath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None);
|
||||
success = true;
|
||||
|
||||
return;
|
||||
}
|
||||
} catch (IOException) {
|
||||
await Task.Delay(SpinLockDelay).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (!success) {
|
||||
LocalSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async Task<bool> ICrossProcessSemaphore.WaitAsync(int millisecondsTimeout) {
|
||||
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
|
||||
if (!await LocalSemaphore.WaitAsync(millisecondsTimeout).ConfigureAwait(false)) {
|
||||
stopwatch.Stop();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
try {
|
||||
stopwatch.Stop();
|
||||
|
||||
millisecondsTimeout -= (int) stopwatch.ElapsedMilliseconds;
|
||||
|
||||
if (millisecondsTimeout <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
|
||||
lock (LocalSemaphore) {
|
||||
if (FileLock != null) {
|
||||
throw new InvalidOperationException(nameof(FileLock));
|
||||
}
|
||||
|
||||
EnsureFileExists();
|
||||
|
||||
FileLock = new FileStream(FilePath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None);
|
||||
success = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch (IOException) {
|
||||
if (millisecondsTimeout <= SpinLockDelay) {
|
||||
return false;
|
||||
try {
|
||||
while (true) {
|
||||
try {
|
||||
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
|
||||
lock (LocalSemaphore) {
|
||||
if (FileLock != null) {
|
||||
throw new InvalidOperationException(nameof(FileLock));
|
||||
}
|
||||
|
||||
await Task.Delay(SpinLockDelay).ConfigureAwait(false);
|
||||
millisecondsTimeout -= SpinLockDelay;
|
||||
EnsureFileExists();
|
||||
|
||||
FileLock = new FileStream(FilePath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None);
|
||||
success = true;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (!success) {
|
||||
LocalSemaphore.Release();
|
||||
} catch (IOException) {
|
||||
await Task.Delay(SpinLockDelay).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureFileExists() {
|
||||
if (File.Exists(FilePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string? directoryPath = Path.GetDirectoryName(FilePath);
|
||||
|
||||
if (string.IsNullOrEmpty(directoryPath)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(directoryPath));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(directoryPath)) {
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
|
||||
if (OperatingSystem.IsWindows()) {
|
||||
DirectoryInfo directoryInfo = new(directoryPath);
|
||||
|
||||
try {
|
||||
DirectorySecurity directorySecurity = new(directoryPath, AccessControlSections.All);
|
||||
|
||||
directoryInfo.SetAccessControl(directorySecurity);
|
||||
} catch (PrivilegeNotHeldException e) {
|
||||
// Non-critical, user might have no rights to manage the resource
|
||||
ASF.ArchiLogger.LogGenericDebuggingException(e);
|
||||
}
|
||||
} else if (OperatingSystem.IsFreeBSD() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) {
|
||||
OS.UnixSetFileAccess(directoryPath!, OS.EUnixPermission.Combined777);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
new FileStream(FilePath, FileMode.CreateNew).Dispose();
|
||||
|
||||
if (OperatingSystem.IsWindows()) {
|
||||
FileInfo fileInfo = new(FilePath);
|
||||
|
||||
try {
|
||||
FileSecurity fileSecurity = new(FilePath, AccessControlSections.All);
|
||||
|
||||
fileInfo.SetAccessControl(fileSecurity);
|
||||
} catch (PrivilegeNotHeldException e) {
|
||||
// Non-critical, user might have no rights to manage the resource
|
||||
ASF.ArchiLogger.LogGenericDebuggingException(e);
|
||||
}
|
||||
} else if (OperatingSystem.IsFreeBSD() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) {
|
||||
OS.UnixSetFileAccess(FilePath, OS.EUnixPermission.Combined777);
|
||||
}
|
||||
} catch (IOException) {
|
||||
// Ignored, if the file was already created in the meantime by another instance, this is fine
|
||||
} finally {
|
||||
if (!success) {
|
||||
LocalSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async Task<bool> ICrossProcessSemaphore.WaitAsync(int millisecondsTimeout) {
|
||||
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
|
||||
if (!await LocalSemaphore.WaitAsync(millisecondsTimeout).ConfigureAwait(false)) {
|
||||
stopwatch.Stop();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
try {
|
||||
stopwatch.Stop();
|
||||
|
||||
millisecondsTimeout -= (int) stopwatch.ElapsedMilliseconds;
|
||||
|
||||
if (millisecondsTimeout <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
|
||||
lock (LocalSemaphore) {
|
||||
if (FileLock != null) {
|
||||
throw new InvalidOperationException(nameof(FileLock));
|
||||
}
|
||||
|
||||
EnsureFileExists();
|
||||
|
||||
FileLock = new FileStream(FilePath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None);
|
||||
success = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch (IOException) {
|
||||
if (millisecondsTimeout <= SpinLockDelay) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await Task.Delay(SpinLockDelay).ConfigureAwait(false);
|
||||
millisecondsTimeout -= SpinLockDelay;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (!success) {
|
||||
LocalSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureFileExists() {
|
||||
if (File.Exists(FilePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string? directoryPath = Path.GetDirectoryName(FilePath);
|
||||
|
||||
if (string.IsNullOrEmpty(directoryPath)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(directoryPath));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(directoryPath)) {
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
|
||||
if (OperatingSystem.IsWindows()) {
|
||||
DirectoryInfo directoryInfo = new(directoryPath);
|
||||
|
||||
try {
|
||||
DirectorySecurity directorySecurity = new(directoryPath, AccessControlSections.All);
|
||||
|
||||
directoryInfo.SetAccessControl(directorySecurity);
|
||||
} catch (PrivilegeNotHeldException e) {
|
||||
// Non-critical, user might have no rights to manage the resource
|
||||
ASF.ArchiLogger.LogGenericDebuggingException(e);
|
||||
}
|
||||
} else if (OperatingSystem.IsFreeBSD() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) {
|
||||
OS.UnixSetFileAccess(directoryPath!, OS.EUnixPermission.Combined777);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
new FileStream(FilePath, FileMode.CreateNew).Dispose();
|
||||
|
||||
if (OperatingSystem.IsWindows()) {
|
||||
FileInfo fileInfo = new(FilePath);
|
||||
|
||||
try {
|
||||
FileSecurity fileSecurity = new(FilePath, AccessControlSections.All);
|
||||
|
||||
fileInfo.SetAccessControl(fileSecurity);
|
||||
} catch (PrivilegeNotHeldException e) {
|
||||
// Non-critical, user might have no rights to manage the resource
|
||||
ASF.ArchiLogger.LogGenericDebuggingException(e);
|
||||
}
|
||||
} else if (OperatingSystem.IsFreeBSD() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) {
|
||||
OS.UnixSetFileAccess(FilePath, OS.EUnixPermission.Combined777);
|
||||
}
|
||||
} catch (IOException) {
|
||||
// Ignored, if the file was already created in the meantime by another instance, this is fine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user