Foundations for causal tracing

This commit is contained in:
Tony Garnock-Jones 2022-01-19 14:40:50 +01:00
parent f7a5edff39
commit 4dc613a091
25 changed files with 871 additions and 430 deletions

28
Cargo.lock generated
View File

@ -1098,9 +1098,9 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "preserves" name = "preserves"
version = "2.2.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c604be1e4ddac999d7c3d81ca9f7912a33a1234f5394fb902315f8cee25645cd" checksum = "87e17ecd8e57af358d013c7f9cc30078a86bbba63eac64e73e1c9b9651304eb8"
dependencies = [ dependencies = [
"base64", "base64",
"dtoa", "dtoa",
@ -1112,9 +1112,9 @@ dependencies = [
[[package]] [[package]]
name = "preserves-schema" name = "preserves-schema"
version = "2.3.1" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1ae4749701948512784501bcc48bfee33e8511714fac9c3acfe7fabb1d7fbdf" checksum = "758fe4013a89071fafdc71ba79bd919f68ec9baadaa77db78b4a41c21cf8c606"
dependencies = [ dependencies = [
"convert_case", "convert_case",
"glob", "glob",
@ -1318,9 +1318,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]] [[package]]
name = "security-framework" name = "security-framework"
version = "2.4.2" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" checksum = "d09d3c15d814eda1d6a836f2f2b56a6abc1446c8a34351cb3180d3db92ffe4ce"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"core-foundation", "core-foundation",
@ -1331,9 +1331,9 @@ dependencies = [
[[package]] [[package]]
name = "security-framework-sys" name = "security-framework-sys"
version = "2.4.2" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" checksum = "e90dd10c41c6bfc633da6e0c659bd25d31e0791e5974ac42970267d59eba87f7"
dependencies = [ dependencies = [
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
@ -1386,9 +1386,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.74" version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" checksum = "c059c05b48c5c0067d4b4b2b4f0732dd65feb52daf7e0ea09cd87e7dadc1af79"
dependencies = [ dependencies = [
"itoa 1.0.1", "itoa 1.0.1",
"ryu", "ryu",
@ -1459,9 +1459,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "structopt" name = "structopt"
version = "0.3.25" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
dependencies = [ dependencies = [
"clap", "clap",
"lazy_static", "lazy_static",
@ -1890,9 +1890,9 @@ dependencies = [
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.10.2+wasi-snapshot-preview1" version = "0.10.3+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" checksum = "46a2e384a3f170b0c7543787a91411175b71afd56ba4d3a0ae5678d4e2243c0e"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"

View File

@ -8,11 +8,11 @@ use syndicate::value::NestedValue;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
syndicate::convenient_logging()?; syndicate::convenient_logging()?;
Actor::new(None).boot(tracing::Span::current(), |t| { Actor::top(None, |t| {
let ds = Cap::new(&t.create(Dataspace::new(None))); let ds = Cap::new(&t.create(Dataspace::new(None)));
let _ = t.prevent_inert_check(); let _ = t.prevent_inert_check();
t.spawn(syndicate::name!("box"), enclose!((ds) move |t| { t.spawn(Some(AnyValue::symbol("box")), enclose!((ds) move |t| {
let current_value = t.named_field("current_value", 0u64); let current_value = t.named_field("current_value", 0u64);
t.dataflow({ t.dataflow({
@ -49,7 +49,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(()) Ok(())
})); }));
t.spawn(syndicate::name!("client"), enclose!((ds) move |t| { t.spawn(Some(AnyValue::symbol("client")), enclose!((ds) move |t| {
let box_state_handler = syndicate::entity(0u32) let box_state_handler = syndicate::entity(0u32)
.on_asserted(enclose!((ds) move |count, t, captures: AnyValue| { .on_asserted(enclose!((ds) move |count, t, captures: AnyValue| {
*count = *count + 1; *count = *count + 1;

View File

@ -26,7 +26,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::from_args(); let config = Config::from_args();
let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?; let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?;
let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split(); let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split();
Actor::new(None).boot(syndicate::name!("consumer"), |t| { Actor::top(None, |t| {
relay::connect_stream(t, i, o, false, sturdyref, (), |_state, t, ds| { relay::connect_stream(t, i, o, false, sturdyref, (), |_state, t, ds| {
let consumer = syndicate::entity(0) let consumer = syndicate::entity(0)
.on_message(|message_count, _t, m: AnyValue| { .on_message(|message_count, _t, m: AnyValue| {
@ -44,13 +44,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
observer: Arc::clone(&consumer), observer: Arc::clone(&consumer),
}); });
t.linked_task(syndicate::name!("tick"), async move { t.linked_task(Some(AnyValue::symbol("tick")), async move {
let mut stats_timer = interval(Duration::from_secs(1)); let mut stats_timer = interval(Duration::from_secs(1));
loop { loop {
stats_timer.tick().await; stats_timer.tick().await;
let consumer = Arc::clone(&consumer); let consumer = Arc::clone(&consumer);
external_event(&Arc::clone(&consumer.underlying.mailbox), external_event(&Arc::clone(&consumer.underlying.mailbox),
&Account::new(syndicate::name!("account")), None,
&Account::new(None, None),
Box::new(move |t| t.with_entity( Box::new(move |t| t.with_entity(
&consumer.underlying, &consumer.underlying,
|t, e| e.message(t, AnyValue::new(true)))))?; |t, e| e.message(t, AnyValue::new(true)))))?;

View File

@ -93,7 +93,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::from_args(); let config = Config::from_args();
let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?; let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?;
let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split(); let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split();
Actor::new(None).boot(syndicate::name!("pingpong"), |t| { Actor::top(None, |t| {
relay::connect_stream(t, i, o, false, sturdyref, (), move |_state, t, ds| { relay::connect_stream(t, i, o, false, sturdyref, (), move |_state, t, ds| {
let (send_label, recv_label, report_latency_every, should_echo, bytes_padding) = let (send_label, recv_label, report_latency_every, should_echo, bytes_padding) =
@ -172,13 +172,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
observer: Arc::clone(&consumer), observer: Arc::clone(&consumer),
}); });
t.linked_task(syndicate::name!("tick"), async move { t.linked_task(Some(AnyValue::symbol("tick")), async move {
let mut stats_timer = interval(Duration::from_secs(1)); let mut stats_timer = interval(Duration::from_secs(1));
loop { loop {
stats_timer.tick().await; stats_timer.tick().await;
let consumer = Arc::clone(&consumer); let consumer = Arc::clone(&consumer);
external_event(&Arc::clone(&consumer.underlying.mailbox), external_event(&Arc::clone(&consumer.underlying.mailbox),
&Account::new(syndicate::name!("account")), None,
&Account::new(None, None),
Box::new(move |t| t.with_entity( Box::new(move |t| t.with_entity(
&consumer.underlying, &consumer.underlying,
|t, e| e.message(t, AnyValue::new(true)))))?; |t, e| e.message(t, AnyValue::new(true)))))?;
@ -189,7 +190,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let turn_count = c.turn_count; let turn_count = c.turn_count;
let action_count = c.action_count; let action_count = c.action_count;
let account = Arc::clone(t.account()); let account = Arc::clone(t.account());
t.linked_task(syndicate::name!("boot-ping"), async move { t.linked_task(Some(AnyValue::symbol("boot-ping")), async move {
let padding = AnyValue::bytestring(vec![0; bytes_padding]); let padding = AnyValue::bytestring(vec![0; bytes_padding]);
for _ in 0..turn_count { for _ in 0..turn_count {
let mut events: PendingEventQueue = vec![]; let mut events: PendingEventQueue = vec![];
@ -203,7 +204,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
&ds.underlying, &ds.underlying,
|t, e| e.message(t, current_rec)))); |t, e| e.message(t, current_rec))));
} }
external_events(&ds.underlying.mailbox, &account, events)? external_events(&ds.underlying.mailbox, None, &account, events)?
} }
Ok(LinkedTaskTermination::KeepFacet) Ok(LinkedTaskTermination::KeepFacet)
}); });

View File

@ -2,9 +2,10 @@ use structopt::StructOpt;
use syndicate::actor::*; use syndicate::actor::*;
use syndicate::enclose; use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::relay; use syndicate::relay;
use syndicate::sturdy; use syndicate::sturdy;
use syndicate::value::Value; use syndicate::value::NestedValue;
use tokio::net::TcpStream; use tokio::net::TcpStream;
@ -20,35 +21,30 @@ pub struct Config {
dataspace: String, dataspace: String,
} }
#[inline]
fn says(who: AnyValue, what: AnyValue) -> AnyValue {
let mut r = Value::simple_record("Says", 2);
r.fields_vec_mut().push(who);
r.fields_vec_mut().push(what);
r.finish().wrap()
}
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
syndicate::convenient_logging()?; syndicate::convenient_logging()?;
let config = Config::from_args(); let config = Config::from_args();
let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?; let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?;
let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split(); let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split();
Actor::new(None).boot(syndicate::name!("producer"), |t| { Actor::top(None, |t| {
relay::connect_stream(t, i, o, false, sturdyref, (), move |_state, t, ds| { relay::connect_stream(t, i, o, false, sturdyref, (), move |_state, t, ds| {
let padding: AnyValue = Value::ByteString(vec![0; config.bytes_padding]).wrap(); let padding = AnyValue::new(&vec![0u8; config.bytes_padding][..]);
let action_count = config.action_count; let action_count = config.action_count;
let account = Account::new(syndicate::name!("account")); let account = Account::new(None, None);
t.linked_task(syndicate::name!("sender"), async move { t.linked_task(Some(AnyValue::symbol("sender")), async move {
loop { loop {
account.ensure_clear_funds().await; account.ensure_clear_funds().await;
let mut events: PendingEventQueue = Vec::new(); let mut events: PendingEventQueue = Vec::new();
for _ in 0..action_count { for _ in 0..action_count {
events.push(Box::new(enclose!((ds, padding) move |t| t.with_entity( events.push(Box::new(enclose!((ds, padding) move |t| t.with_entity(
&ds.underlying, |t, e| e.message( &ds.underlying, |t, e| e.message(
t, says(Value::from("producer").wrap(), padding)))))); t,
rec![AnyValue::symbol("Says"),
AnyValue::new("producer"),
padding])))));
} }
external_events(&ds.underlying.mailbox, &account, events)?; external_events(&ds.underlying.mailbox, None, &account, events)?;
} }
}); });
Ok(None) Ok(None)

View File

@ -26,7 +26,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::from_args(); let config = Config::from_args();
let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?; let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?;
let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split(); let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split();
Actor::new(None).boot(syndicate::name!("state-consumer"), |t| { Actor::top(None, |t| {
relay::connect_stream(t, i, o, false, sturdyref, (), |_state, t, ds| { relay::connect_stream(t, i, o, false, sturdyref, (), |_state, t, ds| {
let consumer = { let consumer = {
#[derive(Default)] #[derive(Default)]
@ -65,13 +65,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
observer: Arc::clone(&consumer), observer: Arc::clone(&consumer),
}); });
t.linked_task(syndicate::name!("tick"), async move { t.linked_task(Some(AnyValue::symbol("tick")), async move {
let mut stats_timer = interval(Duration::from_secs(1)); let mut stats_timer = interval(Duration::from_secs(1));
loop { loop {
stats_timer.tick().await; stats_timer.tick().await;
let consumer = Arc::clone(&consumer); let consumer = Arc::clone(&consumer);
external_event(&Arc::clone(&consumer.underlying.mailbox), external_event(&Arc::clone(&consumer.underlying.mailbox),
&Account::new(syndicate::name!("account")), None,
&Account::new(None, None),
Box::new(move |t| t.with_entity( Box::new(move |t| t.with_entity(
&consumer.underlying, &consumer.underlying,
|t, e| e.message(t, AnyValue::new(true)))))?; |t, e| e.message(t, AnyValue::new(true)))))?;

View File

@ -4,9 +4,10 @@ use structopt::StructOpt;
use syndicate::actor::*; use syndicate::actor::*;
use syndicate::enclose; use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::relay; use syndicate::relay;
use syndicate::sturdy; use syndicate::sturdy;
use syndicate::value::Value; use syndicate::value::NestedValue;
use tokio::net::TcpStream; use tokio::net::TcpStream;
@ -22,23 +23,21 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::from_args(); let config = Config::from_args();
let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?; let sturdyref = sturdy::SturdyRef::from_hex(&config.dataspace)?;
let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split(); let (i, o) = TcpStream::connect("127.0.0.1:8001").await?.into_split();
Actor::new(None).boot(syndicate::name!("state-producer"), |t| { Actor::top(None, |t| {
relay::connect_stream(t, i, o, false, sturdyref, (), move |_state, t, ds| { relay::connect_stream(t, i, o, false, sturdyref, (), move |_state, t, ds| {
let account = Account::new(syndicate::name!("account")); let account = Account::new(None, None);
t.linked_task(syndicate::name!("sender"), async move { t.linked_task(Some(AnyValue::symbol("sender")), async move {
let presence: AnyValue = Value::simple_record1( let presence = rec![AnyValue::symbol("Present"), AnyValue::new(std::process::id())];
"Present",
Value::from(std::process::id()).wrap()).wrap();
let handle = syndicate::actor::next_handle(); let handle = syndicate::actor::next_handle();
let assert_e = || { let assert_e = || {
external_event( external_event(
&Arc::clone(&ds.underlying.mailbox), &account, Box::new(enclose!( &Arc::clone(&ds.underlying.mailbox), None, &account, Box::new(enclose!(
(ds, presence, handle) move |t| t.with_entity( (ds, presence, handle) move |t| t.with_entity(
&ds.underlying, |t, e| e.assert(t, presence, handle))))) &ds.underlying, |t, e| e.assert(t, presence, handle)))))
}; };
let retract_e = || { let retract_e = || {
external_event( external_event(
&Arc::clone(&ds.underlying.mailbox), &account, Box::new(enclose!( &Arc::clone(&ds.underlying.mailbox), None, &account, Box::new(enclose!(
(ds, handle) move |t| t.with_entity( (ds, handle) move |t| t.with_entity(
&ds.underlying, |t, e| e.retract(t, handle))))) &ds.underlying, |t, e| e.retract(t, handle)))))
}; };

View File

@ -5,6 +5,7 @@ use std::sync::Arc;
use syndicate::actor::*; use syndicate::actor::*;
use syndicate::during::entity; use syndicate::during::entity;
use syndicate::enclose; use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::schemas::dataspace::Observe; use syndicate::schemas::dataspace::Observe;
use syndicate::schemas::service; use syndicate::schemas::service;
use syndicate::value::NestedValue; use syndicate::value::NestedValue;
@ -16,10 +17,10 @@ use crate::schemas::internal_services;
use syndicate_macros::during; use syndicate_macros::during;
pub fn boot(t: &mut Activation, ds: Arc<Cap>) { pub fn boot(t: &mut Activation, ds: Arc<Cap>) {
t.spawn(syndicate::name!("dependencies"), move |t| { t.spawn(Some(AnyValue::symbol("dependencies_listener")), move |t| {
Ok(during!(t, ds, language(), <require-service $spec>, |t: &mut Activation| { Ok(during!(t, ds, language(), <require-service $spec>, |t: &mut Activation| {
tracing::debug!(?spec, "tracking dependencies"); tracing::debug!(?spec, "tracking dependencies");
t.spawn_link(syndicate::name!(parent: None, "dependencies", spec = ?spec), t.spawn_link(Some(rec![AnyValue::symbol("dependencies"), language().unparse(&spec)]),
enclose!((ds) |t| run(t, ds, spec))); enclose!((ds) |t| run(t, ds, spec)));
Ok(()) Ok(())
})) }))

View File

@ -1,5 +1,6 @@
use preserves_schema::Codec; use preserves_schema::Codec;
use std::io;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
@ -11,6 +12,7 @@ use syndicate::enclose;
use syndicate::relay; use syndicate::relay;
use syndicate::schemas::service; use syndicate::schemas::service;
use syndicate::schemas::transport_address; use syndicate::schemas::transport_address;
use syndicate::trace;
use syndicate::value::Map; use syndicate::value::Map;
use syndicate::value::NestedValue; use syndicate::value::NestedValue;
@ -50,6 +52,9 @@ struct ServerConfig {
#[structopt(long)] #[structopt(long)]
no_banner: bool, no_banner: bool,
#[structopt(short = "t", long)]
trace_file: Option<PathBuf>,
} }
#[tokio::main] #[tokio::main]
@ -83,13 +88,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing::trace!("startup"); tracing::trace!("startup");
Actor::new(None).boot(tracing::Span::current(), move |t| { let trace_collector = config.trace_file.clone().map(
let server_config_ds = Cap::new(&t.create(Dataspace::new(Some(syndicate::name!("config"))))); |p| Ok::<trace::TraceCollector, io::Error>(trace::TraceCollector::ascii(
let log_ds = Cap::new(&t.create(Dataspace::new(Some(syndicate::name!("log"))))); io::BufWriter::new(std::fs::File::create(p)?))))
.transpose()?;
Actor::top(trace_collector, move |t| {
let server_config_ds = Cap::new(&t.create(Dataspace::new(Some(AnyValue::symbol("config")))));
let log_ds = Cap::new(&t.create(Dataspace::new(Some(AnyValue::symbol("log")))));
if config.inferior { if config.inferior {
tracing::info!("inferior server instance"); tracing::info!("inferior server instance");
t.spawn(syndicate::name!("parent"), enclose!((server_config_ds) move |t| { t.spawn(Some(AnyValue::symbol("parent")), enclose!((server_config_ds) move |t| {
protocol::run_io_relay(t, protocol::run_io_relay(t,
relay::Input::Bytes(Box::pin(tokio::io::stdin())), relay::Input::Bytes(Box::pin(tokio::io::stdin())),
relay::Output::Bytes(Box::pin(tokio::io::stdout())), relay::Output::Bytes(Box::pin(tokio::io::stdout())),
@ -154,7 +164,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}); });
} }
t.spawn(tracing::Span::current(), enclose!((log_ds) move |t| { t.spawn(Some(AnyValue::symbol("logger")), enclose!((log_ds) move |t| {
let n_unknown: AnyValue = AnyValue::symbol("-"); let n_unknown: AnyValue = AnyValue::symbol("-");
let n_pid: AnyValue = AnyValue::symbol("pid"); let n_pid: AnyValue = AnyValue::symbol("pid");
let n_line: AnyValue = AnyValue::symbol("line"); let n_line: AnyValue = AnyValue::symbol("line");

View File

@ -9,6 +9,7 @@ use syndicate::actor::*;
use syndicate::error::Error; use syndicate::error::Error;
use syndicate::error::error; use syndicate::error::error;
use syndicate::relay; use syndicate::relay;
use syndicate::trace;
use syndicate::value::NestedValue; use syndicate::value::NestedValue;
use tokio::net::TcpStream; use tokio::net::TcpStream;
@ -37,16 +38,19 @@ pub fn run_io_relay(
} }
pub fn run_connection( pub fn run_connection(
trace_collector: Option<trace::TraceCollector>,
facet: FacetRef, facet: FacetRef,
i: relay::Input, i: relay::Input,
o: relay::Output, o: relay::Output,
initial_ref: Arc<Cap>, initial_ref: Arc<Cap>,
) { ) {
facet.activate(Account::new(syndicate::name!("start-session")), let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("start-session"));
|t| run_io_relay(t, i, o, initial_ref)); let account = Account::new(Some(AnyValue::symbol("start-session")), trace_collector);
facet.activate(&account, cause, |t| run_io_relay(t, i, o, initial_ref));
} }
pub async fn detect_protocol( pub async fn detect_protocol(
trace_collector: Option<trace::TraceCollector>,
facet: FacetRef, facet: FacetRef,
stream: TcpStream, stream: TcpStream,
gateway: Arc<Cap>, gateway: Arc<Cap>,
@ -76,7 +80,7 @@ pub async fn detect_protocol(
_ => unreachable!() _ => unreachable!()
} }
}; };
run_connection(facet, i, o, gateway); run_connection(trace_collector, facet, i, o, gateway);
Ok(()) Ok(())
} }

View File

@ -3,6 +3,8 @@ use notify::Watcher;
use notify::RecursiveMode; use notify::RecursiveMode;
use notify::watcher; use notify::watcher;
use syndicate::preserves::rec;
use std::fs; use std::fs;
use std::future; use std::future;
use std::io; use std::io;
@ -16,6 +18,7 @@ use syndicate::actor::*;
use syndicate::error::Error; use syndicate::error::Error;
use syndicate::enclose; use syndicate::enclose;
use syndicate::supervise::{Supervisor, SupervisorConfiguration}; use syndicate::supervise::{Supervisor, SupervisorConfiguration};
use syndicate::trace;
use syndicate::value::BinarySource; use syndicate::value::BinarySource;
use syndicate::value::BytesBinarySource; use syndicate::value::BytesBinarySource;
use syndicate::value::Map; use syndicate::value::Map;
@ -32,11 +35,11 @@ use crate::script;
use syndicate_macros::during; use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, config_ds: Arc<Cap>) { pub fn on_demand(t: &mut Activation, config_ds: Arc<Cap>) {
t.spawn(syndicate::name!("config_watcher"), move |t| { t.spawn(Some(AnyValue::symbol("config_watcher")), move |t| {
Ok(during!(t, config_ds, language(), <run-service $spec: internal_services::ConfigWatcher>, |t| { Ok(during!(t, config_ds, language(), <run-service $spec: internal_services::ConfigWatcher>, |t| {
Supervisor::start( Supervisor::start(
t, t,
syndicate::name!(parent: None, "config", path = ?spec.path), Some(rec![AnyValue::symbol("config"), AnyValue::new(spec.path.clone())]),
SupervisorConfiguration::default(), SupervisorConfiguration::default(),
enclose!((config_ds, spec) lifecycle::updater(config_ds, spec)), enclose!((config_ds, spec) lifecycle::updater(config_ds, spec)),
enclose!((config_ds) move |t| enclose!((config_ds, spec) run(t, config_ds, spec)))) enclose!((config_ds) move |t| enclose!((config_ds, spec) run(t, config_ds, spec))))
@ -175,26 +178,32 @@ fn run(
watcher.watch(&env.path, RecursiveMode::Recursive).map_err(convert_notify_error)?; watcher.watch(&env.path, RecursiveMode::Recursive).map_err(convert_notify_error)?;
let facet = t.facet.clone(); let facet = t.facet.clone();
let trace_collector = t.trace_collector();
let span = tracing::Span::current(); let span = tracing::Span::current();
thread::spawn(move || { thread::spawn(move || {
let _entry = span.enter(); let _entry = span.enter();
let mut path_state: Map<PathBuf, FacetId> = Map::new(); let mut path_state: Map<PathBuf, FacetId> = Map::new();
if !facet.activate(
Account::new(syndicate::name!("initial_scan")),
|t| {
initial_scan(t, &mut path_state, &config_ds, env.clone());
config_ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{ {
return; let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("initial_scan"));
let account = Account::new(Some(AnyValue::symbol("initial_scan")), trace_collector.clone());
if !facet.activate(
&account, cause, |t| {
initial_scan(t, &mut path_state, &config_ds, env.clone());
config_ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{
return;
}
} }
tracing::trace!("initial_scan complete"); tracing::trace!("initial_scan complete");
let mut rescan = |paths: Vec<PathBuf>| { let mut rescan = |paths: Vec<PathBuf>| {
facet.activate(Account::new(syndicate::name!("rescan")), |t| { let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("rescan"));
let account = Account::new(Some(AnyValue::symbol("rescan")), trace_collector.clone());
facet.activate(&account, cause, |t| {
let mut to_stop = Vec::new(); let mut to_stop = Vec::new();
for path in paths.into_iter() { for path in paths.into_iter() {
let maybe_facet_id = path_state.remove(&path); let maybe_facet_id = path_state.remove(&path);
@ -236,15 +245,19 @@ fn run(
if !keep_running { break; } if !keep_running { break; }
} }
facet.activate(Account::new(syndicate::name!("termination")), |t| { {
tracing::trace!("linked thread terminating associated facet"); let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("termination"));
Ok(t.stop()) let account = Account::new(Some(AnyValue::symbol("termination")), trace_collector);
}); facet.activate(&account, cause, |t| {
tracing::trace!("linked thread terminating associated facet");
Ok(t.stop())
});
}
tracing::trace!("linked thread done"); tracing::trace!("linked thread done");
}); });
t.linked_task(syndicate::name!("cancel-wait"), async move { t.linked_task(Some(AnyValue::symbol("cancel-wait")), async move {
future::pending::<()>().await; future::pending::<()>().await;
drop(watcher); drop(watcher);
Ok(LinkedTaskTermination::KeepFacet) Ok(LinkedTaskTermination::KeepFacet)

View File

@ -4,8 +4,10 @@ use std::sync::Arc;
use syndicate::actor::*; use syndicate::actor::*;
use syndicate::enclose; use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::schemas::service; use syndicate::schemas::service;
use syndicate::supervise::{Supervisor, SupervisorConfiguration}; use syndicate::supervise::{Supervisor, SupervisorConfiguration};
use syndicate::trace;
use syndicate::value::NestedValue; use syndicate::value::NestedValue;
use tokio::io::AsyncRead; use tokio::io::AsyncRead;
@ -21,7 +23,7 @@ use crate::schemas::external_services::*;
use syndicate_macros::during; use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, config_ds: Arc<Cap>, root_ds: Arc<Cap>) { pub fn on_demand(t: &mut Activation, config_ds: Arc<Cap>, root_ds: Arc<Cap>) {
t.spawn(syndicate::name!("daemon"), move |t| { t.spawn(Some(AnyValue::symbol("daemon_listener")), move |t| {
Ok(during!(t, config_ds, language(), <run-service $spec: DaemonService>, Ok(during!(t, config_ds, language(), <run-service $spec: DaemonService>,
enclose!((config_ds, root_ds) move |t: &mut Activation| { enclose!((config_ds, root_ds) move |t: &mut Activation| {
supervise_daemon(t, config_ds, root_ds, spec) supervise_daemon(t, config_ds, root_ds, spec)
@ -46,7 +48,7 @@ fn supervise_daemon(
})); }));
Supervisor::start( Supervisor::start(
t, t,
syndicate::name!(parent: None, "daemon", id = ?spec.id), Some(language().unparse(&spec)),
SupervisorConfiguration::on_error_only(), SupervisorConfiguration::on_error_only(),
enclose!((config_ds, spec) lifecycle::updater(config_ds, spec)), enclose!((config_ds, spec) lifecycle::updater(config_ds, spec)),
enclose!((config_ds, root_ds) move |t| enclose!((config_ds, root_ds) move |t|
@ -162,7 +164,6 @@ struct DaemonInstance {
config_ds: Arc<Cap>, config_ds: Arc<Cap>,
log_ds: Arc<Cap>, log_ds: Arc<Cap>,
service: AnyValue, service: AnyValue,
name: tracing::Span,
cmd: process::Command, cmd: process::Command,
announce_presumed_readiness: bool, announce_presumed_readiness: bool,
unready_configs: Arc<Field<isize>>, unready_configs: Arc<Field<isize>>,
@ -236,9 +237,13 @@ impl DaemonInstance {
Some(n) => AnyValue::new(n), Some(n) => AnyValue::new(n),
None => AnyValue::symbol("unknown"), None => AnyValue::symbol("unknown"),
}; };
t.spawn(syndicate::name!(parent: self.name.clone(), "log"), move |t| { let trace_collector = t.trace_collector();
t.linked_task(tracing::Span::current(), async move { t.spawn(Some(rec![AnyValue::symbol("log"), kind.clone(), self.service.clone()]), move |t| {
t.linked_task(None, async move {
let mut r = BufReader::new(r); let mut r = BufReader::new(r);
let cause = trace_collector.as_ref().map(
|_| trace::TurnCause::external(kind.value().as_symbol().unwrap()));
let account = Account::new(None, trace_collector);
loop { loop {
let mut buf = Vec::new(); let mut buf = Vec::new();
if r.read_until(b'\n', &mut buf).await? == 0 { if r.read_until(b'\n', &mut buf).await? == 0 {
@ -250,8 +255,7 @@ impl DaemonInstance {
}; };
let now = AnyValue::new(chrono::Utc::now().to_rfc3339()); let now = AnyValue::new(chrono::Utc::now().to_rfc3339());
if !facet.activate( if !facet.activate(
Account::new(tracing::Span::current()), &account, cause.clone(), enclose!((pid, service, kind) |t| {
enclose!((pid, service, kind) |t| {
log_ds.message(t, &(), &syndicate_macros::template!( log_ds.message(t, &(), &syndicate_macros::template!(
"<log =now { "<log =now {
pid: =pid, pid: =pid,
@ -304,13 +308,17 @@ impl DaemonInstance {
counter::adjust(t, &self.unready_configs, -1); counter::adjust(t, &self.unready_configs, -1);
} }
let trace_collector = t.trace_collector();
t.linked_task( t.linked_task(
syndicate::name!(parent: self.name.clone(), "wait"), Some(rec![AnyValue::symbol("wait"), self.service.clone()]),
enclose!((facet) async move { enclose!((facet) async move {
tracing::trace!("waiting for process exit"); tracing::trace!("waiting for process exit");
let status = child.wait().await?; let status = child.wait().await?;
tracing::debug!(?status); tracing::debug!(?status);
facet.activate(Account::new(syndicate::name!("instance-terminated")), |t| { let cause = trace_collector.as_ref().map(
|_| trace::TurnCause::external("instance-terminated"));
let account = Account::new(Some(AnyValue::symbol("instance-terminated")), trace_collector);
facet.activate(&account, cause, |t| {
let m = if status.success() { None } else { Some(format!("{}", status)) }; let m = if status.success() { None } else { Some(format!("{}", status)) };
self.handle_exit(t, m) self.handle_exit(t, m)
}); });
@ -378,9 +386,10 @@ fn run(
Ok(()) Ok(())
}))?; }))?;
let trace_collector = t.trace_collector();
enclose!((config_ds, unready_configs, completed_processes) enclose!((config_ds, unready_configs, completed_processes)
during!(t, config_ds.clone(), language(), <daemon #(&service.id) $config>, { during!(t, config_ds.clone(), language(), <daemon #(&service.id) $config>, {
enclose!((spec, config_ds, root_ds, unready_configs, completed_processes) enclose!((spec, config_ds, root_ds, unready_configs, completed_processes, trace_collector)
|t: &mut Activation| { |t: &mut Activation| {
tracing::debug!(?config, "new config"); tracing::debug!(?config, "new config");
counter::adjust(t, &unready_configs, 1); counter::adjust(t, &unready_configs, 1);
@ -391,7 +400,7 @@ fn run(
tracing::info!(?config); tracing::info!(?config);
let config = config.elaborate(); let config = config.elaborate();
let facet = t.facet.clone(); let facet = t.facet.clone();
t.linked_task(syndicate::name!("subprocess"), async move { t.linked_task(Some(AnyValue::symbol("subprocess")), async move {
let mut cmd = config.process.build_command().ok_or("Cannot start daemon process")?; let mut cmd = config.process.build_command().ok_or("Cannot start daemon process")?;
let announce_presumed_readiness = match config.ready_on_start { let announce_presumed_readiness = match config.ready_on_start {
@ -432,7 +441,6 @@ fn run(
config_ds, config_ds,
log_ds: root_ds, log_ds: root_ds,
service: spec, service: spec,
name: tracing::Span::current(),
cmd, cmd,
announce_presumed_readiness, announce_presumed_readiness,
unready_configs, unready_configs,
@ -441,7 +449,10 @@ fn run(
protocol, protocol,
}; };
facet.activate(Account::new(syndicate::name!("instance-startup")), |t| { let cause = trace_collector.as_ref().map(
|_| trace::TurnCause::external("instance-startup"));
let account = Account::new(Some(AnyValue::symbol("instance-startup")), trace_collector);
facet.activate(&account, cause, |t| {
daemon_instance.start(t) daemon_instance.start(t)
}); });
Ok(LinkedTaskTermination::KeepFacet) Ok(LinkedTaskTermination::KeepFacet)

View File

@ -1,7 +1,11 @@
use preserves_schema::Codec;
use std::sync::Arc; use std::sync::Arc;
use syndicate::actor::*; use syndicate::actor::*;
use syndicate::enclose; use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::preserves::value::NestedValue;
use crate::language::language; use crate::language::language;
use crate::lifecycle; use crate::lifecycle;
@ -10,9 +14,10 @@ use crate::schemas::internal_services::DebtReporter;
use syndicate_macros::during; use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) { pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) {
t.spawn(syndicate::name!("debt_reporter"), move |t| { t.spawn(Some(AnyValue::symbol("debt_reporter_listener")), move |t| {
Ok(during!(t, ds, language(), <run-service $spec: DebtReporter>, |t: &mut Activation| { Ok(during!(t, ds, language(), <run-service $spec: DebtReporter>, |t: &mut Activation| {
t.spawn_link(tracing::Span::current(), enclose!((ds) |t| run(t, ds, spec))); t.spawn_link(Some(rec![AnyValue::symbol("debt_reporter"), language().unparse(&spec)]),
enclose!((ds) |t| run(t, ds, spec)));
Ok(()) Ok(())
})) }))
}); });
@ -23,8 +28,7 @@ fn run(t: &mut Activation, ds: Arc<Cap>, spec: DebtReporter) -> ActorResult {
ds.assert(t, language(), &lifecycle::ready(&spec)); ds.assert(t, language(), &lifecycle::ready(&spec));
t.every(core::time::Duration::from_millis((spec.interval_seconds.0 * 1000.0) as u64), |_t| { t.every(core::time::Duration::from_millis((spec.interval_seconds.0 * 1000.0) as u64), |_t| {
for (id, (name, debt)) in syndicate::actor::ACCOUNTS.read().iter() { for (id, (name, debt)) in syndicate::actor::ACCOUNTS.read().iter() {
let _enter = name.enter(); tracing::info!(id, ?name, debt = ?debt.load(std::sync::atomic::Ordering::Relaxed));
tracing::info!(id, debt = ?debt.load(std::sync::atomic::Ordering::Relaxed));
} }
Ok(()) Ok(())
}) })

View File

@ -1,7 +1,10 @@
use preserves_schema::Codec;
use std::sync::Arc; use std::sync::Arc;
use syndicate::actor::*; use syndicate::actor::*;
use syndicate::enclose; use syndicate::enclose;
use syndicate::preserves::value::NestedValue;
use syndicate::supervise::{Supervisor, SupervisorConfiguration}; use syndicate::supervise::{Supervisor, SupervisorConfiguration};
use crate::language::language; use crate::language::language;
@ -11,11 +14,11 @@ use crate::schemas::internal_services::Milestone;
use syndicate_macros::during; use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) { pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) {
t.spawn(syndicate::name!("milestone"), move |t| { t.spawn(Some(AnyValue::symbol("milestone_listener")), move |t| {
Ok(during!(t, ds, language(), <run-service $spec: Milestone>, |t: &mut Activation| { Ok(during!(t, ds, language(), <run-service $spec: Milestone>, |t: &mut Activation| {
Supervisor::start( Supervisor::start(
t, t,
syndicate::name!(parent: None, "milestone", name = ?spec.name), Some(language().unparse(&spec)),
SupervisorConfiguration::default(), SupervisorConfiguration::default(),
|_, _| Ok(()), |_, _| Ok(()),
enclose!((ds) move |t| enclose!((ds, spec) run(t, ds, spec)))) enclose!((ds) move |t| enclose!((ds, spec) run(t, ds, spec))))

View File

@ -1,9 +1,14 @@
use preserves_schema::Codec;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::sync::Arc; use std::sync::Arc;
use syndicate::actor::*; use syndicate::actor::*;
use syndicate::enclose; use syndicate::enclose;
use syndicate::preserves::rec;
use syndicate::preserves::value::NestedValue;
use syndicate::supervise::{Supervisor, SupervisorConfiguration}; use syndicate::supervise::{Supervisor, SupervisorConfiguration};
use syndicate::trace;
use tokio::net::TcpListener; use tokio::net::TcpListener;
@ -15,11 +20,11 @@ use crate::schemas::internal_services::TcpRelayListener;
use syndicate_macros::during; use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) { pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) {
t.spawn(syndicate::name!("tcp_relay_listener"), move |t| { t.spawn(Some(AnyValue::symbol("tcp_relay_listener")), move |t| {
Ok(during!(t, ds, language(), <run-service $spec: TcpRelayListener>, |t| { Ok(during!(t, ds, language(), <run-service $spec: TcpRelayListener>, |t| {
Supervisor::start( Supervisor::start(
t, t,
syndicate::name!(parent: None, "relay", addr = ?spec), Some(rec![AnyValue::symbol("relay"), language().unparse(&spec)]),
SupervisorConfiguration::default(), SupervisorConfiguration::default(),
enclose!((ds, spec) lifecycle::updater(ds, spec)), enclose!((ds, spec) lifecycle::updater(ds, spec)),
enclose!((ds) move |t| enclose!((ds, spec) run(t, ds, spec)))) enclose!((ds) move |t| enclose!((ds, spec) run(t, ds, spec))))
@ -31,40 +36,45 @@ fn run(t: &mut Activation, ds: Arc<Cap>, spec: TcpRelayListener) -> ActorResult
lifecycle::terminate_on_service_restart(t, &ds, &spec); lifecycle::terminate_on_service_restart(t, &ds, &spec);
let host = spec.addr.host.clone(); let host = spec.addr.host.clone();
let port = u16::try_from(&spec.addr.port).map_err(|_| "Invalid TCP port number")?; let port = u16::try_from(&spec.addr.port).map_err(|_| "Invalid TCP port number")?;
let parent_span = tracing::Span::current();
let facet = t.facet.clone(); let facet = t.facet.clone();
t.linked_task(syndicate::name!("listener"), async move { let trace_collector = t.trace_collector();
t.linked_task(Some(AnyValue::symbol("listener")), async move {
let listen_addr = format!("{}:{}", host, port); let listen_addr = format!("{}:{}", host, port);
let listener = TcpListener::bind(listen_addr).await?; let listener = TcpListener::bind(listen_addr).await?;
if !facet.activate(
Account::new(syndicate::name!("readiness")), |t| {
tracing::info!("listening");
ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{ {
return Ok(LinkedTaskTermination::Normal); let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("readiness"));
let account = Account::new(Some(AnyValue::symbol("readiness")), trace_collector.clone());
if !facet.activate(
&account, cause, |t| {
tracing::info!("listening");
ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{
return Ok(LinkedTaskTermination::Normal);
}
} }
loop { loop {
let (stream, addr) = listener.accept().await?; let (stream, addr) = listener.accept().await?;
let gatekeeper = spec.gatekeeper.clone(); let gatekeeper = spec.gatekeeper.clone();
let name = syndicate::name!(parent: parent_span.clone(), "conn"); let name = Some(rec![AnyValue::symbol("tcp"), AnyValue::new(format!("{}", &addr))]);
let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("connect"));
let account = Account::new(name.clone(), trace_collector.clone());
if !facet.activate( if !facet.activate(
Account::new(name.clone()), &account, cause, enclose!((trace_collector) move |t| {
move |t| {
t.spawn(name, move |t| { t.spawn(name, move |t| {
Ok(t.linked_task(tracing::Span::current(), { Ok(t.linked_task(None, {
let facet = t.facet.clone(); let facet = t.facet.clone();
async move { async move {
detect_protocol(facet, stream, gatekeeper, addr).await?; detect_protocol(trace_collector, facet, stream, gatekeeper, addr).await?;
Ok(LinkedTaskTermination::KeepFacet) Ok(LinkedTaskTermination::KeepFacet)
} }
})) }))
}); });
Ok(()) Ok(())
}) }))
{ {
return Ok(LinkedTaskTermination::Normal); return Ok(LinkedTaskTermination::Normal);
} }

View File

@ -1,3 +1,5 @@
use preserves_schema::Codec;
use std::io; use std::io;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
@ -5,8 +7,11 @@ use std::sync::Arc;
use syndicate::actor::*; use syndicate::actor::*;
use syndicate::enclose; use syndicate::enclose;
use syndicate::error::Error; use syndicate::error::Error;
use syndicate::preserves::rec;
use syndicate::preserves::value::NestedValue;
use syndicate::relay; use syndicate::relay;
use syndicate::supervise::{Supervisor, SupervisorConfiguration}; use syndicate::supervise::{Supervisor, SupervisorConfiguration};
use syndicate::trace;
use tokio::net::UnixListener; use tokio::net::UnixListener;
use tokio::net::UnixStream; use tokio::net::UnixStream;
@ -19,11 +24,11 @@ use crate::schemas::internal_services::UnixRelayListener;
use syndicate_macros::during; use syndicate_macros::during;
pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) { pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) {
t.spawn(syndicate::name!("unix_relay_listener"), move |t| { t.spawn(Some(AnyValue::symbol("unix_relay_listener")), move |t| {
Ok(during!(t, ds, language(), <run-service $spec: UnixRelayListener>, |t| { Ok(during!(t, ds, language(), <run-service $spec: UnixRelayListener>, |t| {
Supervisor::start( Supervisor::start(
t, t,
syndicate::name!(parent: None, "relay", addr = ?spec), Some(rec![AnyValue::symbol("relay"), language().unparse(&spec)]),
SupervisorConfiguration::default(), SupervisorConfiguration::default(),
enclose!((ds, spec) lifecycle::updater(ds, spec)), enclose!((ds, spec) lifecycle::updater(ds, spec)),
enclose!((ds) move |t| enclose!((ds, spec) run(t, ds, spec)))) enclose!((ds) move |t| enclose!((ds, spec) run(t, ds, spec))))
@ -34,38 +39,44 @@ pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) {
fn run(t: &mut Activation, ds: Arc<Cap>, spec: UnixRelayListener) -> ActorResult { fn run(t: &mut Activation, ds: Arc<Cap>, spec: UnixRelayListener) -> ActorResult {
lifecycle::terminate_on_service_restart(t, &ds, &spec); lifecycle::terminate_on_service_restart(t, &ds, &spec);
let path_str = spec.addr.path.clone(); let path_str = spec.addr.path.clone();
let parent_span = tracing::Span::current();
let facet = t.facet.clone(); let facet = t.facet.clone();
t.linked_task(syndicate::name!("listener"), async move { let trace_collector = t.trace_collector();
t.linked_task(Some(AnyValue::symbol("listener")), async move {
let listener = bind_unix_listener(&PathBuf::from(path_str)).await?; let listener = bind_unix_listener(&PathBuf::from(path_str)).await?;
if !facet.activate(
Account::new(syndicate::name!("readiness")), |t| {
tracing::info!("listening");
ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{ {
return Ok(LinkedTaskTermination::Normal); let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("readiness"));
let account = Account::new(Some(AnyValue::symbol("readiness")), trace_collector.clone());
if !facet.activate(
&account, cause, |t| {
tracing::info!("listening");
ds.assert(t, language(), &lifecycle::ready(&spec));
Ok(())
})
{
return Ok(LinkedTaskTermination::Normal);
}
} }
loop { loop {
let (stream, _addr) = listener.accept().await?; let (stream, _addr) = listener.accept().await?;
let peer = stream.peer_cred()?; let peer = stream.peer_cred()?;
let gatekeeper = spec.gatekeeper.clone(); let gatekeeper = spec.gatekeeper.clone();
let name = syndicate::name!(parent: parent_span.clone(), "conn", let name = Some(rec![AnyValue::symbol("unix"),
pid = ?peer.pid().unwrap_or(-1), AnyValue::new(peer.pid().unwrap_or(-1)),
uid = peer.uid()); AnyValue::new(peer.uid())]);
let cause = trace_collector.as_ref().map(|_| trace::TurnCause::external("connect"));
let account = Account::new(name.clone(), trace_collector.clone());
if !facet.activate( if !facet.activate(
Account::new(name.clone()), &account, cause, enclose!((trace_collector) move |t| {
move |t| {
t.spawn(name, |t| { t.spawn(name, |t| {
Ok(t.linked_task(tracing::Span::current(), { Ok(t.linked_task(None, {
let facet = t.facet.clone(); let facet = t.facet.clone();
async move { async move {
tracing::info!(protocol = %"unix"); tracing::info!(protocol = %"unix");
let (i, o) = stream.into_split(); let (i, o) = stream.into_split();
run_connection(facet, run_connection(trace_collector,
facet,
relay::Input::Bytes(Box::pin(i)), relay::Input::Bytes(Box::pin(i)),
relay::Output::Bytes(Box::pin(o)), relay::Output::Bytes(Box::pin(o)),
gatekeeper); gatekeeper);
@ -74,7 +85,7 @@ fn run(t: &mut Activation, ds: Arc<Cap>, spec: UnixRelayListener) -> ActorResult
})) }))
}); });
Ok(()) Ok(())
}) }))
{ {
return Ok(LinkedTaskTermination::Normal); return Ok(LinkedTaskTermination::Normal);
} }

View File

@ -52,19 +52,19 @@ pub fn bench_pub(c: &mut Criterion) {
b.iter_custom(|iters| { b.iter_custom(|iters| {
let start = Instant::now(); let start = Instant::now();
rt.block_on(async move { rt.block_on(async move {
Actor::new(None).boot(syndicate::name!("dataspace"), move |t| { Actor::top(None, move |t| {
let ds = t.create(Dataspace::new(None)); let ds = t.create(Dataspace::new(None));
let shutdown = t.create(ShutdownEntity); let shutdown = t.create(ShutdownEntity);
let account = Account::new(syndicate::name!("sender-account")); let account = Account::new(None, None);
t.linked_task(syndicate::name!("sender"), async move { t.linked_task(Some(AnyValue::symbol("sender")), async move {
for _ in 0..iters { for _ in 0..iters {
external_event(&ds.mailbox, &account, Box::new( external_event(&ds.mailbox, None, &account, Box::new(
enclose!((ds) move |t| t.with_entity( enclose!((ds) move |t| t.with_entity(
&ds, &ds,
|t, e| e.message(t, says(AnyValue::new("bench_pub"), |t, e| e.message(t, says(AnyValue::new("bench_pub"),
Value::ByteString(vec![]).wrap()))))))? Value::ByteString(vec![]).wrap()))))))?
} }
external_event(&shutdown.mailbox, &account, Box::new( external_event(&shutdown.mailbox, None, &account, Box::new(
enclose!((shutdown) move |t| t.with_entity( enclose!((shutdown) move |t| t.with_entity(
&shutdown, &shutdown,
|t, e| e.message(t, AnyValue::new(true))))))?; |t, e| e.message(t, AnyValue::new(true))))))?;
@ -83,7 +83,7 @@ pub fn bench_pub(c: &mut Criterion) {
rt.block_on(async move { rt.block_on(async move {
let turn_count = Arc::new(AtomicU64::new(0)); let turn_count = Arc::new(AtomicU64::new(0));
Actor::new(None).boot(syndicate::name!("dataspace"), { Actor::top(None, {
let iters = iters.clone(); let iters = iters.clone();
let turn_count = Arc::clone(&turn_count); let turn_count = Arc::clone(&turn_count);
@ -103,7 +103,7 @@ pub fn bench_pub(c: &mut Criterion) {
observer: shutdown, observer: shutdown,
}); });
t.spawn(syndicate::name!("consumer"), move |t| { t.spawn(Some(AnyValue::symbol("consumer")), move |t| {
struct Receiver(Arc<AtomicU64>); struct Receiver(Arc<AtomicU64>);
impl Entity<AnyValue> for Receiver { impl Entity<AnyValue> for Receiver {
fn message(&mut self, _t: &mut Activation, _m: AnyValue) -> ActorResult { fn message(&mut self, _t: &mut Activation, _m: AnyValue) -> ActorResult {
@ -139,10 +139,11 @@ pub fn bench_pub(c: &mut Criterion) {
}); });
let account = Arc::clone(t.account()); let account = Arc::clone(t.account());
t.linked_task(syndicate::name!("sender"), async move { t.linked_task(Some(AnyValue::symbol("sender")), async move {
for _i in 0..iters { for _i in 0..iters {
let ds = Arc::clone(&ds); let ds = Arc::clone(&ds);
external_event(&Arc::clone(&ds.underlying.mailbox), &account, Box::new( external_event(
&Arc::clone(&ds.underlying.mailbox), None, &account, Box::new(
move |t| t.with_entity( move |t| t.with_entity(
&ds.underlying, &ds.underlying,
|t, e| e.message(t, says(AnyValue::new("bench_pub"), |t, e| e.message(t, says(AnyValue::new("bench_pub"),
@ -150,7 +151,8 @@ pub fn bench_pub(c: &mut Criterion) {
} }
{ {
let ds = Arc::clone(&ds); let ds = Arc::clone(&ds);
external_event(&Arc::clone(&ds.underlying.mailbox), &account, Box::new( external_event(
&Arc::clone(&ds.underlying.mailbox), None, &account, Box::new(
move |t| t.with_entity( move |t| t.with_entity(
&ds.underlying, &ds.underlying,
|t, e| e.message(t, AnyValue::new(true)))))?; |t, e| e.message(t, AnyValue::new(true)))))?;

View File

@ -7,6 +7,8 @@ use std::time::Duration;
use std::time::Instant; use std::time::Instant;
use syndicate::actor::*; use syndicate::actor::*;
use syndicate::preserves::rec;
use syndicate::value::NestedValue;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
@ -88,14 +90,16 @@ pub fn bench_ring(c: &mut Criterion) {
self.i += 1; self.i += 1;
let spawner_ref = Arc::clone(&self.self_ref); let spawner_ref = Arc::clone(&self.self_ref);
ACTORS_CREATED.fetch_add(1, Ordering::Relaxed); ACTORS_CREATED.fetch_add(1, Ordering::Relaxed);
t.spawn(syndicate::name!("forwarder", ?i), move |t| { t.spawn(
let _ = t.prevent_inert_check(); Some(rec![AnyValue::symbol("forwarder"), AnyValue::new(i)]),
let f = t.create(Forwarder { move |t| {
next, let _ = t.prevent_inert_check();
let f = t.create(Forwarder {
next,
});
t.message(&spawner_ref, f);
Ok(())
}); });
t.message(&spawner_ref, f);
Ok(())
});
} else { } else {
let mut c_state = Counter { let mut c_state = Counter {
start: Instant::now(), start: Instant::now(),
@ -118,7 +122,7 @@ pub fn bench_ring(c: &mut Criterion) {
} }
ACTORS_CREATED.fetch_add(1, Ordering::Relaxed); ACTORS_CREATED.fetch_add(1, Ordering::Relaxed);
Actor::new(None).boot(syndicate::name!("counter"), move |t| { Actor::top(None, move |t| {
let _ = t.prevent_inert_check(); let _ = t.prevent_inert_check();
let mut s = Spawner { let mut s = Spawner {
self_ref: t.create_inert(), self_ref: t.create_inert(),

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@ use preserves_schema::Codec;
/// A Dataspace object (entity). /// A Dataspace object (entity).
#[derive(Debug)] #[derive(Debug)]
pub struct Dataspace { pub struct Dataspace {
pub name: tracing::Span, pub name: Name,
/// Index over assertions placed in the dataspace; used to /// Index over assertions placed in the dataspace; used to
/// efficiently route assertion changes and messages to observers. /// efficiently route assertion changes and messages to observers.
pub index: skeleton::Index, pub index: skeleton::Index,
@ -31,10 +31,9 @@ pub struct Dataspace {
impl Dataspace { impl Dataspace {
/// Construct a new, empty dataspace. /// Construct a new, empty dataspace.
pub fn new(name: Option<tracing::Span>) -> Self { pub fn new(name: Name) -> Self {
Self { Self {
name: name.map_or_else(|| crate::name!("anonymous_dataspace"), name: name,
|n| crate::name!(parent: &n, "dataspace")),
index: skeleton::Index::new(), index: skeleton::Index::new(),
handle_map: Map::new(), handle_map: Map::new(),
} }
@ -62,10 +61,8 @@ impl Dataspace {
impl Entity<_Any> for Dataspace { impl Entity<_Any> for Dataspace {
fn assert(&mut self, t: &mut Activation, a: _Any, h: Handle) -> ActorResult { fn assert(&mut self, t: &mut Activation, a: _Any, h: Handle) -> ActorResult {
let _guard = self.name.enter();
let is_new = self.index.insert(t, &a); let is_new = self.index.insert(t, &a);
tracing::trace!(assertion = ?a, handle = ?h, ?is_new, "assert"); tracing::trace!(dataspace = ?self.name, assertion = ?a, handle = ?h, ?is_new, "assert");
if is_new { if is_new {
if let Ok(o) = language().parse::<Observe>(&a) { if let Ok(o) = language().parse::<Observe>(&a) {
@ -78,13 +75,11 @@ impl Entity<_Any> for Dataspace {
} }
fn retract(&mut self, t: &mut Activation, h: Handle) -> ActorResult { fn retract(&mut self, t: &mut Activation, h: Handle) -> ActorResult {
let _guard = self.name.enter();
match self.handle_map.remove(&h) { match self.handle_map.remove(&h) {
None => tracing::warn!(handle = ?h, "retract of unknown handle"), None => tracing::warn!(dataspace = ?self.name, handle = ?h, "retract of unknown handle"),
Some(a) => { Some(a) => {
let is_last = self.index.remove(t, &a); let is_last = self.index.remove(t, &a);
tracing::trace!(assertion = ?a, handle = ?h, ?is_last, "retract"); tracing::trace!(dataspace = ?self.name, assertion = ?a, handle = ?h, ?is_last, "retract");
if is_last { if is_last {
if let Ok(o) = language().parse::<Observe>(&a) { if let Ok(o) = language().parse::<Observe>(&a) {
@ -97,9 +92,7 @@ impl Entity<_Any> for Dataspace {
} }
fn message(&mut self, t: &mut Activation, m: _Any) -> ActorResult { fn message(&mut self, t: &mut Activation, m: _Any) -> ActorResult {
let _guard = self.name.enter(); tracing::trace!(dataspace = ?self.name, body = ?m, "message");
tracing::trace!(body = ?m, "message");
self.index.send(t, &m); self.index.send(t, &m);
Ok(()) Ok(())
} }

View File

@ -1,4 +1,5 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![feature(min_specialization)]
#[doc(inline)] #[doc(inline)]
pub use preserves::value; pub use preserves::value;
@ -29,13 +30,33 @@ pub mod schemas {
pub mod skeleton; pub mod skeleton;
pub mod sturdy; pub mod sturdy;
pub mod tracer; pub mod trace;
#[doc(inline)] #[doc(inline)]
pub use during::entity; pub use during::entity;
#[doc(inline)] /// Sets up [`tracing`] logging in a reasonable way.
pub use tracer::convenient_logging; ///
/// Useful at the top of `main` functions.
pub fn convenient_logging() -> Result<(), Box<dyn std::error::Error>> {
let filter = match std::env::var(tracing_subscriber::filter::EnvFilter::DEFAULT_ENV) {
Err(std::env::VarError::NotPresent) =>
tracing_subscriber::filter::EnvFilter::default()
.add_directive(tracing_subscriber::filter::LevelFilter::INFO.into()),
_ =>
tracing_subscriber::filter::EnvFilter::try_from_default_env()?,
};
let subscriber = tracing_subscriber::fmt()
.with_ansi(true)
.with_thread_ids(true)
.with_max_level(tracing::Level::TRACE)
.with_env_filter(filter)
.with_writer(std::io::stderr)
.finish();
tracing::subscriber::set_global_default(subscriber)
.expect("Could not set tracing global subscriber");
Ok(())
}
preserves_schema::define_language!(language(): Language<actor::AnyValue> { preserves_schema::define_language!(language(): Language<actor::AnyValue> {
syndicate: schemas::Language, syndicate: schemas::Language,

View File

@ -9,6 +9,7 @@ use crate::error::error;
use crate::schemas::gatekeeper; use crate::schemas::gatekeeper;
use crate::schemas::protocol as P; use crate::schemas::protocol as P;
use crate::schemas::sturdy; use crate::schemas::sturdy;
use crate::trace;
use futures::Sink; use futures::Sink;
use futures::SinkExt; use futures::SinkExt;
@ -248,8 +249,10 @@ impl TunnelRelay {
|io| Arc::clone(&tr.membranes.import_oid(t, &tr_ref, io).inc_ref().obj)); |io| Arc::clone(&tr.membranes.import_oid(t, &tr_ref, io).inc_ref().obj));
dump_membranes!(tr.membranes); dump_membranes!(tr.membranes);
*tr_ref.lock() = Some(tr); *tr_ref.lock() = Some(tr);
t.linked_task(crate::name!("writer"), output_loop(o, output_rx)); t.linked_task(Some(AnyValue::symbol("writer")),
t.linked_task(crate::name!("reader"), input_loop(t.facet.clone(), i, tr_ref)); output_loop(o, output_rx));
t.linked_task(Some(AnyValue::symbol("reader")),
input_loop(t.trace_collector(), t.facet.clone(), i, tr_ref));
t.state.add_exit_hook(&self_entity); t.state.add_exit_hook(&self_entity);
result result
} }
@ -619,11 +622,13 @@ impl DomainEncode<Arc<Cap>> for Membranes {
} }
async fn input_loop( async fn input_loop(
trace_collector: Option<trace::TraceCollector>,
facet: FacetRef, facet: FacetRef,
i: Input, i: Input,
relay: TunnelRelayRef, relay: TunnelRelayRef,
) -> Result<LinkedTaskTermination, Error> { ) -> Result<LinkedTaskTermination, Error> {
let account = Account::new(crate::name!("input-loop")); let account = Account::new(Some(AnyValue::symbol("input-loop")), trace_collector);
let cause = trace::TurnCause::external("input-loop");
match i { match i {
Input::Packets(mut src) => { Input::Packets(mut src) => {
loop { loop {
@ -631,12 +636,15 @@ async fn input_loop(
match src.next().await { match src.next().await {
None => break, None => break,
Some(bs) => { Some(bs) => {
let is_alive = facet.activate(Arc::clone(&account), |t| { if !facet.activate(
let mut g = relay.lock(); &account, Some(cause.clone()), |t| {
let tr = g.as_mut().expect("initialized"); let mut g = relay.lock();
tr.handle_inbound_datagram(t, &bs?) let tr = g.as_mut().expect("initialized");
}); tr.handle_inbound_datagram(t, &bs?)
if !is_alive { break; } })
{
break;
}
} }
} }
} }
@ -659,12 +667,15 @@ async fn input_loop(
match n { match n {
0 => break, 0 => break,
_ => { _ => {
let is_alive = facet.activate(Arc::clone(&account), |t| { if !facet.activate(
let mut g = relay.lock(); &account, Some(cause.clone()), |t| {
let tr = g.as_mut().expect("initialized"); let mut g = relay.lock();
tr.handle_inbound_stream(t, &mut buf) let tr = g.as_mut().expect("initialized");
}); tr.handle_inbound_stream(t, &mut buf)
if !is_alive { break; } })
{
break;
}
} }
} }
} }

View File

@ -1,6 +1,8 @@
//! Extremely simple single-actor supervision. Vastly simplified compared to the available //! Extremely simple single-actor supervision. Vastly simplified compared to the available
//! options in [Erlang/OTP](https://erlang.org/doc/man/supervisor.html). //! options in [Erlang/OTP](https://erlang.org/doc/man/supervisor.html).
use preserves::value::NestedValue;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@ -8,7 +10,6 @@ use std::time::Duration;
use tokio::time::Instant; use tokio::time::Instant;
use crate::actor::*; use crate::actor::*;
use crate::enclose;
use crate::schemas::service::State; use crate::schemas::service::State;
pub type Boot = Box<dyn Send + FnMut(&mut Activation) -> ActorResult>; pub type Boot = Box<dyn Send + FnMut(&mut Activation) -> ActorResult>;
@ -36,7 +37,8 @@ pub struct SupervisorConfiguration {
pub struct Supervisor { pub struct Supervisor {
self_ref: Arc<Ref<Protocol>>, self_ref: Arc<Ref<Protocol>>,
name: tracing::Span, my_name: Name,
child_name: Name,
config: SupervisorConfiguration, config: SupervisorConfiguration,
boot_fn: Option<Boot>, boot_fn: Option<Boot>,
restarts: VecDeque<Instant>, restarts: VecDeque<Instant>,
@ -86,8 +88,7 @@ impl Entity<Protocol> for Supervisor
} }
fn retract(&mut self, t: &mut Activation, _h: Handle) -> ActorResult { fn retract(&mut self, t: &mut Activation, _h: Handle) -> ActorResult {
let _name = self.name.clone(); let _entry = tracing::info_span!("supervisor", name = ?self.child_name).entered();
let _entry = _name.enter();
let exit_status = let exit_status =
self.ac_ref.take().expect("valid supervisee ActorRef") self.ac_ref.take().expect("valid supervisee ActorRef")
.exit_status() .exit_status()
@ -144,8 +145,9 @@ impl Entity<Protocol> for Supervisor
} }
fn stop(&mut self, _t: &mut Activation) -> ActorResult { fn stop(&mut self, _t: &mut Activation) -> ActorResult {
let _entry = self.name.enter(); tracing::info!(name = ?self.my_name,
tracing::info!(self_ref = ?self.self_ref, "Supervisor terminating"); self_ref = ?self.self_ref,
"Supervisor terminating");
Ok(()) Ok(())
} }
} }
@ -154,18 +156,21 @@ impl Supervisor {
pub fn start<C: 'static + Send + FnMut(&mut Activation, State) -> ActorResult, pub fn start<C: 'static + Send + FnMut(&mut Activation, State) -> ActorResult,
B: 'static + Send + FnMut(&mut Activation) -> ActorResult>( B: 'static + Send + FnMut(&mut Activation) -> ActorResult>(
t: &mut Activation, t: &mut Activation,
name: tracing::Span, name: Name,
config: SupervisorConfiguration, config: SupervisorConfiguration,
mut state_cb: C, mut state_cb: C,
boot_fn: B, boot_fn: B,
) -> ActorResult { ) -> ActorResult {
let _entry = name.enter(); let _entry = tracing::info_span!("supervisor", ?name).entered();
tracing::trace!(?config); tracing::trace!(?config);
let self_ref = t.create_inert(); let self_ref = t.create_inert();
let state_field = t.named_field("supervisee_state", State::Started); let state_field = t.named_field("supervisee_state", State::Started);
let my_name = name.as_ref().map(
|n| preserves::rec![AnyValue::symbol("supervisor"), n.clone()]);
let mut supervisor = Supervisor { let mut supervisor = Supervisor {
self_ref: Arc::clone(&self_ref), self_ref: Arc::clone(&self_ref),
name: name.clone(), my_name: my_name.clone(),
child_name: name,
config, config,
boot_fn: Some(Box::new(boot_fn)), boot_fn: Some(Box::new(boot_fn)),
restarts: VecDeque::new(), restarts: VecDeque::new(),
@ -174,14 +179,11 @@ impl Supervisor {
}; };
tracing::info!(self_ref = ?supervisor.self_ref, "Supervisor starting"); tracing::info!(self_ref = ?supervisor.self_ref, "Supervisor starting");
supervisor.ensure_started(t)?; supervisor.ensure_started(t)?;
t.dataflow(enclose!((name) move |t| { t.dataflow(move |t| {
let state = t.get(&state_field).clone(); let state = t.get(&state_field).clone();
{ tracing::debug!(name = ?my_name, ?state);
let _entry = name.enter();
tracing::debug!(?state);
}
state_cb(t, state) state_cb(t, state)
}))?; })?;
self_ref.become_entity(supervisor); self_ref.become_entity(supervisor);
t.on_stop_notify(&self_ref); t.on_stop_notify(&self_ref);
Ok(()) Ok(())
@ -190,16 +192,16 @@ impl Supervisor {
fn ensure_started(&mut self, t: &mut Activation) -> ActorResult { fn ensure_started(&mut self, t: &mut Activation) -> ActorResult {
match self.boot_fn.take() { match self.boot_fn.take() {
None => { None => {
let _entry = self.name.enter();
t.set(&self.state, State::Failed); t.set(&self.state, State::Failed);
tracing::error!("Cannot restart supervisee, because it panicked at startup") tracing::error!(name = ?self.my_name,
"Cannot restart supervisee, because it panicked at startup")
} }
Some(mut boot_fn) => { Some(mut boot_fn) => {
let self_ref = Arc::clone(&self.self_ref); let self_ref = Arc::clone(&self.self_ref);
t.facet(|t: &mut Activation| { t.facet(|t: &mut Activation| {
t.assert(&self.self_ref, Protocol::SuperviseeStarted); t.assert(&self.self_ref, Protocol::SuperviseeStarted);
self.ac_ref = Some(t.spawn_link( self.ac_ref = Some(t.spawn_link(
crate::name!(parent: &self.name, "supervisee"), self.child_name.clone(),
move |t| { move |t| {
match boot_fn(t) { match boot_fn(t) {
Ok(()) => { Ok(()) => {

187
syndicate/src/trace.rs Normal file
View File

@ -0,0 +1,187 @@
//! Records *describing* actions committed at the end of a turn and
//! events triggering the start of a turn. These are not the actions
//! or events themselves: they are reflective information on the
//! action of the system, enough to reconstruct interesting
//! projections of system activity.
pub use super::schemas::trace::*;
use preserves::value::NestedValue;
use preserves::value::Writer;
use preserves_schema::Codec;
use super::actor::{self, AnyValue, Ref, Cap};
use super::language;
use std::sync::Arc;
use std::time::SystemTime;
use tokio::select;
use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
#[derive(Debug, Clone)]
pub struct TraceCollector {
pub tx: UnboundedSender<TraceEntry>,
}
impl<M> From<&Ref<M>> for Target {
fn from(v: &Ref<M>) -> Target {
Target {
actor: ActorId(AnyValue::new(v.mailbox.actor_id)),
facet: FacetId(AnyValue::new(u64::from(v.facet_id))),
oid: Oid(AnyValue::new(v.oid())),
}
}
}
impl<M: std::fmt::Debug> From<&M> for AssertionDescription {
default fn from(v: &M) -> Self {
Self::Opaque { description: AnyValue::new(format!("{:?}", v)) }
}
}
impl From<&AnyValue> for AssertionDescription {
fn from(v: &AnyValue) -> Self {
Self::Value { value: v.clone() }
}
}
impl TraceCollector {
pub fn record(&self, id: actor::ActorId, a: ActorActivation) {
let _ = self.tx.send(TraceEntry {
timestamp: SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
.expect("Time after Unix epoch").as_secs_f64().into(),
actor: ActorId(AnyValue::new(id)),
item: a,
});
}
}
impl TurnDescription {
pub fn new(activation_id: u64, cause: TurnCause) -> Self {
Self {
id: TurnId(AnyValue::new(activation_id)),
cause,
actions: Vec::new(),
}
}
pub fn record(&mut self, a: ActionDescription) {
self.actions.push(a)
}
pub fn take(&mut self) -> Self {
Self {
id: self.id.clone(),
cause: self.cause.clone(),
actions: std::mem::take(&mut self.actions),
}
}
}
impl TurnCause {
pub fn external(description: &str) -> Self {
Self::External { description: AnyValue::new(description) }
}
}
struct CapEncoder;
impl preserves::value::DomainEncode<Arc<Cap>> for CapEncoder {
fn encode_embedded<W: Writer>(
&mut self,
w: &mut W,
d: &Arc<Cap>,
) -> std::io::Result<()> {
w.write_string(&d.debug_str())
// use preserves::value::writer::CompoundWriter;
// use preserves::value::boundary as B;
// let mut c = w.start_record(Some(3))?;
// let mut b = B::start(B::Item::RecordLabel);
// c.boundary(&b)?;
// c.write_symbol("cap")?;
// b.shift(Some(B::Item::RecordField));
// c.boundary(&b)?;
// c.write_u64(d.underlying.mailbox.actor_id)?;
// b.shift(Some(B::Item::RecordField));
// c.boundary(&b)?;
// c.write_u64(u64::from(d.underlying.facet_id))?;
// b.shift(Some(B::Item::RecordField));
// c.boundary(&b)?;
// c.write_u64(d.underlying.oid() as u64)?;
// // TODO: write attenuation?
// b.shift(None);
// c.boundary(&b)?;
// w.end_record(c)
}
}
pub enum CollectorEvent {
Event(TraceEntry),
PeriodicFlush,
}
impl TraceCollector {
pub fn new<F: 'static + Send + FnMut(CollectorEvent)>(mut f: F) -> TraceCollector {
let (tx, mut rx) = unbounded_channel::<TraceEntry>();
tokio::spawn(async move {
let mut timer = tokio::time::interval(std::time::Duration::from_secs(1));
loop {
select! {
maybe_entry = rx.recv() => {
match maybe_entry {
None => break,
Some(entry) => {
tracing::trace!(?entry);
f(CollectorEvent::Event(entry));
}
}
},
_ = timer.tick() => f(CollectorEvent::PeriodicFlush),
}
}
});
TraceCollector { tx }
}
pub fn ascii<W: 'static + std::io::Write + Send>(w: W) -> TraceCollector {
let mut writer = preserves::value::TextWriter::new(w);
Self::new(move |event| match event {
CollectorEvent::Event(entry) => {
writer.write(&mut CapEncoder, &language().unparse(&entry))
.expect("failed to write TraceCollector entry");
writer.borrow_write().write_all(b"\n")
.expect("failed to write TraceCollector newline");
},
CollectorEvent::PeriodicFlush =>
writer.flush().expect("failed to flush TraceCollector output"),
})
}
pub fn packed<W: 'static + std::io::Write + Send>(w: W) -> TraceCollector {
let mut writer = preserves::value::PackedWriter::new(w);
Self::new(move |event| match event {
CollectorEvent::Event(entry) =>
writer.write(&mut CapEncoder, &language().unparse(&entry))
.expect("failed to write TraceCollector entry"),
CollectorEvent::PeriodicFlush =>
writer.flush().expect("failed to flush TraceCollector output"),
})
}
}
impl From<actor::Name> for Name {
fn from(v: actor::Name) -> Name {
match v {
None => Name::Anonymous,
Some(n) => Name::Named { name: n.clone() },
}
}
}

View File

@ -1,66 +0,0 @@
use crate::actor::*;
use std::fmt::Debug;
use std::io;
use std::sync::Arc;
struct Tracer(tracing::Span);
fn set_name_oid<M>(t: &mut Tracer, r: &Arc<Ref<M>>) {
t.0.record("oid", &tracing::field::display(&r.oid()));
}
pub fn tracer<M: Debug>(t: &mut Activation, name: tracing::Span) -> Arc<Ref<M>> {
let mut e = Tracer(name);
let r = t.create_inert();
set_name_oid(&mut e, &r);
r.become_entity(e);
r
}
impl<M: Debug> Entity<M> for Tracer {
fn assert(&mut self, _t: &mut Activation, a: M, h: Handle) -> ActorResult {
let _guard = self.0.enter();
tracing::trace!(?a, ?h, "assert");
Ok(())
}
fn retract(&mut self, _t: &mut Activation, h: Handle) -> ActorResult {
let _guard = self.0.enter();
tracing::trace!(?h, "retract");
Ok(())
}
fn message(&mut self, _t: &mut Activation, m: M) -> ActorResult {
let _guard = self.0.enter();
tracing::trace!(?m, "message");
Ok(())
}
fn sync(&mut self, t: &mut Activation, peer: Arc<Ref<Synced>>) -> ActorResult {
let _guard = self.0.enter();
tracing::trace!(?peer, "sync");
t.message(&peer, Synced);
Ok(())
}
}
/// Sets up [`tracing`] logging in a reasonable way.
///
/// Useful at the top of `main` functions.
pub fn convenient_logging() -> Result<(), Box<dyn std::error::Error>> {
let filter = match std::env::var(tracing_subscriber::filter::EnvFilter::DEFAULT_ENV) {
Err(std::env::VarError::NotPresent) =>
tracing_subscriber::filter::EnvFilter::default()
.add_directive(tracing_subscriber::filter::LevelFilter::INFO.into()),
_ =>
tracing_subscriber::filter::EnvFilter::try_from_default_env()?,
};
let subscriber = tracing_subscriber::fmt()
.with_ansi(true)
.with_thread_ids(true)
.with_max_level(tracing::Level::TRACE)
.with_env_filter(filter)
.with_writer(io::stderr)
.finish();
tracing::subscriber::set_global_default(subscriber)
.expect("Could not set tracing global subscriber");
Ok(())
}