diff --git a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.DotNet.verified.txt b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.DotNet.verified.txt index 36bf7bcfab8..d9c4915f5f7 100644 --- a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.DotNet.verified.txt +++ b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.DotNet.verified.txt @@ -579,30 +579,37 @@ namespace Akka.Actor public class ActorSystemTerminateReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } public class ClrExitReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } public class ClusterDowningReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } public class ClusterJoinUnsuccessfulReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } public class ClusterLeavingReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } - public class Reason + public abstract class Reason { protected Reason() { } + public abstract int ExitCode { get; } } public class UnknownReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } } public sealed class CoordinatedShutdownExtension : Akka.Actor.ExtensionIdProvider diff --git a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Net.verified.txt b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Net.verified.txt index 7c2892fc6ce..c49fa57192b 100644 --- a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Net.verified.txt +++ b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveCore.Net.verified.txt @@ -579,30 +579,37 @@ namespace Akka.Actor public class ActorSystemTerminateReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } public class ClrExitReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } public class ClusterDowningReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } public class ClusterJoinUnsuccessfulReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } public class ClusterLeavingReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } - public class Reason + public abstract class Reason { protected Reason() { } + public abstract int ExitCode { get; } } public class UnknownReason : Akka.Actor.CoordinatedShutdown.Reason { public static readonly Akka.Actor.CoordinatedShutdown.Reason Instance; + public override int ExitCode { get; } } } public sealed class CoordinatedShutdownExtension : Akka.Actor.ExtensionIdProvider diff --git a/src/core/Akka.Tests/Actor/CoordinatedShutdownSpec.cs b/src/core/Akka.Tests/Actor/CoordinatedShutdownSpec.cs index 648913c1c80..97091dc4d5c 100644 --- a/src/core/Akka.Tests/Actor/CoordinatedShutdownSpec.cs +++ b/src/core/Akka.Tests/Actor/CoordinatedShutdownSpec.cs @@ -53,6 +53,7 @@ private List CheckTopologicalSort(Dictionary phases) private class CustomReason : CoordinatedShutdown.Reason { + public override int ExitCode => 999; } private static CoordinatedShutdown.Reason customReason = new CustomReason(); diff --git a/src/core/Akka/Actor/CoordinatedShutdown.cs b/src/core/Akka/Actor/CoordinatedShutdown.cs index 403e92bac8f..f0008de8981 100644 --- a/src/core/Akka/Actor/CoordinatedShutdown.cs +++ b/src/core/Akka/Actor/CoordinatedShutdown.cs @@ -156,7 +156,23 @@ public static CoordinatedShutdown Get(ActorSystem sys) public const string PhaseBeforeActorSystemTerminate = "before-actor-system-terminate"; public const string PhaseActorSystemTerminate = "actor-system-terminate"; - + /// + /// Common exit codes supported out of the box. + /// Note: When adding new exit codes, make sure that the exit code adheres + /// to the Linux standard. + /// See: https://manpages.ubuntu.com/manpages/lunar/man3/sysexits.h.3head.html + /// See: https://manpages.ubuntu.com/manpages/noble/man3/EXIT_SUCCESS.3const.html + /// + internal enum CommonExitCodes + { + Ok = 0, + UnknownReason = 1, + // Exit code 2 is reserved for Linux Bash for "Incorrect Usage" + ClusterDowned = 3, + ClusterJoinFailed = 4, + // Exit codes 64-78 is reserved by Linux sysexits.h + // Exit codes 126 and above is reserved by Linux shell + } /// /// Reason for the shutdown, which can be used by tasks in case they need to do @@ -164,8 +180,9 @@ public static CoordinatedShutdown Get(ActorSystem sys) /// predefined reasons, but external libraries applications may also define /// other reasons. /// - public class Reason + public abstract class Reason { + public abstract int ExitCode { get; } protected Reason() { @@ -179,6 +196,8 @@ public class UnknownReason : Reason { public static readonly Reason Instance = new UnknownReason(); + public override int ExitCode => (int)CommonExitCodes.UnknownReason; + private UnknownReason() { @@ -192,6 +211,8 @@ public class ActorSystemTerminateReason : Reason { public static readonly Reason Instance = new ActorSystemTerminateReason(); + public override int ExitCode => (int)CommonExitCodes.Ok; + private ActorSystemTerminateReason() { @@ -205,6 +226,8 @@ public class ClrExitReason : Reason { public static readonly Reason Instance = new ClrExitReason(); + public override int ExitCode => (int)CommonExitCodes.Ok; + private ClrExitReason() { @@ -219,6 +242,8 @@ public class ClusterDowningReason : Reason { public static readonly Reason Instance = new ClusterDowningReason(); + public override int ExitCode => (int)CommonExitCodes.ClusterDowned; + private ClusterDowningReason() { @@ -233,6 +258,8 @@ public class ClusterLeavingReason : Reason { public static readonly Reason Instance = new ClusterLeavingReason(); + public override int ExitCode => (int)CommonExitCodes.Ok; + private ClusterLeavingReason() { @@ -245,6 +272,9 @@ private ClusterLeavingReason() public class ClusterJoinUnsuccessfulReason : Reason { public static readonly Reason Instance = new ClusterJoinUnsuccessfulReason(); + + public override int ExitCode => (int)CommonExitCodes.ClusterJoinFailed; + private ClusterJoinUnsuccessfulReason() { } } @@ -646,7 +676,7 @@ internal static void InitPhaseActorSystemTerminate(ActorSystem system, Config co { if (!system.WhenTerminated.Wait(timeout) && !coord._runningClrHook) { - Environment.Exit(0); + Environment.Exit(coord.ShutdownReason?.ExitCode ?? 0); } }); } @@ -665,7 +695,7 @@ internal static void InitPhaseActorSystemTerminate(ActorSystem system, Config co } else if (exitClr) { - Environment.Exit(0); + Environment.Exit(coord.ShutdownReason?.ExitCode ?? 0); return TaskEx.Completed; } else