Skip to content

Commit

Permalink
Trace correct subject in bottom-up builds.
Browse files Browse the repository at this point in the history
  • Loading branch information
Gohla committed Jan 11, 2024
1 parent ff5335a commit cd7c7e6
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 146 deletions.
25 changes: 13 additions & 12 deletions pie/src/context/bottom_up.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,11 @@ impl<'p, 's> BottomUpContext<'p, 's> {
if self.executing.contains(&requiring_task_node) {
continue; // Don't schedule tasks that are already executing.
}
let requiring_task = self.session.store.get_task(&requiring_task_node).as_key_obj();
// Note: use `output.as_ref()` instead of `&output`, because `&output` results in a `&Box<dyn ValueObj>` which
// also implements `dyn ValueObj`, but cannot be downcasted to the concrete unboxed type!
if !dependency.is_consistent_with(output.as_ref(), &mut self.session.tracker) {
let requiring_task = self.session.store.get_task(&requiring_task_node);
self.session.tracker.schedule_task(requiring_task.as_key_obj());
if !dependency.is_consistent_bottom_up(output.as_ref(), requiring_task, &mut self.session.tracker) {
self.session.tracker.schedule_task(requiring_task);
self.scheduled.add(requiring_task_node);
}
}
Expand All @@ -108,12 +108,13 @@ impl<'p, 's> BottomUpContext<'p, 's> {
output
}

/// Schedule `task` (with corresponding `node`) if it is affected by a change in its resource `dependency`.
/// Schedule `reading_task` (with corresponding `reading_task_node`) if it is affected by a change in its resource
/// `dependency`.
///
/// Note: passing in borrows explicitly instead of a mutable borrow of `self` to make borrows work.
fn try_schedule_task_by_resource_dependency(
task: &dyn KeyObj,
node: TaskNode,
reading_task: &dyn KeyObj,
reading_task_node: TaskNode,
dependency: &dyn ResourceDependencyObj,
resource_state: &mut TypeToAnyMap,
tracker: &mut Tracking,
Expand All @@ -123,19 +124,19 @@ impl<'p, 's> BottomUpContext<'p, 's> {
) {
// TODO: skip when task is already consistent?
// TODO: skip when task is already scheduled?
if executing.contains(&node) {
if executing.contains(&reading_task_node) {
return; // Don't schedule tasks that are already executing.
}
let consistent = dependency.is_consistent(tracker, resource_state);
let consistent = dependency.is_consistent_bottom_up(resource_state, reading_task, tracker);
match consistent {
Err(e) => {
dependency_check_errors.push(e);
tracker.schedule_task(task);
scheduled.add(node);
tracker.schedule_task(reading_task);
scheduled.add(reading_task_node);
}
Ok(false) => {
tracker.schedule_task(task);
scheduled.add(node);
tracker.schedule_task(reading_task);
scheduled.add(reading_task_node);
}
_ => {}
}
Expand Down
12 changes: 6 additions & 6 deletions pie/src/context/top_down.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ impl TopDownContext<'_, '_> {
for dependency in dependencies.into_iter() {
let consistent = match dependency {
Dependency::ReservedRequire => panic!("BUG: attempt to consistency check reserved require task dependency"),
Dependency::Require(d) => Ok(d.as_check_task_dependency().is_consistent(self)),
Dependency::Read(d) | Dependency::Write(d) => d.is_consistent(
&mut self.session.tracker,
Dependency::Require(d) => Ok(d.as_top_down_check().is_consistent(self)),
Dependency::Read(d) | Dependency::Write(d) => d.is_consistent_top_down(
&mut self.session.resource_state,
&mut self.session.tracker,
),
};
match consistent {
Expand All @@ -147,15 +147,15 @@ impl TopDownContext<'_, '_> {
/// Internal trait for top-down recursive checking of task dependencies.
///
/// Object-safe trait.
pub trait CheckTaskDependency {
pub trait TopDownCheck {
fn is_consistent(&self, context: &mut TopDownContext) -> bool;
}
impl<T: Task, C: OutputChecker<T::Output>> CheckTaskDependency for TaskDependency<T, C, C::Stamp> {
impl<T: Task, C: OutputChecker<T::Output>> TopDownCheck for TaskDependency<T, C, C::Stamp> {
#[inline]
fn is_consistent(&self, context: &mut TopDownContext) -> bool {
let check_task_end = context.session.tracker.check_task(self.task(), self.checker(), self.stamp());
let output = context.make_task_consistent(self.task());
let inconsistency = self.get_inconsistency(&output);
let inconsistency = self.check(&output);
let inconsistency_dyn = inconsistency.as_ref().map(|o| o as &dyn Debug);
check_task_end(&mut context.session.tracker, inconsistency_dyn);
inconsistency.is_none()
Expand Down
86 changes: 63 additions & 23 deletions pie/src/dependency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::fmt::Debug;
use dyn_clone::DynClone;

use crate::{OutputChecker, Resource, ResourceChecker, ResourceState, Task};
use crate::context::top_down::CheckTaskDependency;
use crate::context::top_down::TopDownCheck;
use crate::pie::Tracking;
use crate::trait_object::{KeyObj, ValueObj};
use crate::trait_object::collection::TypeToAnyMap;
Expand All @@ -29,8 +29,8 @@ impl<T: Task, C: OutputChecker<T::Output>> TaskDependency<T, C, C::Stamp> {
pub fn stamp(&self) -> &C::Stamp { &self.stamp }

#[inline]
pub fn get_inconsistency<'o>(&'o self, output: &'o T::Output) -> Option<C::Inconsistency<'o>> {
self.checker.get_inconsistency(output, &self.stamp)
pub fn check<'i>(&'i self, output: &'i T::Output) -> Option<C::Inconsistency<'i>> {
self.checker.check(output, &self.stamp)
}

#[inline]
Expand All @@ -45,8 +45,8 @@ pub trait TaskDependencyObj: DynClone + Debug {
fn checker(&self) -> &dyn ValueObj;
fn stamp(&self) -> &dyn ValueObj;

fn as_check_task_dependency(&self) -> &dyn CheckTaskDependency;
fn is_consistent_with(&self, output: &dyn ValueObj, tracker: &mut Tracking) -> bool;
fn as_top_down_check(&self) -> &dyn TopDownCheck;
fn is_consistent_bottom_up(&self, output: &dyn ValueObj, requiring_task: &dyn KeyObj, tracker: &mut Tracking) -> bool;
}

impl<T: Task, C: OutputChecker<T::Output>> TaskDependencyObj for TaskDependency<T, C, C::Stamp> {
Expand All @@ -58,17 +58,23 @@ impl<T: Task, C: OutputChecker<T::Output>> TaskDependencyObj for TaskDependency<
fn stamp(&self) -> &dyn ValueObj { &self.stamp as &dyn ValueObj }

#[inline]
fn as_check_task_dependency(&self) -> &dyn CheckTaskDependency { self as &dyn CheckTaskDependency }
fn as_top_down_check(&self) -> &dyn TopDownCheck { self as &dyn TopDownCheck }
#[inline]
fn is_consistent_with(&self, output: &dyn ValueObj, tracker: &mut Tracking) -> bool {
fn is_consistent_bottom_up(&self, output: &dyn ValueObj, requiring_task: &dyn KeyObj, tracker: &mut Tracking) -> bool {
let Some(output) = output.as_any().downcast_ref::<T::Output>() else {
return false;
};
let check_task_end = tracker.check_task(&self.task, &self.checker, &self.stamp);
let inconsistency = self.get_inconsistency(output);
let inconsistency_dyn = inconsistency.as_ref().map(|o| o as &dyn Debug);
check_task_end(tracker, inconsistency_dyn);
inconsistency.is_none()
let check_task_end = tracker.check_task_require_task(requiring_task, &self.checker, &self.stamp);
match self.check(output) {
Some(inconsistency) => {
check_task_end(tracker, Some(&inconsistency as &dyn Debug));
false
}
None => {
check_task_end(tracker, None);
true
}
}
}
}
impl Clone for Box<dyn TaskDependencyObj> {
Expand Down Expand Up @@ -97,11 +103,26 @@ impl<R: Resource, C: ResourceChecker<R>> ResourceDependency<R, C, C::Stamp> {
pub fn stamp(&self) -> &C::Stamp { &self.stamp }

#[inline]
pub fn get_inconsistency<'i, RS: ResourceState<R>>(
pub fn check<'i, RS: ResourceState<R>>(
&'i self,
state: &'i mut RS,
) -> Result<Option<C::Inconsistency<'i>>, C::Error> {
self.checker.get_inconsistency(&self.resource, state, &self.stamp)
self.checker.check(&self.resource, state, &self.stamp)
}

#[inline]
pub fn is_consistent<'i, RS: ResourceState<R>>(
&'i self,
state: &'i mut RS,
tracker: &mut Tracking,
track_end: impl FnOnce(&mut Tracking, Result<Option<&dyn Debug>, &dyn Error>),
) -> Result<bool, Box<dyn Error>> {
let inconsistency = self.check(state);
let inconsistency_dyn = inconsistency.as_ref()
.map(|o| o.as_ref().map(|i| i as &dyn Debug))
.map_err(|e| e as &dyn Error);
track_end(tracker, inconsistency_dyn);
Ok(inconsistency?.is_none())
}

#[inline]
Expand All @@ -118,7 +139,17 @@ pub trait ResourceDependencyObj: DynClone + Debug {
fn checker(&self) -> &dyn ValueObj;
fn stamp(&self) -> &dyn ValueObj;

fn is_consistent(&self, tracker: &mut Tracking, resource_state: &mut TypeToAnyMap) -> Result<bool, Box<dyn Error>>;
fn is_consistent_top_down(
&self,
resource_state: &mut TypeToAnyMap,
tracker: &mut Tracking,
) -> Result<bool, Box<dyn Error>>;
fn is_consistent_bottom_up(
&self,
resource_state: &mut TypeToAnyMap,
reading_task: &dyn KeyObj,
tracker: &mut Tracking,
) -> Result<bool, Box<dyn Error>>;
}
impl<R: Resource, C: ResourceChecker<R>> ResourceDependencyObj for ResourceDependency<R, C, C::Stamp> {
#[inline]
Expand All @@ -129,14 +160,23 @@ impl<R: Resource, C: ResourceChecker<R>> ResourceDependencyObj for ResourceDepen
fn stamp(&self) -> &dyn ValueObj { &self.stamp as &dyn ValueObj }

#[inline]
fn is_consistent(&self, tracker: &mut Tracking, state: &mut TypeToAnyMap) -> Result<bool, Box<dyn Error>> {
let check_resource_end = tracker.check_resource(&self.resource, &self.checker, &self.stamp);
let result = self.get_inconsistency(state);
let inconsistency = result.as_ref()
.map(|o| o.as_ref().map(|i| i as &dyn Debug))
.map_err(|e| e as &dyn Error);
check_resource_end(tracker, inconsistency);
Ok(result?.is_none())
fn is_consistent_top_down(
&self,
resource_state: &mut TypeToAnyMap,
tracker: &mut Tracking,
) -> Result<bool, Box<dyn Error>> {
let track_end = tracker.check_resource(&self.resource, &self.checker, &self.stamp);
self.is_consistent(resource_state, tracker, track_end)
}
#[inline]
fn is_consistent_bottom_up(
&self,
resource_state: &mut TypeToAnyMap,
reading_task: &dyn KeyObj,
tracker: &mut Tracking,
) -> Result<bool, Box<dyn Error>> {
let track_end = tracker.check_task_read_resource(reading_task, &self.checker, &self.stamp);
self.is_consistent(resource_state, tracker, track_end)
}
}
impl Clone for Box<dyn ResourceDependencyObj> {
Expand Down
8 changes: 4 additions & 4 deletions pie/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ pub trait OutputChecker<O>: KeyBounds {
fn stamp(&self, output: &O) -> Self::Stamp;

/// Type of inconsistency used for debugging/logging purposes. The `'i` lifetime represents this checker, or the
/// output/stamp passed to [Self::get_inconsistency].
/// output/stamp passed to [Self::check].
type Inconsistency<'i>: Debug where O: 'i;
/// Checks whether `output` is inconsistent w.r.t. `stamp`, returning `Some(inconsistency)` if inconsistent, `None` if
/// consistent.
fn get_inconsistency<'i>(&'i self, output: &'i O, stamp: &'i Self::Stamp) -> Option<Self::Inconsistency<'i>>;
fn check<'i>(&'i self, output: &'i O, stamp: &'i Self::Stamp) -> Option<Self::Inconsistency<'i>>;
}


Expand Down Expand Up @@ -188,11 +188,11 @@ pub trait ResourceChecker<R: Resource>: KeyBounds {
fn stamp_writer(&self, resource: &R, writer: R::Writer<'_>) -> Result<Self::Stamp, Self::Error>;

/// Type of inconsistency used for debugging/logging purposes. The `'i` lifetime represents this checker and the
/// lifetime of the `resource`, `state`, and `stamp` passed to [Self::get_inconsistency].
/// lifetime of the `resource`, `state`, and `stamp` passed to [Self::check].
type Inconsistency<'i>: Debug;
/// Checks whether `resource` is inconsistent w.r.t. `stamp`, with access to `state`. Returns `Some(inconsistency)`
/// when inconsistent, `None` when consistent.
fn get_inconsistency<'i, RS: ResourceState<R>>(
fn check<'i, RS: ResourceState<R>>(
&'i self,
resource: &'i R,
state: &'i mut RS,
Expand Down
34 changes: 28 additions & 6 deletions pie/src/pie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,27 @@ impl Tracking<'_> {
|tracking, output| tracking.0.execute_end(task, output)
}

#[inline]
#[must_use]
pub fn schedule_affected_by_task<'a>(
&mut self,
task: &'a dyn KeyObj,
) -> impl FnOnce(&mut Tracking) + 'a {
self.0.schedule_affected_by_task_start(task);
|tracking| tracking.0.schedule_affected_by_task_end(task)
}
#[inline]
#[must_use]
pub fn check_task_require_task<'a>(
&mut self,
requiring_task: &'a dyn KeyObj,
checker: &'a dyn ValueObj,
stamp: &'a dyn ValueObj,
) -> impl FnOnce(&mut Tracking, Option<&dyn Debug>) + 'a {
self.0.check_task_require_task_start(requiring_task, checker, stamp);
|tracking, inconsistency| tracking.0.check_task_require_task_end(requiring_task, checker, stamp, inconsistency)
}

#[inline]
#[must_use]
pub fn schedule_affected_by_resource<'a>(
Expand All @@ -192,15 +213,16 @@ impl Tracking<'_> {
self.0.schedule_affected_by_resource_start(resource);
|tracking| tracking.0.schedule_affected_by_resource_end(resource)
}

#[inline]
#[must_use]
pub fn schedule_affected_by_task<'a>(
pub fn check_task_read_resource<'a>(
&mut self,
task: &'a dyn KeyObj,
) -> impl FnOnce(&mut Tracking) + 'a {
self.0.schedule_affected_by_task_start(task);
|tracking| tracking.0.schedule_affected_by_task_end(task)
reading_task: &'a dyn KeyObj,
checker: &'a dyn ValueObj,
stamp: &'a dyn ValueObj,
) -> impl FnOnce(&mut Tracking, Result<Option<&dyn Debug>, &dyn Error>) + 'a {
self.0.check_task_read_resource_start(reading_task, checker, stamp);
|tracking, inconsistency| tracking.0.check_task_read_resource_end(reading_task, checker, stamp, inconsistency)
}
}
impl<'p> Deref for Tracking<'p> {
Expand Down
Loading

0 comments on commit cd7c7e6

Please sign in to comment.