Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
cc: ["gcc-9", "gcc-11", "gcc-13", "clang-10", "clang-14", "clang-18"]
cflags: ["-O3"]
otp: ["25", "26", "27"]
gleam_version: ["1.8.0"]
gleam_version: ["1.11.1"]

include:
- cc: "gcc-7"
Expand Down
3 changes: 2 additions & 1 deletion CMakeModules/BuildGleam.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ macro(pack_gleam_runnable avm_name main)

list(APPEND SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/${main}.gleam)
list(APPEND BEAMS ${CMAKE_CURRENT_BINARY_DIR}/build/prod/erlang/${main}/ebin/${main}.beam)
list(APPEND BEAMS ${CMAKE_CURRENT_BINARY_DIR}/build/prod/erlang/*/ebin/*.beam)

if(AVM_RELEASE)
set(INCLUDE_LINES "")
Expand All @@ -74,7 +75,7 @@ macro(pack_gleam_runnable avm_name main)
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/gleam.toml ${CMAKE_CURRENT_SOURCE_DIR}/manifest.toml ${CMAKE_CURRENT_BINARY_DIR}/
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src
COMMAND gleam export erlang-shipment
COMMAND ${CMAKE_BINARY_DIR}/tools/packbeam/PackBEAM ${INCLUDE_LINES} ${avm_name}.avm ${BEAMS} build/prod/erlang/gleam_stdlib/ebin/*.beam ${ARCHIVES}
COMMAND ${CMAKE_BINARY_DIR}/tools/packbeam/PackBEAM ${INCLUDE_LINES} ${avm_name}.avm ${BEAMS} ${ARCHIVES}
COMMENT "Packing gleam runnable ${avm_name}.avm"
)

Expand Down
1 change: 1 addition & 0 deletions examples/gleam/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
project(examples_gleam)

add_subdirectory(hello_atomvm)
add_subdirectory(supervise_actor)
25 changes: 25 additions & 0 deletions examples/gleam/supervise_actor/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#
# This file is part of AtomVM.
#
# Copyright 2025 Mikael Karlsson <mikael.karlsson@creado.se>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
#

project(supervise_actor)

include(BuildGleam)

pack_gleam_runnable(supervise_actor supervise_actor estdlib)
31 changes: 31 additions & 0 deletions examples/gleam/supervise_actor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!---
Copyright 2025 Mikael Karlsson <mikael.karlsson@creado.se>
SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
-->

# supervise_actor

This is a basic supervised actors program created with
`gleam new supervise_actor`.

AtomVM currently requires a `start/0` function which has been added to
`supervise_actor.gleam` and simply calls `main()` function.

The project can be run using Gleam's default runtime:
```sh
gleam run # Run the project
```

It can also be run using AtomVM on generic unix with:
```sh
gleam export erlang-shipment
../../../build/src/AtomVM build/erlang-shipment/{supervise_actor,gleam_stdlib,gleam_erlang,gleam_otp}/ebin/*.beam ../../../build/libs/atomvmlib.avm
```

Alternatively, an AVM file can be generated and executed with:
```sh
gleam export erlang-shipment
../../../build/tools/packbeam/PackBEAM supervise_actor.avm build/erlang-shipment/supervise_actor/ebin/*.beam build/erlang-shipment/{gleam_stdlib,gleam_erlang,gleam_otp}/ebin/*.beam
../../../build/src/AtomVM supervise_actor.avm ../../../build/libs/atomvmlib.avm
```
33 changes: 33 additions & 0 deletions examples/gleam/supervise_actor/gleam.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#
# This file is part of AtomVM.
#
# Copyright 2025 Mikael Karlsson <mikael.karlsson@creado.se>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
#

name = "supervise_actor"
version = "1.0.0"

description = "Supervisor and Actor example"
licences = ["Apache-2.0", "LGPL-2.1-or-later"]

# For a full reference of all the available options, you can have a look at
# https://gleam.run/writing-gleam/gleam-toml/.

[dependencies]
gleam_stdlib = ">= 0.65.0 and < 1.0.0"
gleam_erlang = ">= 1.3.0 and < 2.0.0"
gleam_otp = ">= 1.2.0 and < 2.0.0"
33 changes: 33 additions & 0 deletions examples/gleam/supervise_actor/manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#
# This file is part of AtomVM.
#
# Copyright 2025 Mikael Karlsson <mikael.karlsson@creado.se>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
#

# This file was generated by Gleam
# You typically do not need to edit this file

packages = [
{ name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" },
{ name = "gleam_otp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BA6A294E295E428EC1562DC1C11EA7530DCB981E8359134BEABC8493B7B2258E" },
{ name = "gleam_stdlib", version = "0.65.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "7C69C71D8C493AE11A5184828A77110EB05A7786EBF8B25B36A72F879C3EE107" },
]

[requirements]
gleam_erlang = { version = ">= 1.3.0 and < 2.0.0" }
gleam_otp = { version = ">= 1.2.0 and < 2.0.0" }
gleam_stdlib = { version = ">= 0.65.0 and < 1.0.0" }
158 changes: 158 additions & 0 deletions examples/gleam/supervise_actor/src/supervise_actor.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//
// This file is part of AtomVM.
//
// Copyright 2025 Mikael Karlsson <mikael.karlsson@creado.se>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
//

import gleam/erlang/process.{type Subject}
import gleam/io
import gleam/otp/actor
import gleam/otp/static_supervisor as supervisor
import gleam/otp/supervision

pub type RequestMessage {
SendPid
Kill
}

pub type ReplyMessage {
MyPidIs(process.Pid)
Aaarghhh
}

// AtomVM currently requires a start/0 function instead of main/0
pub fn start() -> Nil {
io.println("Hello from AtomVM!")
main()
}

pub fn main() -> Nil {
io.println("Hello from Gleam !")
io.println("Hello from supervise_actor!")
let actor_name1 = process.new_name("actor1")
let actor_name2 = process.new_name("actor2")
let subject1: Subject(RequestMessage) = process.named_subject(actor_name1)
let subject2: Subject(RequestMessage) = process.named_subject(actor_name2)
let my_subject: Subject(ReplyMessage) = process.new_subject()

let child_spec1 =
supervision.worker(fn() { init(my_subject, actor_name1) })
|> supervision.restart(supervision.Permanent)

let child_spec2 =
supervision.worker(fn() { init(my_subject, actor_name2) })
|> supervision.restart(supervision.Permanent)

io.println("Start a supervisor with two permanent actor children.")
let assert Ok(actor.Started(_pid, _supervisor)) =
supervisor.new(supervisor.OneForOne)
|> supervisor.add(child_spec1)
|> supervisor.add(child_spec2)
|> supervisor.start()

io.println("Get child pids.")
let #(pid1, pid2) = echo get_pids(subject1, subject2, my_subject)
assert pid1 != pid2

io.println("Kill first actor, it shall be restarted with new pid.")
process.send(subject1, Kill)
let assert Ok(Aaarghhh) = process.receive(my_subject, 100)
// Give the supervisor some time to restart actor 1,
process.sleep(200)
io.println("Get child pids again, first shall be new.")
let #(pid3, pid4) = echo get_pids(subject1, subject2, my_subject)
assert pid1 != pid3
assert pid2 == pid4

// Now add a new OneForAll supervisor
// Once PR 1958 is done

// let actor_name1 = process.new_name("actor3")
// let actor_name2 = process.new_name("actor4")
// let subject1: Subject(RequestMessage) = process.named_subject(actor_name1)
// let subject2: Subject(RequestMessage) = process.named_subject(actor_name2)
// let my_subject: Subject(ReplyMessage) = process.new_subject()

// let child_spec1 =
// supervision.worker(fn() { init(my_subject, actor_name1) })
// |> supervision.restart(supervision.Permanent)

// let child_spec2 =
// supervision.worker(fn() { init(my_subject, actor_name2) })
// |> supervision.restart(supervision.Permanent)

// let assert Ok(actor.Started(_pid, _supervisor)) =
// supervisor.new(supervisor.OneForAll)
// |> supervisor.add(child_spec1)
// |> supervisor.add(child_spec2)
// |> supervisor.start()

// let #(pid1, pid2) = get_pids(subject1, subject2, my_subject)
// assert pid1 != pid2

// // Kill first actor, both actore shall be restarted with new pid
// process.send(subject1, Kill)
// let assert Ok(Aaarghhh) = process.receive(my_subject, 100)
// // Give the supervisor some time to restart actor 1,
// process.sleep(200)
// let #(pid3, pid4) = get_pids(subject1, subject2, my_subject)
// assert pid1 != pid3
// assert pid2 != pid4
Nil
}

// --------- Actor stanza--------
type State {
State(client: Subject(ReplyMessage))
}

fn init(
client: Subject(ReplyMessage),
name: process.Name(RequestMessage),
) -> actor.StartResult(Subject(RequestMessage)) {
let state = State(client: client)
actor.new(state)
|> actor.named(name)
|> actor.on_message(handle_message)
|> actor.start()
}

fn handle_message(state: State, message: RequestMessage) {
case message {
SendPid -> {
actor.send(state.client, MyPidIs(process.self()))
actor.continue(state)
}
Kill -> {
actor.send(state.client, Aaarghhh)
actor.stop_abnormal("My manager killed me!")
}
}
// actor.continue(state)
}

fn get_pids(
subject1: Subject(RequestMessage),
subject2: Subject(RequestMessage),
my_subject: Subject(ReplyMessage),
) {
process.send(subject1, SendPid)
let assert Ok(MyPidIs(pid1)) = process.receive(my_subject, 300)
process.send(subject2, SendPid)
let assert Ok(MyPidIs(pid2)) = process.receive(my_subject, 300)
#(pid1, pid2)
}
Loading