diff --git a/CHANGELOG.md b/CHANGELOG.md index 70e64f7..f5dc649 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- The `actor` module gains a `stop_abnormal` function + ## v1.0.0-rc1 - 2025-05-16 - The `supervisor` module has been removed. diff --git a/manifest.toml b/manifest.toml index bb464f8..5c5d924 100644 --- a/manifest.toml +++ b/manifest.toml @@ -3,8 +3,8 @@ packages = [ { name = "gleam_erlang", version = "1.0.0-rc1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "6E0CF4E1F66E2C9226B7554589544F00F12CE14858440EB1BF7EFDACDE1BBC64" }, - { name = "gleam_stdlib", version = "0.59.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "F8FEE9B35797301994B81AF75508CF87C328FE1585558B0FFD188DC2B32EAA95" }, - { name = "gleeunit", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "0E6C83834BA65EDCAAF4FE4FB94AC697D9262D83E6F58A750D63C9F6C8A9D9FF" }, + { name = "gleam_stdlib", version = "0.60.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "621D600BB134BC239CB2537630899817B1A42E60A1D46C5E9F3FAE39F88C800B" }, + { name = "gleeunit", version = "1.3.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "A7DD6C07B7DA49A6E28796058AA89E651D233B357D5607006D70619CD89DAAAB" }, { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, ] diff --git a/src/gleam/otp/actor.gleam b/src/gleam/otp/actor.gleam index 6065167..38d3845 100644 --- a/src/gleam/otp/actor.gleam +++ b/src/gleam/otp/actor.gleam @@ -194,6 +194,15 @@ pub fn stop() -> Next(state, message) { Stop(process.Normal) } +/// Indicate the actor is in a bad state and should shut down. It will not +/// handle any new messages, and any linked processes will also exit abnormally. +/// +/// The provided reason will be given and propagated. +/// +pub fn stop_abnormal(reason: String) -> Next(state, message) { + Stop(process.Abnormal(dynamic.string(reason))) +} + /// Provide a selector to change the messages that the actor is handling /// going forward. This replaces any selector that was previously given /// in the actor's `init` callback, or in any previous `Next` value. diff --git a/test/gleam/otp/actor_test.gleam b/test/gleam/otp/actor_test.gleam index 030ebd2..a25f478 100644 --- a/test/gleam/otp/actor_test.gleam +++ b/test/gleam/otp/actor_test.gleam @@ -294,6 +294,34 @@ pub fn killed_exit_can_be_trapped_test() { |> should.equal(Ok(process.ExitMessage(actor.pid, process.Killed))) } +pub fn abnormal_stop_exits_linked_test() { + process.trap_exits(True) + let exits = + process.new_selector() + |> process.select_trapped_exits(function.identity) + + // Make an actor exit with an abnormal reason + let assert Ok(actor) = + actor.new(Nil) + |> actor.on_message(fn(_, _) { actor.stop_abnormal("wibble") }) + |> actor.start + + process.send(actor.data, "okay") + + let trapped_reason = process.selector_receive(exits, 10) + + // Stop trapping exits, as otherwise other tests fail + process.trap_exits(False) + + trapped_reason + |> should.equal( + Ok(process.ExitMessage( + actor.pid, + process.Abnormal(dynamic.string("wibble")), + )), + ) +} + fn mapped_selector( selector: process.Selector(ActorMessage), mapper: fn(a) -> ActorMessage,