Track and report Task::units to debug API
This commit is contained in:
parent
5f15522368
commit
121102e55b
3 changed files with 121 additions and 78 deletions
|
|
@ -17,8 +17,8 @@ pub fn theme_changed(f: impl FnOnce() -> Option<theme::Palette>) {
|
||||||
internal::theme_changed(f);
|
internal::theme_changed(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn commands_spawned(amount: usize) {
|
pub fn tasks_spawned(amount: usize) {
|
||||||
internal::commands_spawned(amount)
|
internal::tasks_spawned(amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscriptions_tracked(amount: usize) {
|
pub fn subscriptions_tracked(amount: usize) {
|
||||||
|
|
@ -114,7 +114,7 @@ mod internal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn commands_spawned(amount: usize) {
|
pub fn tasks_spawned(amount: usize) {
|
||||||
BEACON.log(client::Event::CommandsSpawned(amount));
|
BEACON.log(client::Event::CommandsSpawned(amount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,7 +215,7 @@ mod internal {
|
||||||
|
|
||||||
pub fn theme_changed(_f: impl FnOnce() -> Option<theme::Palette>) {}
|
pub fn theme_changed(_f: impl FnOnce() -> Option<theme::Palette>) {}
|
||||||
|
|
||||||
pub fn commands_spawned(_amount: usize) {}
|
pub fn tasks_spawned(_amount: usize) {}
|
||||||
|
|
||||||
pub fn subscriptions_tracked(_amount: usize) {}
|
pub fn subscriptions_tracked(_amount: usize) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,18 @@ pub use sipper::{Never, Sender, Sipper, Straw, sipper, stream};
|
||||||
/// A [`Task`] _may_ produce a bunch of values of type `T`.
|
/// A [`Task`] _may_ produce a bunch of values of type `T`.
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
#[must_use = "`Task` must be returned to the runtime to take effect; normally in your `update` or `new` functions."]
|
#[must_use = "`Task` must be returned to the runtime to take effect; normally in your `update` or `new` functions."]
|
||||||
pub struct Task<T>(Option<BoxStream<Action<T>>>);
|
pub struct Task<T> {
|
||||||
|
stream: Option<BoxStream<Action<T>>>,
|
||||||
|
units: usize,
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> Task<T> {
|
impl<T> Task<T> {
|
||||||
/// Creates a [`Task`] that does nothing.
|
/// Creates a [`Task`] that does nothing.
|
||||||
pub fn none() -> Self {
|
pub fn none() -> Self {
|
||||||
Self(None)
|
Self {
|
||||||
|
stream: None,
|
||||||
|
units: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`Task`] that instantly produces the given value.
|
/// Creates a new [`Task`] that instantly produces the given value.
|
||||||
|
|
@ -80,9 +86,16 @@ impl<T> Task<T> {
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
Self(Some(boxed_stream(stream::select_all(
|
let select_all = stream::select_all(
|
||||||
tasks.into_iter().filter_map(|task| task.0),
|
tasks.into_iter().filter_map(|task| task.stream),
|
||||||
))))
|
);
|
||||||
|
|
||||||
|
let units = select_all.len();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
stream: Some(boxed_stream(select_all)),
|
||||||
|
units,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps the output of a [`Task`] with the given closure.
|
/// Maps the output of a [`Task`] with the given closure.
|
||||||
|
|
@ -110,21 +123,26 @@ impl<T> Task<T> {
|
||||||
T: MaybeSend + 'static,
|
T: MaybeSend + 'static,
|
||||||
O: MaybeSend + 'static,
|
O: MaybeSend + 'static,
|
||||||
{
|
{
|
||||||
Task(match self.0 {
|
Task {
|
||||||
None => None,
|
stream: match self.stream {
|
||||||
Some(stream) => {
|
None => None,
|
||||||
Some(boxed_stream(stream.flat_map(move |action| {
|
Some(stream) => {
|
||||||
match action.output() {
|
Some(boxed_stream(stream.flat_map(move |action| {
|
||||||
Ok(output) => f(output)
|
match action.output() {
|
||||||
.0
|
Ok(output) => {
|
||||||
.unwrap_or_else(|| boxed_stream(stream::empty())),
|
f(output).stream.unwrap_or_else(|| {
|
||||||
Err(action) => {
|
boxed_stream(stream::empty())
|
||||||
boxed_stream(stream::once(async move { action }))
|
})
|
||||||
|
}
|
||||||
|
Err(action) => boxed_stream(stream::once(
|
||||||
|
async move { action },
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
})))
|
||||||
})))
|
}
|
||||||
}
|
},
|
||||||
})
|
units: self.units,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Chains a new [`Task`] to be performed once the current one finishes completely.
|
/// Chains a new [`Task`] to be performed once the current one finishes completely.
|
||||||
|
|
@ -132,11 +150,17 @@ impl<T> Task<T> {
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
match self.0 {
|
match self.stream {
|
||||||
None => task,
|
None => task,
|
||||||
Some(first) => match task.0 {
|
Some(first) => match task.stream {
|
||||||
None => Task(Some(first)),
|
None => Self {
|
||||||
Some(second) => Task(Some(boxed_stream(first.chain(second)))),
|
stream: Some(first),
|
||||||
|
units: self.units,
|
||||||
|
},
|
||||||
|
Some(second) => Self {
|
||||||
|
stream: Some(boxed_stream(first.chain(second))),
|
||||||
|
units: self.units + task.units,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -146,35 +170,39 @@ impl<T> Task<T> {
|
||||||
where
|
where
|
||||||
T: MaybeSend + 'static,
|
T: MaybeSend + 'static,
|
||||||
{
|
{
|
||||||
match self.0 {
|
match self.stream {
|
||||||
None => Task::done(Vec::new()),
|
None => Task::done(Vec::new()),
|
||||||
Some(stream) => Task(Some(boxed_stream(
|
Some(stream) => Task {
|
||||||
stream::unfold(
|
stream: Some(boxed_stream(
|
||||||
(stream, Some(Vec::new())),
|
stream::unfold(
|
||||||
move |(mut stream, outputs)| async move {
|
(stream, Some(Vec::new())),
|
||||||
let mut outputs = outputs?;
|
move |(mut stream, outputs)| async move {
|
||||||
|
let mut outputs = outputs?;
|
||||||
|
|
||||||
let Some(action) = stream.next().await else {
|
let Some(action) = stream.next().await else {
|
||||||
return Some((
|
return Some((
|
||||||
Some(Action::Output(outputs)),
|
Some(Action::Output(outputs)),
|
||||||
(stream, None),
|
(stream, None),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
match action.output() {
|
match action.output() {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
outputs.push(output);
|
outputs.push(output);
|
||||||
|
|
||||||
Some((None, (stream, Some(outputs))))
|
Some((None, (stream, Some(outputs))))
|
||||||
|
}
|
||||||
|
Err(action) => Some((
|
||||||
|
Some(action),
|
||||||
|
(stream, Some(outputs)),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
Err(action) => {
|
},
|
||||||
Some((Some(action), (stream, Some(outputs))))
|
)
|
||||||
}
|
.filter_map(future::ready),
|
||||||
}
|
)),
|
||||||
},
|
units: self.units,
|
||||||
)
|
},
|
||||||
.filter_map(future::ready),
|
|
||||||
))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,26 +222,25 @@ impl<T> Task<T> {
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
match self.0 {
|
let (stream, handle) = match self.stream {
|
||||||
Some(stream) => {
|
Some(stream) => {
|
||||||
let (stream, handle) = stream::abortable(stream);
|
let (stream, handle) = stream::abortable(stream);
|
||||||
|
|
||||||
(
|
(Some(boxed_stream(stream)), InternalHandle::Manual(handle))
|
||||||
Self(Some(boxed_stream(stream))),
|
|
||||||
Handle {
|
|
||||||
internal: InternalHandle::Manual(handle),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
None => (
|
None => (
|
||||||
Self(None),
|
None,
|
||||||
Handle {
|
InternalHandle::Manual(stream::AbortHandle::new_pair().0),
|
||||||
internal: InternalHandle::Manual(
|
|
||||||
stream::AbortHandle::new_pair().0,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
Self {
|
||||||
|
stream,
|
||||||
|
units: self.units,
|
||||||
|
},
|
||||||
|
Handle { internal: handle },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`Task`] that runs the given [`Future`] and produces
|
/// Creates a new [`Task`] that runs the given [`Future`] and produces
|
||||||
|
|
@ -231,7 +258,15 @@ impl<T> Task<T> {
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
Self(Some(boxed_stream(stream.map(Action::Output))))
|
Self {
|
||||||
|
stream: Some(boxed_stream(stream.map(Action::Output))),
|
||||||
|
units: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of work "units" of the [`Task`].
|
||||||
|
pub fn units(&self) -> usize {
|
||||||
|
self.units
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -365,13 +400,14 @@ where
|
||||||
|
|
||||||
let action = f(sender);
|
let action = f(sender);
|
||||||
|
|
||||||
Task(Some(boxed_stream(
|
Task {
|
||||||
stream::once(async move { action }).chain(
|
stream: Some(boxed_stream(stream::once(async move { action }).chain(
|
||||||
receiver.into_stream().filter_map(|result| async move {
|
receiver.into_stream().filter_map(|result| async move {
|
||||||
Some(Action::Output(result.ok()?))
|
Some(Action::Output(result.ok()?))
|
||||||
}),
|
}),
|
||||||
),
|
))),
|
||||||
)))
|
units: 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`Task`] that executes the [`Action`] returned by the closure and
|
/// Creates a new [`Task`] that executes the [`Action`] returned by the closure and
|
||||||
|
|
@ -384,22 +420,28 @@ where
|
||||||
|
|
||||||
let action = f(sender);
|
let action = f(sender);
|
||||||
|
|
||||||
Task(Some(boxed_stream(
|
Task {
|
||||||
stream::once(async move { action })
|
stream: Some(boxed_stream(
|
||||||
.chain(receiver.map(|result| Action::Output(result))),
|
stream::once(async move { action })
|
||||||
)))
|
.chain(receiver.map(|result| Action::Output(result))),
|
||||||
|
)),
|
||||||
|
units: 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`Task`] that executes the given [`Action`] and produces no output.
|
/// Creates a new [`Task`] that executes the given [`Action`] and produces no output.
|
||||||
pub fn effect<T>(action: impl Into<Action<Never>>) -> Task<T> {
|
pub fn effect<T>(action: impl Into<Action<Never>>) -> Task<T> {
|
||||||
let action = action.into();
|
let action = action.into();
|
||||||
|
|
||||||
Task(Some(boxed_stream(stream::once(async move {
|
Task {
|
||||||
action.output().expect_err("no output")
|
stream: Some(boxed_stream(stream::once(async move {
|
||||||
}))))
|
action.output().expect_err("no output")
|
||||||
|
}))),
|
||||||
|
units: 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the underlying [`Stream`] of the [`Task`].
|
/// Returns the underlying [`Stream`] of the [`Task`].
|
||||||
pub fn into_stream<T>(task: Task<T>) -> Option<BoxStream<Action<T>>> {
|
pub fn into_stream<T>(task: Task<T>) -> Option<BoxStream<Action<T>>> {
|
||||||
task.0
|
task.stream
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1100,6 +1100,7 @@ fn update<P: Program, E: Executor>(
|
||||||
for message in messages.drain(..) {
|
for message in messages.drain(..) {
|
||||||
let update_span = debug::update(&message);
|
let update_span = debug::update(&message);
|
||||||
let task = runtime.enter(|| program.update(message));
|
let task = runtime.enter(|| program.update(message));
|
||||||
|
debug::tasks_spawned(task.units());
|
||||||
update_span.finish();
|
update_span.finish();
|
||||||
|
|
||||||
if let Some(stream) = runtime::task::into_stream(task) {
|
if let Some(stream) = runtime::task::into_stream(task) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue