Skip to content
This repository was archived by the owner on Jun 10, 2024. It is now read-only.

Commit 18f712e

Browse files
authored
Merge pull request #207 from lumen/link
:erlang.link/1
2 parents 1086514 + e1c92b6 commit 18f712e

File tree

27 files changed

+589
-134
lines changed

27 files changed

+589
-134
lines changed

liblumen_alloc/src/erts/process.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use core::sync::atomic::{AtomicU16, AtomicU64, AtomicUsize, Ordering};
1919

2020
use ::alloc::sync::Arc;
2121

22-
use hashbrown::HashMap;
22+
use hashbrown::{HashMap, HashSet};
2323
use intrusive_collections::{LinkedList, UnsafeRef};
2424

2525
use liblumen_core::locks::{Mutex, MutexGuard, RwLock, SpinLock};
@@ -100,6 +100,9 @@ pub struct ProcessControlBlock {
100100
code_stack: Mutex<code::stack::Stack>,
101101
pub status: RwLock<Status>,
102102
pub registered_name: RwLock<Option<Atom>>,
103+
/// Pids of processes that are linked to this process and need to be exited when this process
104+
/// exits
105+
pub linked_pid_set: Mutex<HashSet<Pid>>,
103106
pub mailbox: Mutex<RefCell<Mailbox>>,
104107
// process heap, cache line aligned to avoid false sharing with rest of struct
105108
heap: Mutex<ProcessHeap>,
@@ -140,6 +143,7 @@ impl ProcessControlBlock {
140143
run_reductions: Default::default(),
141144
total_reductions: Default::default(),
142145
registered_name: Default::default(),
146+
linked_pid_set: Default::default(),
143147
}
144148
}
145149

@@ -242,6 +246,8 @@ impl ProcessControlBlock {
242246
heap.virtual_alloc(bin)
243247
}
244248

249+
// Stack
250+
245251
/// Frees stack space occupied by the last term on the stack,
246252
/// adjusting the stack pointer accordingly.
247253
///
@@ -290,6 +296,23 @@ impl ProcessControlBlock {
290296
self.heap.lock().stack_used()
291297
}
292298

299+
// Links
300+
301+
pub fn link(&self, other: &ProcessControlBlock) {
302+
// link in order so that locks are always taken in the same order to prevent deadlocks
303+
if self.pid < other.pid {
304+
let mut self_pid_set = self.linked_pid_set.lock();
305+
let mut other_pid_set = other.linked_pid_set.lock();
306+
307+
self_pid_set.insert(other.pid);
308+
other_pid_set.insert(self.pid);
309+
} else {
310+
other.link(self)
311+
}
312+
}
313+
314+
// Pid
315+
293316
pub fn pid(&self) -> Pid {
294317
self.pid
295318
}

lumen_runtime/src/otp/erlang.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
pub mod add_2;
44
pub mod apply_3;
55
pub mod convert_time_unit_3;
6+
pub mod exit_1;
7+
pub mod link_1;
68
pub mod monotonic_time_0;
79
pub mod number_or_badarith_1;
810
pub mod self_0;
@@ -38,7 +40,7 @@ use liblumen_alloc::erts::term::{
3840
Term, Tuple, TypedTerm,
3941
};
4042
use liblumen_alloc::erts::ProcessControlBlock;
41-
use liblumen_alloc::{badarg, badarith, badkey, badmap, error, exit, raise, throw};
43+
use liblumen_alloc::{badarg, badarith, badkey, badmap, error, raise, throw};
4244

4345
use crate::binary::{start_length_to_part_range, PartRange, ToTermOptions};
4446
use crate::node;
@@ -822,10 +824,6 @@ pub fn error_2(reason: Term, arguments: Term) -> Result {
822824
Err(error!(reason, Some(arguments)).into())
823825
}
824826

