Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misc comments in turbo_tasks #5723

Merged
merged 1 commit into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions crates/turbo-tasks/src/task/concrete_task_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,12 @@ impl<'de> Deserialize<'de> for SharedValue {
}
}

/// Intermediate representation of task inputs.
///
/// When a task is called, all its arguments will be converted and stored as
/// [`ConcreteTaskInput`]s. When the task is actually run, these inputs will be
/// converted back into the argument types. This is handled by the [`TaskInput`]
/// trait.
#[allow(clippy::derived_hash_with_manual_eq)]
#[derive(Debug, Hash, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum ConcreteTaskInput {
Expand Down
32 changes: 30 additions & 2 deletions crates/turbo-tasks/src/task/function.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
//! # Function tasks
//!
//! This module contains the trait definitions and implementations that are
//! necessary for accepting functions as tasks when using the
//! `turbo_tasks::function` macro.
//!
//! This system is inspired by Bevy's Systems and Axum's Handlers.
//!
//! The original principle is somewhat simple: a function is accepted if all
//! of its arguments implement `TaskInput` and its return type implements
//! `TaskOutput`. There are a few hoops one needs to jump through to make this
//! work, but they are described in this blog post:
//! https://blog.logrocket.com/rust-bevy-entity-component-system/
//!
//! However, there are is an additional complication in our case: async methods
//! that accept a reference to the receiver as their first argument.
//!
//! This complication handled through our own version of the `async_trait`
//! crate, which allows us to target `async fn` as trait bounds. The naive
//! approach runs into many issues with lifetimes, hence the need for an
//! intermediate trait. However, this implementation doesn't support all async
//! methods (see commented out tests).

use std::{future::Future, marker::PhantomData, pin::Pin};

use anyhow::{bail, Context, Result};
Expand Down Expand Up @@ -56,10 +79,15 @@ trait TaskFnInputFunction<Mode: TaskFnMode, Inputs: TaskInputs>: Send + Sync + C
fn functor(&self, name: &'static str, inputs: &[ConcreteTaskInput]) -> Result<NativeTaskFn>;
}

pub trait TaskFnMode: Send + Sync + 'static {}

pub trait TaskInputs: Send + Sync + 'static {}

/// Modes to allow multiple `TaskFnInputFunction` blanket implementations on
/// `Fn`s. Even though the implementations are non-conflicting in practice, they
/// could be in theory (at least from with the compiler's current limitations).
/// Despite this, the compiler is still able to infer the correct mode from a
/// function.
pub trait TaskFnMode: Send + Sync + 'static {}

pub struct FunctionMode;
impl TaskFnMode for FunctionMode {}

Expand Down
5 changes: 5 additions & 0 deletions crates/turbo-tasks/src/task/task_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ use crate::{
TypedForInput, Value, Vc, VcValueType,
};

/// Trait to implement in order for a type to be accepted as a
/// `turbo_tasks::function` argument.
///
/// See also [`ConcreteTaskInput`].
pub trait TaskInput: Send + Sync + Clone {
fn try_from_concrete(input: &ConcreteTaskInput) -> Result<Self>;
fn into_concrete(self) -> ConcreteTaskInput;
Expand Down Expand Up @@ -306,6 +310,7 @@ macro_rules! tuple_impls {
};
}

// Implement `TaskInput` for all tuples of 1 to 12 elements.
tuple_impls! { A }
tuple_impls! { A B }
tuple_impls! { A B C }
Expand Down
2 changes: 2 additions & 0 deletions crates/turbo-tasks/src/task/task_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use anyhow::Result;

use crate::{unit, RawVc, Vc};

/// Trait to implement in order for a type to be accepted as a
/// `turbo_tasks::function` return type.
pub trait TaskOutput {
type Return;

Expand Down
12 changes: 11 additions & 1 deletion crates/turbo-tasks/src/unit.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
use crate::Vc;
use crate::{ValueDefault, Vc};

// TODO(alexkirsz) Should this be `#[turbo_tasks::function]` or is it okay to
// always return a new `Vc`?
pub fn unit() -> Vc<()> {
Vc::cell(())
}

impl ValueDefault for () {
// TODO(alexkirsz) Should this be `#[turbo_tasks::function]` or is it
// preferrable to always return a new `Vc`?
fn value_default() -> Vc<Self> {
Vc::cell(())
}
}
11 changes: 11 additions & 0 deletions crates/turbo-tasks/src/vc/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ use turbo_tasks::Vc;

use crate::{self as turbo_tasks};

/// `Vc<T>` analog to the `Default` trait.
///
/// Implementing this trait on `T` will make `Vc::default()` produce
/// `T::value_default()`.
///
/// There are two ways to implement this trait:
/// 1. Annotating with `#[turbo_tasks::value_impl]`: this will make
/// `Vc::default()` always return the same underlying value (i.e. a
/// singleton).
/// 2. No annotations: this will make `Vc::default()` always return a different
/// value.
#[turbo_tasks::value_trait]
pub trait ValueDefault {
fn value_default() -> Vc<Self>;
Expand Down
5 changes: 3 additions & 2 deletions crates/turbo-tasks/src/vc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,16 @@ where
pub(crate) _t: PhantomData<T>,
}

/// This only exists to satisfy the Rust type system. However, this struct can
/// never actually be instantiated, as dereferencing a `Vc<T>` will result in a
/// linker error. See the implementation of `Deref` for `Vc<T>`.
pub struct VcDeref<T>
where
T: ?Sized,
{
_t: PhantomData<T>,
}

trait Impossible {}

macro_rules! do_not_use_or_you_will_be_fired {
($($name:ident)*) => {
impl<T> VcDeref<T>
Expand Down
Loading