Skip to content

Commit 074f6bf

Browse files
giacomocavalierilpil
authored andcommitted
call-forever
1 parent a3cbbae commit 074f6bf

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- The `gleam/erlang/process` module gains the `call_forever` and
6+
`try_call_forever` functions.
7+
38
## v0.28.0 - 2024-10-24
49

510
- The `gleam/erlang/process` module gains the `deselecting_process_down`

src/gleam/erlang/process.gleam

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,54 @@ pub fn call(
620620
resp
621621
}
622622

623+
/// Similar to the `call` function but will wait forever for a message to
624+
/// arrive rather than timing out after a specified amount of time.
625+
///
626+
/// If the receiving process exits, the calling process crashes.
627+
/// If you wish an error to be returned instead see the `try_call_forever`
628+
/// function.
629+
///
630+
pub fn call_forever(
631+
subject: Subject(request),
632+
make_request: fn(Subject(response)) -> request,
633+
) -> response {
634+
let assert Ok(response) = try_call_forever(subject, make_request)
635+
response
636+
}
637+
638+
/// Similar to the `try_call` function but will wait forever for a message
639+
/// to arrive rather than timing out after a specified amount of time.
640+
///
641+
/// If the receiving process exits then an error is returned.
642+
///
643+
pub fn try_call_forever(
644+
subject: Subject(request),
645+
make_request: fn(Subject(response)) -> request,
646+
) -> Result(response, CallError(c)) {
647+
let reply_subject = new_subject()
648+
649+
// Monitor the callee process so we can tell if it goes down (meaning we
650+
// won't get a reply)
651+
let monitor = monitor_process(subject_owner(subject))
652+
653+
// Send the request to the process over the channel
654+
send(subject, make_request(reply_subject))
655+
656+
// Await a reply or handle failure modes (timeout, process down, etc)
657+
let result =
658+
new_selector()
659+
|> selecting(reply_subject, Ok)
660+
|> selecting_process_down(monitor, fn(down) {
661+
Error(CalleeDown(reason: down.reason))
662+
})
663+
|> select_forever
664+
665+
// Demonitor the process and close the channels as we're done
666+
demonitor_process(monitor)
667+
668+
result
669+
}
670+
623671
/// Creates a link between the calling process and another process.
624672
///
625673
/// When a process crashes any linked processes will also crash. This is useful

test/gleam/erlang/process_test.gleam

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,26 @@ pub fn try_call_timeout_test() {
199199
process.try_call(call_subject, fn(x) { x }, 10)
200200
}
201201

202+
pub fn try_call_forever_test() {
203+
let parent_subject = process.new_subject()
204+
205+
process.start(linked: True, running: fn() {
206+
// Send the child subject to the parent so it can call the child
207+
let child_subject = process.new_subject()
208+
process.send(parent_subject, child_subject)
209+
// Wait for the subject to be messaged
210+
let assert Ok(#(x, reply)) = process.receive(child_subject, 50)
211+
// Reply
212+
process.send(reply, x + 1)
213+
})
214+
215+
let assert Ok(call_subject) = process.receive(parent_subject, 50)
216+
217+
// Call the child process and get a response.
218+
let assert Ok(2) =
219+
process.try_call_forever(call_subject, fn(subject) { #(1, subject) })
220+
}
221+
202222
pub fn call_test() {
203223
let parent_subject = process.new_subject()
204224

@@ -218,6 +238,26 @@ pub fn call_test() {
218238
let assert 2 = process.call(call_subject, fn(subject) { #(1, subject) }, 50)
219239
}
220240

241+
pub fn call_forever_test() {
242+
let parent_subject = process.new_subject()
243+
244+
process.start(linked: True, running: fn() {
245+
// Send the child subject to the parent so it can call the child
246+
let child_subject = process.new_subject()
247+
process.send(parent_subject, child_subject)
248+
// Wait for the subject to be messaged
249+
let assert Ok(#(x, reply)) = process.receive(child_subject, 50)
250+
// Reply
251+
process.send(reply, x + 1)
252+
})
253+
254+
let assert Ok(call_subject) = process.receive(parent_subject, 50)
255+
256+
// Call the child process and get a response.
257+
let assert 2 =
258+
process.call_forever(call_subject, fn(subject) { #(1, subject) })
259+
}
260+
221261
@external(erlang, "erlang", "send")
222262
fn send(a: process.Pid, b: anything) -> Nil
223263

0 commit comments

Comments
 (0)