825-
pub fn exit_1(reason: Term) -> Result {
826-
Err(exit!(reason).into())
827-
}
828-
829827
pub fn hd_1(list: Term) -> Result {
830828
let cons: Boxed<Cons> = list.try_into()?;
831829

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// wasm32 proptest cannot be compiled at the same time as non-wasm32 proptest, so disable tests that
2+
// use proptest completely for wasm32
3+
//
4+
// See https://github.com/rust-lang/cargo/issues/4866
5+
#[cfg(all(not(target_arch = "wasm32"), test))]
6+
mod test;
7+
8+
use std::sync::Arc;
9+
10+
use liblumen_alloc::erts::exception;
11+
use liblumen_alloc::erts::exception::system::Alloc;
12+
use liblumen_alloc::erts::process::code::stack::frame::{Frame, Placement};
13+
use liblumen_alloc::erts::process::code::{self, result_from_exception};
14+
use liblumen_alloc::erts::process::ProcessControlBlock;
15+
use liblumen_alloc::erts::term::{Atom, Term};
16+
use liblumen_alloc::{exit, ModuleFunctionArity};
17+
18+
pub fn place_frame_with_arguments(
19+
process: &ProcessControlBlock,
20+
placement: Placement,
21+
reason: Term,
22+
) -> Result<(), Alloc> {
23+
process.stack_push(reason)?;
24+
process.place_frame(frame(), placement);
25+
26+
Ok(())
27+
}
28+
29+
// Private
30+
31+
fn code(arc_process: &Arc<ProcessControlBlock>) -> code::Result {
32+
arc_process.reduce();
33+
34+
let reason = arc_process.stack_pop().unwrap();
35+
36+
match native(reason) {
37+
Ok(_) => unreachable!(),
38+
Err(exception) => result_from_exception(arc_process, exception),
39+
}
40+
}
41+
42+
fn frame() -> Frame {
43+
Frame::new(module_function_arity(), code)
44+
}
45+
46+
fn function() -> Atom {
47+
Atom::try_from_str("exit").unwrap()
48+
}
49+
50+
fn module_function_arity() -> Arc<ModuleFunctionArity> {
51+
Arc::new(ModuleFunctionArity {
52+
module: super::module(),
53+
function: function(),
54+
arity: 1,
55+
})
56+
}
57+
58+
fn native(reason: Term) -> exception::Result {
59+
Err(exit!(reason).into())
60+
}

lumen_runtime/src/otp/erlang/tests/exit_1.rs renamed to lumen_runtime/src/otp/erlang/exit_1/test.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
use super::*;
1+
use proptest::prop_assert_eq;
2+
use proptest::test_runner::{Config, TestRunner};
3+
4+
use liblumen_alloc::exit;
5+
6+
use crate::otp::erlang::exit_1;
7+
use crate::scheduler::with_process_arc;
8+
use crate::test::strategy;
29

310
#[test]
411
fn exits_with_reason() {
512
with_process_arc(|arc_process| {
613
TestRunner::new(Config::with_source_file(file!()))
714
.run(&strategy::term(arc_process.clone()), |reason| {
8-
prop_assert_eq!(erlang::exit_1(reason), Err(exit!(reason).into()));
15+
prop_assert_eq!(exit_1::native(reason), Err(exit!(reason).into()));
916

1017
Ok(())
1118
})
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// wasm32 proptest cannot be compiled at the same time as non-wasm32 proptest, so disable tests that
2+
// use proptest completely for wasm32
3+
//
4+
// See https://github.com/rust-lang/cargo/issues/4866
5+
#[cfg(all(not(target_arch = "wasm32"), test))]
6+
mod test;
7+
8+
use std::sync::Arc;
9+
10+
use liblumen_alloc::erts::exception;
11+
use liblumen_alloc::erts::exception::system::Alloc;
12+
use liblumen_alloc::erts::process::code::stack::frame::{Frame, Placement};
13+
use liblumen_alloc::erts::process::code::{self, result_from_exception};
14+
use liblumen_alloc::erts::process::ProcessControlBlock;
15+
use liblumen_alloc::erts::term::{atom_unchecked, Atom, Term, TypedTerm};
16+
use liblumen_alloc::{badarg, error, ModuleFunctionArity};
17+
18+
use crate::registry::pid_to_process;
19+
20+
pub fn place_frame_with_arguments(
21+
process: &ProcessControlBlock,
22+
placement: Placement,
23+
pid_or_port: Term,
24+
) -> Result<(), Alloc> {
25+
process.stack_push(pid_or_port)?;
26+
process.place_frame(frame(), placement);
27+
28+
Ok(())
29+
}
30+
31+
// Private
32+
33+
fn code(arc_process: &Arc<ProcessControlBlock>) -> code::Result {
34+
arc_process.reduce();
35+
36+
let pid_or_port = arc_process.stack_pop().unwrap();
37+
38+
match native(arc_process, pid_or_port) {
39+
Ok(true_term) => {
40+
arc_process.return_from_call(true_term)?;
41+
42+
ProcessControlBlock::call_code(arc_process)
43+
}
44+
Err(exception) => result_from_exception(arc_process, exception),
45+
}
46+
}
47+
48+
fn frame() -> Frame {
49+
Frame::new(module_function_arity(), code)
50+
}
51+
52+
fn function() -> Atom {
53+
Atom::try_from_str("link").unwrap()
54+
}
55+
56+
fn module_function_arity() -> Arc<ModuleFunctionArity> {
57+
Arc::new(ModuleFunctionArity {
58+
module: super::module(),
59+
function: function(),
60+
arity: 1,
61+
})
62+
}
63+
64+
fn native(process: &ProcessControlBlock, pid_or_port: Term) -> exception::Result {
65+
match pid_or_port.to_typed_term().unwrap() {
66+
TypedTerm::Pid(pid) => {
67+
if pid == process.pid() {
68+
Ok(true.into())
69+
} else {
70+
match pid_to_process(&pid) {
71+
Some(pid_arc_process) => {
72+
process.link(&pid_arc_process);
73+
74+
Ok(true.into())
75+
}
76+
None => Err(error!(atom_unchecked("noproc")).into()),
77+
}
78+
}
79+
}
80+
TypedTerm::Port(_) => unimplemented!(),
81+
TypedTerm::Boxed(boxed) => match boxed.to_typed_term().unwrap() {
82+
TypedTerm::ExternalPid(_) => unimplemented!(),
83+
TypedTerm::ExternalPort(_) => unimplemented!(),
84+
_ => Err(badarg!().into()),
85+
},
86+
_ => Err(badarg!().into()),
87+
}
88+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
mod with_local_pid;
2+
3+
use proptest::prop_assert_eq;
4+
use proptest::strategy::Strategy;
5+
use proptest::test_runner::{Config, TestRunner};
6+
7+
use liblumen_alloc::badarg;
8+
use liblumen_alloc::erts::process::ProcessControlBlock;
9+
10+
use crate::otp::erlang::link_1::native;
11+
use crate::scheduler::{with_process, with_process_arc};
12+
use crate::test::strategy;
13+
14+
#[test]
15+
fn without_pid_or_port_errors_badarg() {
16+
with_process_arc(|arc_process| {
17+
TestRunner::new(Config::with_source_file(file!()))
18+
.run(
19+
&strategy::term(arc_process.clone())
20+
.prop_filter("Cannot be pid or port", |pid_or_port| {
21+
!(pid_or_port.is_pid() || pid_or_port.is_port())
22+
}),
23+
|pid_or_port| {
24+
prop_assert_eq!(native(&arc_process, pid_or_port), Err(badarg!().into()));
25+
26+
Ok(())
27+
},
28+
)
29+
.unwrap();
30+
});
31+
}
32+
33+
fn link_count(process: &ProcessControlBlock) -> usize {
34+
process.linked_pid_set.lock().len()
35+
}

0 commit comments

Comments
 (0)