public static bool IsSolaris => RuntimeInformation.IsOSPlatform(OSPlatform.Create("SOLARIS"));
public static bool IsBrowser => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));
public static bool IsNotBrowser => !IsBrowser;
- public static bool IsNotMobile => IsNotBrowser && !IsMacCatalyst && !IsiOS && !IstvOS && !IsAndroid;
+ public static bool IsMobile => IsBrowser || IsMacCatalyst || IsiOS || IstvOS || IsAndroid;
+ public static bool IsNotMobile => !IsMobile;
public static bool IsNotNetFramework => !IsNetFramework;
public static bool IsArmProcess => RuntimeInformation.ProcessArchitecture == Architecture.Arm;
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\NFloat.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OptionalAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OutAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PosixSignal.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PosixSignalContext.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PosixSignalRegistration.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PreserveSigAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ProgIdAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\SafeArrayRankMismatchException.cs" />
</ItemGroup>
<ItemGroup Condition="'$(IsMobileLike)' == 'true'">
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Loader\AssemblyDependencyResolver.PlatformNotSupported.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PosixSignalRegistration.PlatformNotSupported.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.ActivityControl.cs">
<Compile Include="$(CommonPath)Interop\Windows\Interop.OBJECT_ATTRIBUTES.cs">
<Link>Common\Interop\Windows\Interop.OBJECT_ATTRIBUTES.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetConsoleCtrlHandler.cs">
+ <Link>Common\Interop\Windows\Interop.SetConsoleCtrlHandler.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetCurrentDirectory.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.SetCurrentDirectory.cs</Link>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshal.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\NativeMemory.Windows.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PosixSignalRegistration.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StandardOleMarshalObject.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelMonitor.Windows.cs" />
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetPid.cs">
<Link>Common\Interop\Unix\System.Native\Interop.GetPid.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.InitializeTerminalAndSignalHandling.cs"
+ Link="Common\Interop\Unix\Interop.InitializeTerminalAndSignalHandling.cs" />
+ <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.PosixSignal.cs"
+ Link="Common\Interop\Unix\Interop.PosixSignal.cs" />
<Compile Include="$(CommonPath)Interop\FreeBSD\Interop.Process.GetProcInfo.cs" Condition="'$(TargetsFreeBSD)' == 'true'"
Link="Common\Interop\FreeBSD\Interop.Process.GetProcInfo.cs" />
<Compile Include="$(CommonPath)Interop\BSD\System.Native\Interop.Sysctl.cs" Condition="'$(TargetsFreeBSD)' == 'true'"
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Unix.cs" />
</ItemGroup>
+ <ItemGroup Condition="'$(TargetsUnix)' == 'true' and '$(IsMobileLike)' != 'true'">
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PosixSignalRegistration.Unix.cs" />
+ </ItemGroup>
<ItemGroup Condition="'$(TargetsBrowser)' == 'true'">
<Compile Include="$(MSBuildThisFileDirectory)System\AppContext.Browser.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Browser.cs" />
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.Versioning;
+
+namespace System.Runtime.InteropServices
+{
+ /// <summary>Specifies a POSIX signal number.</summary>
+ public enum PosixSignal
+ {
+ /// <summary>Hangup</summary>
+ SIGHUP = -1,
+
+ /// <summary>Interrupt</summary>
+ SIGINT = -2,
+
+ /// <summary>Quit</summary>
+ SIGQUIT = -3,
+
+ /// <summary>Termination</summary>
+ SIGTERM = -4,
+
+ /// <summary>Child stopped</summary>
+ [UnsupportedOSPlatform("windows")]
+ SIGCHLD = -5,
+
+ /// <summary>Continue if stopped</summary>
+ [UnsupportedOSPlatform("windows")]
+ SIGCONT = -6,
+
+ /// <summary>Window resized</summary>
+ [UnsupportedOSPlatform("windows")]
+ SIGWINCH = -7,
+
+ /// <summary>Terminal input for background process</summary>
+ [UnsupportedOSPlatform("windows")]
+ SIGTTIN = -8,
+
+ /// <summary>Terminal output for background process</summary>
+ [UnsupportedOSPlatform("windows")]
+ SIGTTOU = -9,
+
+ /// <summary>Stop typed at terminal</summary>
+ [UnsupportedOSPlatform("windows")]
+ SIGTSTP = -10
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Runtime.InteropServices
+{
+ /// <summary>
+ /// Provides data for a <see cref="PosixSignalRegistration"/> event.
+ /// </summary>
+ public sealed class PosixSignalContext
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PosixSignalContext"/> class.
+ /// </summary>
+ public PosixSignalContext(PosixSignal signal) => Signal = signal;
+
+ /// <summary>
+ /// Gets the signal that occurred.
+ /// </summary>
+ public PosixSignal Signal { get; internal set; }
+
+ /// <summary>
+ /// Gets or sets a value that indicates whether to cancel the default handling of the signal. The default is <see langword="false"/>.
+ /// </summary>
+ public bool Cancel { get; set; }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace System.Runtime.InteropServices
+{
+ public sealed partial class PosixSignalRegistration
+ {
+ private PosixSignalRegistration() { }
+
+ [DynamicDependency("#ctor")] // Prevent the private ctor and the IDisposable implementation from getting linked away
+ public static partial PosixSignalRegistration Create(PosixSignal signal, Action<PosixSignalContext> handler)
+ {
+ if (handler is null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ throw new PlatformNotSupportedException();
+ }
+
+ public partial void Dispose() { }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Threading;
+
+namespace System.Runtime.InteropServices
+{
+ public sealed partial class PosixSignalRegistration
+ {
+ private static volatile bool s_initialized;
+ private static readonly Dictionary<int, List<WeakReference<PosixSignalRegistration>?>> s_registrations = new();
+
+ private readonly Action<PosixSignalContext> _handler;
+ private readonly PosixSignal _signal;
+ private readonly int _signo;
+ private bool _registered;
+ private readonly object _gate = new object();
+
+ private PosixSignalRegistration(PosixSignal signal, int signo, Action<PosixSignalContext> handler)
+ {
+ _signal = signal;
+ _signo = signo;
+ _handler = handler;
+ }
+
+ public static partial PosixSignalRegistration Create(PosixSignal signal, Action<PosixSignalContext> handler)
+ {
+ if (handler == null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ int signo = Interop.Sys.GetPlatformSignalNumber(signal);
+ if (signo == 0)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ PosixSignalRegistration registration = new PosixSignalRegistration(signal, signo, handler);
+ registration.Register();
+ return registration;
+ }
+
+ private unsafe void Register()
+ {
+ if (!s_initialized)
+ {
+ if (!Interop.Sys.InitializeTerminalAndSignalHandling())
+ {
+ // We can't use Win32Exception because that causes a cycle with
+ // Microsoft.Win32.Primitives.
+ Interop.CheckIo(-1);
+ }
+
+ Interop.Sys.SetPosixSignalHandler(&OnPosixSignal);
+ s_initialized = true;
+ }
+
+ lock (s_registrations)
+ {
+ if (!s_registrations.TryGetValue(_signo, out List<WeakReference<PosixSignalRegistration>?>? signalRegistrations))
+ {
+ signalRegistrations = new List<WeakReference<PosixSignalRegistration>?>();
+ s_registrations.Add(_signo, signalRegistrations);
+ }
+
+ if (signalRegistrations.Count == 0)
+ {
+ if (!Interop.Sys.EnablePosixSignalHandling(_signo))
+ {
+ // We can't use Win32Exception because that causes a cycle with
+ // Microsoft.Win32.Primitives.
+ Interop.CheckIo(-1);
+ }
+ }
+
+ signalRegistrations.Add(new WeakReference<PosixSignalRegistration>(this));
+ }
+
+ _registered = true;
+ }
+
+ private bool CallHandler(PosixSignalContext context)
+ {
+ lock (_gate)
+ {
+ if (_registered)
+ {
+ _handler(context);
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ private static int OnPosixSignal(int signo, PosixSignal signal)
+ {
+ PosixSignalRegistration?[]? registrations = GetRegistrations(signo);
+ if (registrations != null)
+ {
+ // This is called on the native signal handling thread. We need to move to another thread so
+ // signal handling is not blocked. Otherwise we may get deadlocked when the handler depends
+ // on work triggered from the signal handling thread.
+
+ // For terminate/interrupt signals we use a dedicated Thread
+ // in case the ThreadPool is saturated.
+ bool useDedicatedThread = signal == PosixSignal.SIGINT ||
+ signal == PosixSignal.SIGQUIT ||
+ signal == PosixSignal.SIGTERM;
+
+ if (useDedicatedThread)
+ {
+ Thread handlerThread = new Thread(HandleSignal)
+ {
+ IsBackground = true,
+ Name = ".NET Signal Handler"
+ };
+ handlerThread.UnsafeStart((signo, registrations));
+ }
+ else
+ {
+ ThreadPool.UnsafeQueueUserWorkItem(HandleSignal, (signo, registrations));
+ }
+
+ return 1;
+ }
+
+ return 0;
+ }
+
+ private static PosixSignalRegistration?[]? GetRegistrations(int signo)
+ {
+ lock (s_registrations)
+ {
+ if (s_registrations.TryGetValue(signo, out List<WeakReference<PosixSignalRegistration>?>? signalRegistrations))
+ {
+ if (signalRegistrations.Count != 0)
+ {
+ var registrations = new PosixSignalRegistration?[signalRegistrations.Count];
+ bool hasRegistrations = false;
+ bool pruneWeakReferences = false;
+
+ for (int i = 0; i < signalRegistrations.Count; i++)
+ {
+ if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration))
+ {
+ registrations[i] = registration;
+ hasRegistrations = true;
+ }
+ else
+ {
+ // WeakReference no longer holds an object. PosixSignalRegistration got finalized.
+ signalRegistrations[i] = null;
+ pruneWeakReferences = true;
+ }
+ }
+
+ if (pruneWeakReferences)
+ {
+ signalRegistrations.RemoveAll(item => item is null);
+ }
+
+ if (hasRegistrations)
+ {
+ return registrations;
+ }
+ else
+ {
+ Interop.Sys.DisablePosixSignalHandling(signo);
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ private static void HandleSignal(object? state)
+ {
+ HandleSignal(((int, PosixSignalRegistration?[]))state!);
+ }
+
+ private static void HandleSignal((int signo, PosixSignalRegistration?[]? registrations) state)
+ {
+ do
+ {
+ bool handlersCalled = false;
+ if (state.registrations != null)
+ {
+ PosixSignalContext ctx = new(0);
+ foreach (PosixSignalRegistration? registration in state.registrations)
+ {
+ if (registration != null)
+ {
+ // Different values for PosixSignal map to the same signo.
+ // Match the PosixSignal value used when registering.
+ ctx.Signal = registration._signal;
+ if (registration.CallHandler(ctx))
+ {
+ handlersCalled = true;
+ }
+ }
+ }
+
+ if (ctx.Cancel)
+ {
+ return;
+ }
+ }
+
+ if (Interop.Sys.HandleNonCanceledPosixSignal(state.signo, handlersCalled ? 0 : 1))
+ {
+ return;
+ }
+
+ // HandleNonCanceledPosixSignal returns false when handlers got registered.
+ state.registrations = GetRegistrations(state.signo);
+ } while (true);
+ }
+
+ public partial void Dispose()
+ {
+ if (_registered)
+ {
+ lock (s_registrations)
+ {
+ List<WeakReference<PosixSignalRegistration>?> signalRegistrations = s_registrations[_signo];
+ bool pruneWeakReferences = false;
+ for (int i = 0; i < signalRegistrations.Count; i++)
+ {
+ if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration))
+ {
+ if (ReferenceEquals(this, registration))
+ {
+ signalRegistrations.RemoveAt(i);
+ break;
+ }
+ }
+ else
+ {
+ // WeakReference no longer holds an object. PosixSignalRegistration got finalized.
+ signalRegistrations[i] = null;
+ pruneWeakReferences = true;
+ }
+ }
+
+ if (pruneWeakReferences)
+ {
+ signalRegistrations.RemoveAll(item => item is null);
+ }
+
+ if (signalRegistrations.Count == 0)
+ {
+ Interop.Sys.DisablePosixSignalHandling(_signo);
+ }
+ }
+
+ // Synchronize with _handler invocations.
+ lock (_gate)
+ {
+ _registered = false;
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.IO;
+
+namespace System.Runtime.InteropServices
+{
+ public sealed unsafe partial class PosixSignalRegistration
+ {
+ private static readonly HashSet<Token> s_handlers = new();
+
+ private Token? _token;
+
+ private PosixSignalRegistration(Token token) => _token = token;
+
+ private static object SyncObj => s_handlers;
+
+ public static partial PosixSignalRegistration Create(PosixSignal signal, Action<PosixSignalContext> handler)
+ {
+ if (handler is null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ lock (SyncObj)
+ {
+ switch (signal)
+ {
+ case PosixSignal.SIGINT:
+ case PosixSignal.SIGQUIT:
+ case PosixSignal.SIGTERM:
+ case PosixSignal.SIGHUP:
+ break;
+
+ default:
+ throw new PlatformNotSupportedException();
+ }
+
+ if (s_handlers.Count == 0 &&
+ !Interop.Kernel32.SetConsoleCtrlHandler(&HandlerRoutine, Add: true))
+ {
+ throw Win32Marshal.GetExceptionForLastWin32Error();
+ }
+
+ var token = new Token(signal, handler);
+ s_handlers.Add(token);
+ return new PosixSignalRegistration(token);
+ }
+ }
+
+ public partial void Dispose()
+ {
+ lock (SyncObj)
+ {
+ if (_token is Token token)
+ {
+ _token = null;
+
+ s_handlers.Remove(token);
+ if (s_handlers.Count == 0 &&
+ !Interop.Kernel32.SetConsoleCtrlHandler(&HandlerRoutine, Add: false))
+ {
+ throw Win32Marshal.GetExceptionForLastWin32Error();
+ }
+ }
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ private static Interop.BOOL HandlerRoutine(int dwCtrlType)
+ {
+ PosixSignal signal;
+ switch (dwCtrlType)
+ {
+ case Interop.Kernel32.CTRL_C_EVENT:
+ signal = PosixSignal.SIGINT;
+ break;
+
+ case Interop.Kernel32.CTRL_BREAK_EVENT:
+ signal = PosixSignal.SIGQUIT;
+ break;
+
+ case Interop.Kernel32.CTRL_SHUTDOWN_EVENT:
+ signal = PosixSignal.SIGTERM;
+ break;
+
+ case Interop.Kernel32.CTRL_CLOSE_EVENT:
+ signal = PosixSignal.SIGHUP;
+ break;
+
+ default:
+ return Interop.BOOL.FALSE;
+ }
+
+ List<Token>? tokens = null;
+ lock (SyncObj)
+ {
+ foreach (Token token in s_handlers)
+ {
+ if (token.Signal == signal)
+ {
+ (tokens ??= new()).Add(token);
+ }
+ }
+ }
+
+ if (tokens is null)
+ {
+ return Interop.BOOL.FALSE;
+ }
+
+ var context = new PosixSignalContext(signal);
+ foreach (Token handler in tokens)
+ {
+ handler.Handler(context);
+ }
+
+ return context.Cancel ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
+ }
+
+ private sealed class Token
+ {
+ public Token(PosixSignal signal, Action<PosixSignalContext> handler)
+ {
+ Signal = signal;
+ Handler = handler;
+ }
+
+ public PosixSignal Signal { get; }
+ public Action<PosixSignalContext> Handler { get; }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using System.Runtime.Versioning;
+
+namespace System.Runtime.InteropServices
+{
+ /// <summary>Handles a <see cref="PosixSignal"/>.</summary>
+ public sealed partial class PosixSignalRegistration : IDisposable
+ {
+ /// <summary>Registers a <paramref name="handler"/> that is invoked when the <paramref name="signal"/> occurs.</summary>
+ /// <param name="signal">The signal to register for.</param>
+ /// <param name="handler">The handler that gets invoked.</param>
+ /// <returns>A <see cref="PosixSignalRegistration"/> instance that can be disposed to unregister the handler.</returns>
+ /// <exception cref="ArgumentNullException"><paramref name="handler"/> is <see langword="null"/>.</exception>
+ /// <exception cref="PlatformNotSupportedException"><paramref name="signal"/> is not supported by the platform.</exception>
+ /// <exception cref="IOException">An error occurred while setting up the signal handling or while installing the handler for the specified signal.</exception>
+ /// <remarks>
+ /// Raw values can be provided for <paramref name="signal"/> on Unix by casting them to <see cref="PosixSignal"/>.
+ /// Default handling of the signal can be canceled through <see cref="PosixSignalContext.Cancel"/>.
+ /// <see cref="PosixSignal.SIGINT"/> and <see cref="PosixSignal.SIGQUIT"/> can be canceled on both
+ /// Windows and on Unix platforms; <see cref="PosixSignal.SIGTERM"/> can only be canceled on Unix.
+ /// On Unix, terminal configuration can be canceled for <see cref="PosixSignal.SIGCHLD"/> and <see cref="PosixSignal.SIGCONT"/>.
+ /// </remarks>
+ [UnsupportedOSPlatform("android")]
+ [UnsupportedOSPlatform("browser")]
+ [UnsupportedOSPlatform("ios")]
+ [UnsupportedOSPlatform("maccatalyst")]
+ [UnsupportedOSPlatform("tvos")]
+ public static partial PosixSignalRegistration Create(PosixSignal signal, Action<PosixSignalContext> handler);
+
+ /// <summary>Unregister the handler.</summary>
+ public partial void Dispose();
+
+ ~PosixSignalRegistration() => Dispose();
+ }
+}
<data name="ArgumentOutOfRange_FileLengthTooBig" xml:space="preserve">
<value>Specified file length was too large for the file system.</value>
</data>
- <data name="IO_AlreadyExists_Name" xml:space="preserve">
- <value>Cannot create '{0}' because a file or directory with the same name already exists.</value>
- </data>
</root>
<PropertyGroup>
<IsPartialFacadeAssembly>true</IsPartialFacadeAssembly>
<Nullable>enable</Nullable>
- <TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser</TargetFrameworks>
+ <TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Runtime\CompilerServices\IDispatchConstantAttribute.cs" />
<Compile Include="System\Runtime\InteropServices\ImportedFromTypeLibAttribute.cs" />
<Compile Include="System\Runtime\InteropServices\ManagedToNativeComInteropStubAttribute.cs" />
<Compile Include="System\Runtime\InteropServices\PrimaryInteropAssemblyAttribute.cs" />
- <Compile Include="System\Runtime\InteropServices\PosixSignal.cs" />
- <Compile Include="System\Runtime\InteropServices\PosixSignalContext.cs" />
- <Compile Include="System\Runtime\InteropServices\PosixSignalRegistration.cs" />
<Compile Include="System\Runtime\InteropServices\RegistrationClassContext.cs" />
<Compile Include="System\Runtime\InteropServices\RegistrationConnectionType.cs" />
<Compile Include="System\Runtime\InteropServices\RuntimeEnvironment.cs" />
<Compile Include="System\Security\SecureStringMarshal.cs" />
<Compile Include="$(CommonPath)System\Obsoletions.cs" Link="Common\System\Obsoletions.cs" />
</ItemGroup>
- <ItemGroup Condition="'$(TargetsUnix)' == 'true'">
- <Compile Include="System\Runtime\InteropServices\PosixSignalRegistration.Unix.cs" />
- <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.PosixSignal.cs" Link="Common\Interop\Unix\Interop.PosixSignal.cs" />
- <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.InitializeTerminalAndSignalHandling.cs" Link="Common\Interop\Unix\Interop.InitializeTerminalAndSignalHandling.cs" />
- <Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs" Link="Common\Interop\Unix\Interop.Libraries.cs" />
- <Compile Include="$(CommonPath)Interop\Unix\Interop.Errors.cs" Link="Common\CoreLib\Interop\Unix\Interop.Errors.cs" />
- <Compile Include="$(CommonPath)Interop\Unix\Interop.IOErrors.cs" Link="Common\Interop\Unix\Interop.IOErrors.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(TargetsWindows)' == 'true'">
- <Compile Include="System\Runtime\InteropServices\PosixSignalRegistration.Windows.cs" />
- <Compile Include="$(CommonPath)System\IO\Win32Marshal.cs" Link="Common\System\IO\Win32Marshal.cs" />
- <Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs" Link="Common\Interop\Windows\Interop.BOOL.cs" />
- <Compile Include="$(CommonPath)Interop\Windows\Interop.Errors.cs" Link="Common\Interop\Windows\Interop.Errors.cs" />
- <Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs" Link="Common\Interop\Windows\Interop.Libraries.cs" />
- <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.FormatMessage.cs" Link="Common\Interop\Windows\Interop.FormatMessage.cs" />
- <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetConsoleCtrlHandler.cs" Link="Common\Interop\Windows\Interop.SetConsoleCtrlHandler.cs" />
- </ItemGroup>
- <ItemGroup Condition="'$(TargetsBrowser)' == 'true'">
- <Compile Include="System\Runtime\InteropServices\PosixSignalRegistration.Unsupported.cs" />
- </ItemGroup>
<ItemGroup>
<ProjectReference Include="$(CoreLibProject)" />
</ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Runtime.Versioning;
-
-namespace System.Runtime.InteropServices
-{
- /// <summary>Specifies a POSIX signal number.</summary>
- public enum PosixSignal
- {
- /// <summary>Hangup</summary>
- SIGHUP = -1,
-
- /// <summary>Interrupt</summary>
- SIGINT = -2,
-
- /// <summary>Quit</summary>
- SIGQUIT = -3,
-
- /// <summary>Termination</summary>
- SIGTERM = -4,
-
- /// <summary>Child stopped</summary>
- [UnsupportedOSPlatform("windows")]
- SIGCHLD = -5,
-
- /// <summary>Continue if stopped</summary>
- [UnsupportedOSPlatform("windows")]
- SIGCONT = -6,
-
- /// <summary>Window resized</summary>
- [UnsupportedOSPlatform("windows")]
- SIGWINCH = -7,
-
- /// <summary>Terminal input for background process</summary>
- [UnsupportedOSPlatform("windows")]
- SIGTTIN = -8,
-
- /// <summary>Terminal output for background process</summary>
- [UnsupportedOSPlatform("windows")]
- SIGTTOU = -9,
-
- /// <summary>Stop typed at terminal</summary>
- [UnsupportedOSPlatform("windows")]
- SIGTSTP = -10
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System.Runtime.InteropServices
-{
- /// <summary>
- /// Provides data for a <see cref="PosixSignalRegistration"/> event.
- /// </summary>
- public sealed class PosixSignalContext
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="PosixSignalContext"/> class.
- /// </summary>
- public PosixSignalContext(PosixSignal signal) => Signal = signal;
-
- /// <summary>
- /// Gets the signal that occurred.
- /// </summary>
- public PosixSignal Signal { get; internal set; }
-
- /// <summary>
- /// Gets or sets a value that indicates whether to cancel the default handling of the signal. The default is <see langword="false"/>.
- /// </summary>
- public bool Cancel { get; set; }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using System.Threading;
-
-namespace System.Runtime.InteropServices
-{
- public sealed partial class PosixSignalRegistration
- {
- private static volatile bool s_initialized;
- private static readonly Dictionary<int, List<WeakReference<PosixSignalRegistration>?>> s_registrations = new();
-
- private readonly Action<PosixSignalContext> _handler;
- private readonly PosixSignal _signal;
- private readonly int _signo;
- private bool _registered;
- private readonly object _gate = new object();
-
- private PosixSignalRegistration(PosixSignal signal, int signo, Action<PosixSignalContext> handler)
- {
- _signal = signal;
- _signo = signo;
- _handler = handler;
- }
-
- public static partial PosixSignalRegistration Create(PosixSignal signal, Action<PosixSignalContext> handler)
- {
- if (handler == null)
- {
- throw new ArgumentNullException(nameof(handler));
- }
-
- int signo = Interop.Sys.GetPlatformSignalNumber(signal);
- if (signo == 0)
- {
- throw new PlatformNotSupportedException();
- }
-
- PosixSignalRegistration registration = new PosixSignalRegistration(signal, signo, handler);
- registration.Register();
- return registration;
- }
-
- private unsafe void Register()
- {
- if (!s_initialized)
- {
- if (!Interop.Sys.InitializeTerminalAndSignalHandling())
- {
- // We can't use Win32Exception because that causes a cycle with
- // Microsoft.Win32.Primitives.
- Interop.CheckIo(-1);
- }
-
- Interop.Sys.SetPosixSignalHandler(&OnPosixSignal);
- s_initialized = true;
- }
-
- lock (s_registrations)
- {
- if (!s_registrations.TryGetValue(_signo, out List<WeakReference<PosixSignalRegistration>?>? signalRegistrations))
- {
- signalRegistrations = new List<WeakReference<PosixSignalRegistration>?>();
- s_registrations.Add(_signo, signalRegistrations);
- }
-
- if (signalRegistrations.Count == 0)
- {
- if (!Interop.Sys.EnablePosixSignalHandling(_signo))
- {
- // We can't use Win32Exception because that causes a cycle with
- // Microsoft.Win32.Primitives.
- Interop.CheckIo(-1);
- }
- }
-
- signalRegistrations.Add(new WeakReference<PosixSignalRegistration>(this));
- }
-
- _registered = true;
- }
-
- private bool CallHandler(PosixSignalContext context)
- {
- lock (_gate)
- {
- if (_registered)
- {
- _handler(context);
- return true;
- }
-
- return false;
- }
- }
-
- [UnmanagedCallersOnly]
- private static int OnPosixSignal(int signo, PosixSignal signal)
- {
- PosixSignalRegistration?[]? registrations = GetRegistrations(signo);
- if (registrations != null)
- {
- // This is called on the native signal handling thread. We need to move to another thread so
- // signal handling is not blocked. Otherwise we may get deadlocked when the handler depends
- // on work triggered from the signal handling thread.
-
- // For terminate/interrupt signals we use a dedicated Thread
- // in case the ThreadPool is saturated.
- bool useDedicatedThread = signal == PosixSignal.SIGINT ||
- signal == PosixSignal.SIGQUIT ||
- signal == PosixSignal.SIGTERM;
-
- if (useDedicatedThread)
- {
- Thread handlerThread = new Thread(HandleSignal)
- {
- IsBackground = true,
- Name = ".NET Signal Handler"
- };
- handlerThread.UnsafeStart((signo, registrations));
- }
- else
- {
- ThreadPool.UnsafeQueueUserWorkItem(HandleSignal, (signo, registrations));
- }
-
- return 1;
- }
-
- return 0;
- }
-
- private static PosixSignalRegistration?[]? GetRegistrations(int signo)
- {
- lock (s_registrations)
- {
- if (s_registrations.TryGetValue(signo, out List<WeakReference<PosixSignalRegistration>?>? signalRegistrations))
- {
- if (signalRegistrations.Count != 0)
- {
- var registrations = new PosixSignalRegistration?[signalRegistrations.Count];
- bool hasRegistrations = false;
- bool pruneWeakReferences = false;
-
- for (int i = 0; i < signalRegistrations.Count; i++)
- {
- if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration))
- {
- registrations[i] = registration;
- hasRegistrations = true;
- }
- else
- {
- // WeakReference no longer holds an object. PosixSignalRegistration got finalized.
- signalRegistrations[i] = null;
- pruneWeakReferences = true;
- }
- }
-
- if (pruneWeakReferences)
- {
- signalRegistrations.RemoveAll(item => item is null);
- }
-
- if (hasRegistrations)
- {
- return registrations;
- }
- else
- {
- Interop.Sys.DisablePosixSignalHandling(signo);
- }
- }
- }
- return null;
- }
- }
-
- private static void HandleSignal(object? state)
- {
- HandleSignal(((int, PosixSignalRegistration?[]))state!);
- }
-
- private static void HandleSignal((int signo, PosixSignalRegistration?[]? registrations) state)
- {
- do
- {
- bool handlersCalled = false;
- if (state.registrations != null)
- {
- PosixSignalContext ctx = new(0);
- foreach (PosixSignalRegistration? registration in state.registrations)
- {
- if (registration != null)
- {
- // Different values for PosixSignal map to the same signo.
- // Match the PosixSignal value used when registering.
- ctx.Signal = registration._signal;
- if (registration.CallHandler(ctx))
- {
- handlersCalled = true;
- }
- }
- }
-
- if (ctx.Cancel)
- {
- return;
- }
- }
-
- if (Interop.Sys.HandleNonCanceledPosixSignal(state.signo, handlersCalled ? 0 : 1))
- {
- return;
- }
-
- // HandleNonCanceledPosixSignal returns false when handlers got registered.
- state.registrations = GetRegistrations(state.signo);
- } while (true);
- }
-
- public partial void Dispose()
- {
- if (_registered)
- {
- lock (s_registrations)
- {
- List<WeakReference<PosixSignalRegistration>?> signalRegistrations = s_registrations[_signo];
- bool pruneWeakReferences = false;
- for (int i = 0; i < signalRegistrations.Count; i++)
- {
- if (signalRegistrations[i]!.TryGetTarget(out PosixSignalRegistration? registration))
- {
- if (ReferenceEquals(this, registration))
- {
- signalRegistrations.RemoveAt(i);
- break;
- }
- }
- else
- {
- // WeakReference no longer holds an object. PosixSignalRegistration got finalized.
- signalRegistrations[i] = null;
- pruneWeakReferences = true;
- }
- }
-
- if (pruneWeakReferences)
- {
- signalRegistrations.RemoveAll(item => item is null);
- }
-
- if (signalRegistrations.Count == 0)
- {
- Interop.Sys.DisablePosixSignalHandling(_signo);
- }
- }
-
- // Synchronize with _handler invocations.
- lock (_gate)
- {
- _registered = false;
- }
- }
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System.Runtime.InteropServices
-{
- public sealed partial class PosixSignalRegistration
- {
- private PosixSignalRegistration() { }
-
- public static partial PosixSignalRegistration Create(PosixSignal signal, Action<PosixSignalContext> handler)
- {
- if (handler is null)
- {
- throw new ArgumentNullException(nameof(handler));
- }
-
- throw new PlatformNotSupportedException();
- }
-
- public partial void Dispose() { }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using System.IO;
-
-namespace System.Runtime.InteropServices
-{
- public sealed unsafe partial class PosixSignalRegistration
- {
- private static readonly HashSet<Token> s_handlers = new();
-
- private Token? _token;
-
- private PosixSignalRegistration(Token token) => _token = token;
-
- private static object SyncObj => s_handlers;
-
- public static partial PosixSignalRegistration Create(PosixSignal signal, Action<PosixSignalContext> handler)
- {
- if (handler is null)
- {
- throw new ArgumentNullException(nameof(handler));
- }
-
- lock (SyncObj)
- {
- switch (signal)
- {
- case PosixSignal.SIGINT:
- case PosixSignal.SIGQUIT:
- case PosixSignal.SIGTERM:
- case PosixSignal.SIGHUP:
- break;
-
- default:
- throw new PlatformNotSupportedException();
- }
-
- if (s_handlers.Count == 0 &&
- !Interop.Kernel32.SetConsoleCtrlHandler(&HandlerRoutine, Add: true))
- {
- throw Win32Marshal.GetExceptionForLastWin32Error();
- }
-
- var token = new Token(signal, handler);
- s_handlers.Add(token);
- return new PosixSignalRegistration(token);
- }
- }
-
- public partial void Dispose()
- {
- lock (SyncObj)
- {
- if (_token is Token token)
- {
- _token = null;
-
- s_handlers.Remove(token);
- if (s_handlers.Count == 0 &&
- !Interop.Kernel32.SetConsoleCtrlHandler(&HandlerRoutine, Add: false))
- {
- throw Win32Marshal.GetExceptionForLastWin32Error();
- }
- }
- }
- }
-
- [UnmanagedCallersOnly]
- private static Interop.BOOL HandlerRoutine(int dwCtrlType)
- {
- PosixSignal signal;
- switch (dwCtrlType)
- {
- case Interop.Kernel32.CTRL_C_EVENT:
- signal = PosixSignal.SIGINT;
- break;
-
- case Interop.Kernel32.CTRL_BREAK_EVENT:
- signal = PosixSignal.SIGQUIT;
- break;
-
- case Interop.Kernel32.CTRL_SHUTDOWN_EVENT:
- signal = PosixSignal.SIGTERM;
- break;
-
- case Interop.Kernel32.CTRL_CLOSE_EVENT:
- signal = PosixSignal.SIGHUP;
- break;
-
- default:
- return Interop.BOOL.FALSE;
- }
-
- List<Token>? tokens = null;
- lock (SyncObj)
- {
- foreach (Token token in s_handlers)
- {
- if (token.Signal == signal)
- {
- (tokens ??= new()).Add(token);
- }
- }
- }
-
- if (tokens is null)
- {
- return Interop.BOOL.FALSE;
- }
-
- var context = new PosixSignalContext(signal);
- foreach (Token handler in tokens)
- {
- handler.Handler(context);
- }
-
- return context.Cancel ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
- }
-
- private sealed class Token
- {
- public Token(PosixSignal signal, Action<PosixSignalContext> handler)
- {
- Signal = signal;
- Handler = handler;
- }
-
- public PosixSignal Signal { get; }
- public Action<PosixSignalContext> Handler { get; }
- }
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.IO;
-using System.Runtime.Versioning;
-
-namespace System.Runtime.InteropServices
-{
- /// <summary>Handles a <see cref="PosixSignal"/>.</summary>
- public sealed partial class PosixSignalRegistration : IDisposable
- {
- /// <summary>Registers a <paramref name="handler"/> that is invoked when the <paramref name="signal"/> occurs.</summary>
- /// <param name="signal">The signal to register for.</param>
- /// <param name="handler">The handler that gets invoked.</param>
- /// <returns>A <see cref="PosixSignalRegistration"/> instance that can be disposed to unregister the handler.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="handler"/> is <see langword="null"/>.</exception>
- /// <exception cref="PlatformNotSupportedException"><paramref name="signal"/> is not supported by the platform.</exception>
- /// <exception cref="IOException">An error occurred while setting up the signal handling or while installing the handler for the specified signal.</exception>
- /// <remarks>
- /// Raw values can be provided for <paramref name="signal"/> on Unix by casting them to <see cref="PosixSignal"/>.
- /// Default handling of the signal can be canceled through <see cref="PosixSignalContext.Cancel"/>.
- /// <see cref="PosixSignal.SIGINT"/> and <see cref="PosixSignal.SIGQUIT"/> can be canceled on both
- /// Windows and on Unix platforms; <see cref="PosixSignal.SIGTERM"/> can only be canceled on Unix.
- /// On Unix, terminal configuration can be canceled for <see cref="PosixSignal.SIGCHLD"/> and <see cref="PosixSignal.SIGCONT"/>.
- /// </remarks>
- [UnsupportedOSPlatform("android")]
- [UnsupportedOSPlatform("browser")]
- [UnsupportedOSPlatform("ios")]
- [UnsupportedOSPlatform("maccatalyst")]
- [UnsupportedOSPlatform("tvos")]
- public static partial PosixSignalRegistration Create(PosixSignal signal, Action<PosixSignalContext> handler);
-
- /// <summary>Unregister the handler.</summary>
- public partial void Dispose();
-
- ~PosixSignalRegistration() => Dispose();
- }
-}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser</TargetFrameworks>
+ <TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix</TargetFrameworks>
<TestRuntime>true</TestRuntime>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
</PropertyGroup>
<Compile Include="System\Runtime\InteropServices\PosixSignalRegistrationTests.Unix.cs" />
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs" Link="Common\Interop\Unix\Interop.Libraries.cs" />
</ItemGroup>
- <ItemGroup Condition="'$(TargetsBrowser)' == 'true'">
- <Compile Include="System\Runtime\InteropServices\PosixSignalRegistrationTests.Browser.cs" />
- </ItemGroup>
</Project>
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Collections.Generic;
-
-namespace System.Tests
-{
- public partial class PosixSignalRegistrationTests
- {
- public static IEnumerable<object[]> UninstallableSignals() => Enumerable.Empty<object[]>();
-
- public static IEnumerable<object[]> SupportedSignals() => Enumerable.Empty<object[]>();
-
- public static IEnumerable<object[]> UnsupportedSignals()
- {
- foreach (PosixSignal signal in Enum.GetValues<PosixSignal>())
- {
- yield return new object[] { signal };
- }
-
- yield return new object[] { 0 };
- yield return new object[] { 3 };
- yield return new object[] { -1000 };
- yield return new object[] { 1000 };
- }
- }
-}
{
public static IEnumerable<object[]> UninstallableSignals()
{
- yield return new object[] { (PosixSignal)9 };
+ if (PlatformDetection.IsNotMobile)
+ {
+ yield return new object[] { (PosixSignal)9 };
+ }
}
public static IEnumerable<object[]> SupportedSignals()
{
- foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal)))
- yield return new object[] { value };
+ if (PlatformDetection.IsNotMobile)
+ {
+ foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal)))
+ yield return new object[] { value };
+ }
}
public static IEnumerable<object[]> UnsupportedSignals()
{
+ if (PlatformDetection.IsMobile)
+ {
+ foreach (PosixSignal value in Enum.GetValues(typeof(PosixSignal)))
+ yield return new object[] { value };
+ }
+
yield return new object[] { 0 };
yield return new object[] { -1000 };
yield return new object[] { 1000 };
}
- [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public static bool NotMobileAndRemoteExecutable => PlatformDetection.IsNotMobile && RemoteExecutor.IsSupported;
+
+ [ConditionalTheory(nameof(NotMobileAndRemoteExecutable))]
[MemberData(nameof(SupportedSignals))]
public void SignalHandlerCalledForKnownSignals(PosixSignal s)
{
}, s.ToString()).Dispose();
}
- [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ [ConditionalTheory(nameof(NotMobileAndRemoteExecutable))]
[MemberData(nameof(PosixSignalAsRawValues))]
public void SignalHandlerCalledForRawSignals(PosixSignal s)
{
}, s.ToString()).Dispose();
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))]
public void SignalHandlerWorksForSecondRegistration()
{
PosixSignal signal = PosixSignal.SIGCONT;
}
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))]
public void SignalHandlerNotCalledWhenDisposed()
{
PosixSignal signal = PosixSignal.SIGCONT;
Thread.Sleep(100);
}
- [Fact]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile))]
public void SignalHandlerNotCalledWhenFinalized()
{
PosixSignal signal = PosixSignal.SIGCONT;
}
}
- [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ [ConditionalTheory(nameof(NotMobileAndRemoteExecutable))]
[InlineData(PosixSignal.SIGINT, true, 0)]
[InlineData(PosixSignal.SIGINT, false, 130)]
[InlineData(PosixSignal.SIGTERM, true, 0)]