Skip to content

Commit

Permalink
rune: Store Future in AnyObj instead of Mutable (relates #844)
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed Nov 1, 2024
1 parent d37cadf commit 87d9e1f
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 77 deletions.
62 changes: 30 additions & 32 deletions crates/rune/src/modules/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@

use crate as rune;
use crate::alloc::Vec;
use crate::runtime::{
self, Future, Inline, Mut, Mutable, RefRepr, SelectFuture, Value, VmErrorKind, VmResult,
};
use crate::runtime::{self, Future, Inline, RefRepr, SelectFuture, Value, VmErrorKind, VmResult};
use crate::{ContextError, Module, TypeHash};

/// Asynchronous computations.
#[rune::module(::std::future)]
pub fn module() -> Result<Module, ContextError> {
let mut module = Module::from_meta(self::module_meta)?;
module.ty::<Future>()?;
module.function_meta(join)?;
module.function_meta(join__meta)?;
Ok(module)
}

Expand All @@ -27,39 +25,33 @@ where
let mut results = vm_try!(Vec::try_with_capacity(len));

for (index, value) in values.into_iter().enumerate() {
let value = match vm_try!(value.as_ref_repr()) {
match vm_try!(value.as_ref_repr()) {
RefRepr::Inline(value) => {
return VmResult::err([
VmErrorKind::expected::<Future>(value.type_info()),
VmErrorKind::bad_argument(index),
]);
}
RefRepr::Mutable(value) => vm_try!(value.clone().into_mut()),
RefRepr::Any(value) => {
RefRepr::Mutable(value) => {
return VmResult::err([
VmErrorKind::expected::<Future>(value.type_info()),
VmErrorKind::expected::<Future>(vm_try!(value.borrow_ref()).type_info()),
VmErrorKind::bad_argument(index),
]);
}
};

let future = Mut::try_map(value, |kind| match kind {
Mutable::Future(future) => Some(future),
_ => None,
});

let future = match future {
Ok(future) => future,
Err(actual) => {
return VmResult::err([
VmErrorKind::expected::<Future>(actual.type_info()),
VmErrorKind::bad_argument(index),
]);
}
};

futures.push(SelectFuture::new(index, future));
vm_try!(results.try_push(Value::empty()));
RefRepr::Any(value) => match value.type_hash() {
Future::HASH => {
let future = vm_try!(Value::from(value.clone()).into_future());
futures.push(SelectFuture::new(index, future));
vm_try!(results.try_push(Value::empty()));
}
_ => {
return VmResult::err([
VmErrorKind::expected::<Future>(value.type_info()),
VmErrorKind::bad_argument(index),
]);
}
},
}
}

while !futures.is_empty() {
Expand All @@ -75,30 +67,36 @@ where
/// # Examples
///
/// ```rune
/// use std::future;
///
/// let a = async { 1 };
/// let b = async { 2 };
/// let (a, b) = std::future::join((a, b)).await;
/// let (a, b) = future::join((a, b)).await;
/// assert_eq!(1, a);
/// assert_eq!(2, b);
/// ```
///
/// Using a vector:
///
/// ```rune
/// use std::future;
///
/// let a = async { 1 };
/// let b = async { 2 };
/// let [a, b] = std::future::join([a, b]).await;
/// let [a, b] = future::join([a, b]).await;
/// assert_eq!(1, a);
/// assert_eq!(2, b);
/// ```
///
/// Joining an empty collection:
///
/// ```rune
/// let () = std::future::join(()).await;
/// let [] = std::future::join([]).await;
/// use std::future;
///
/// let () = future::join(()).await;
/// let [] = future::join([]).await;
/// ```
#[rune::function]
#[rune::function(keep)]
async fn join(value: Value) -> VmResult<Value> {
match vm_try!(value.as_ref_repr()) {
RefRepr::Inline(value) => match value {
Expand Down
27 changes: 26 additions & 1 deletion crates/rune/src/runtime/any_obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ impl AnyObj {
/// Returns `None` if the interior type is not `T`.
///
/// This prevents other exclusive accesses from being performed while the
/// guard returned from this function is live.
/// guard returned from this function is alive.
pub fn try_borrow_ref<T>(&self) -> Result<Option<BorrowRef<'_, T>>, AccessError>
where
T: Any,
Expand All @@ -408,6 +408,31 @@ impl AnyObj {
}
}

/// Try to borrow a reference to the interior value while checking for
/// exclusive access.
///
/// Returns `None` if the interior type is not `T`.
///
/// This prevents other exclusive accesses from being performed while the
/// guard returned from this function is alive.
pub fn try_borrow_mut<T>(&self) -> Result<Option<BorrowMut<'_, T>>, AccessError>
where
T: Any,
{
let vtable = vtable(self);

if (vtable.type_id)() != TypeId::of::<T>() {
return Ok(None);
}

// SAFETY: We've checked for the appropriate type just above.
unsafe {
let guard = self.shared.as_ref().access.exclusive()?;
let data = vtable.as_ptr(self.shared);
Ok(Some(BorrowMut::new(data, guard.into_raw())))
}
}

/// Returns some mutable reference to the boxed value if it is of type `T`.
pub fn borrow_mut<T>(&self) -> Result<BorrowMut<'_, T>, AnyObjError>
where
Expand Down
18 changes: 6 additions & 12 deletions crates/rune/src/runtime/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ use crate::alloc::fmt::TryWrite;
use crate::alloc::prelude::*;
use crate::alloc::{self, Box, Vec};
use crate::function;
use crate::runtime;
use crate::runtime::vm::Isolated;
use crate::shared::AssertSend;
use crate::Any;
use crate::Hash;

use super::{
Args, Call, ConstValue, Formatter, FromValue, FunctionHandler, GuardedArgs, InstAddress,
Mutable, Output, OwnedTuple, RefRepr, Rtti, RuntimeContext, RuntimeError, Stack, Unit, Value,
VariantRtti, Vm, VmCall, VmErrorKind, VmHalt, VmResult,
Output, OwnedTuple, Rtti, RuntimeContext, RuntimeError, Stack, Unit, Value, VariantRtti, Vm,
VmCall, VmErrorKind, VmHalt, VmResult,
};

/// The type of a function in Rune.
Expand Down Expand Up @@ -611,16 +612,9 @@ where
let future = async move {
let value: Value = vm_try!(self.call(args));

let value = 'out: {
if let RefRepr::Mutable(value) = vm_try!(value.as_ref_repr()) {
let mut value = vm_try!(value.borrow_mut());

if let Mutable::Future(future) = &mut *value {
break 'out vm_try!(future.await);
}
}

value
let value = match vm_try!(value.try_borrow_mut::<runtime::Future>()) {
Some(future) => vm_try!(future.await),
None => value,
};

VmResult::Ok(vm_try!(T::from_value(value)))
Expand Down
4 changes: 1 addition & 3 deletions crates/rune/src/runtime/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ struct Vtable {
/// the virtual machine that created it.
#[derive(Any)]
#[rune(crate)]
#[rune(builtin, static_type = FUTURE)]
#[rune(from_value = Value::into_future)]
#[rune(item = ::std::future)]
pub struct Future {
future: Option<NonNull<()>>,
Expand All @@ -31,7 +29,7 @@ pub struct Future {

impl Future {
/// Construct a new wrapped future.
pub fn new<T, O>(future: T) -> alloc::Result<Self>
pub(crate) fn new<T, O>(future: T) -> alloc::Result<Self>
where
T: 'static + future::Future<Output = VmResult<O>>,
O: ToValue,
Expand Down
2 changes: 0 additions & 2 deletions crates/rune/src/runtime/static_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,6 @@ static_type! {
impl<T> for ::std::collections::HashMap<alloc::String, T>;
}

pub(crate) static [FUTURE, FUTURE_HASH] = ::std::future::Future {}

pub(crate) static [RESULT, RESULT_HASH] = ::std::result::Result {
impl<T, E> for Result<T, E>;
}
Expand Down
54 changes: 29 additions & 25 deletions crates/rune/src/runtime/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,6 @@ impl Value {
Mutable::TupleStruct(value) => Mutable::TupleStruct(vm_try!(value.try_clone())),
Mutable::Struct(value) => Mutable::Struct(vm_try!(value.try_clone())),
Mutable::Variant(value) => Mutable::Variant(vm_try!(value.try_clone())),
_ => {
break 'fallback;
}
},
RefRepr::Any(..) => {
break 'fallback;
Expand Down Expand Up @@ -490,9 +487,6 @@ impl Value {
Mutable::Object(value) => {
vm_try!(vm_write!(f, "{value:?}"));
}
Mutable::Future(value) => {
vm_try!(vm_write!(f, "{value:?}"));
}
Mutable::Option(value) => {
vm_try!(vm_write!(f, "{value:?}"));
}
Expand Down Expand Up @@ -819,15 +813,6 @@ impl Value {
into_object,
}

into_base! {
/// Coerce into a [`Future`].
Future(Future),
into_future_ref,
into_future_mut,
borrow_future_ref,
borrow_future_mut,
}

/// Borrow as a tuple.
///
/// This ensures that the value has read access to the underlying value
Expand Down Expand Up @@ -956,11 +941,15 @@ impl Value {
/// [`Vm`]: crate::Vm
#[inline]
pub fn into_future(self) -> Result<Future, RuntimeError> {
let target = match self.take_repr()? {
OwnedRepr::Mutable(Mutable::Future(future)) => return Ok(future),
OwnedRepr::Inline(value) => Value::from(value),
OwnedRepr::Mutable(value) => Value::try_from(value)?,
OwnedRepr::Any(value) => Value::from(value),
let target = match self.repr {
Repr::Empty => return Err(RuntimeError::from(AccessError::empty())),
Repr::Any(value) => match value.type_hash() {
Future::HASH => {
return Ok(value.downcast::<Future>()?);
}
_ => Value::from(value),
},
repr => Value::from(repr),
};

let value = EnvProtocolCaller
Expand Down Expand Up @@ -1710,6 +1699,18 @@ impl Value {
}
}

pub(crate) fn try_borrow_mut<T>(&self) -> Result<Option<BorrowMut<'_, T>>, AccessError>
where
T: Any,
{
match &self.repr {
Repr::Inline(..) => Ok(None),
Repr::Mutable(..) => Ok(None),
Repr::Any(value) => value.try_borrow_mut(),
Repr::Empty => Err(AccessError::empty()),
}
}

pub(crate) fn protocol_into_iter(&self) -> VmResult<Value> {
EnvProtocolCaller.call_protocol_fn(Protocol::INTO_ITER, self.clone(), &mut ())
}
Expand Down Expand Up @@ -1808,6 +1809,13 @@ impl fmt::Debug for Value {
}
}

impl From<Repr> for Value {
#[inline]
fn from(repr: Repr) -> Self {
Self { repr }
}
}

impl From<()> for Value {
#[inline]
fn from((): ()) -> Self {
Expand Down Expand Up @@ -1896,7 +1904,6 @@ from! {
Struct => Struct,
Variant => Variant,
Object => Object,
Future => Future,
}

any_from! {
Expand All @@ -1910,6 +1917,7 @@ any_from! {
super::Generator,
super::Stream,
super::Function,
super::Future,
}

from_container! {
Expand Down Expand Up @@ -2035,8 +2043,6 @@ impl TypeValue {
pub(crate) enum Mutable {
/// An object.
Object(Object),
/// A stored future.
Future(Future),
/// An empty value indicating nothing.
Option(Option<Value>),
/// A stored result in a slot.
Expand All @@ -2055,7 +2061,6 @@ impl Mutable {
pub(crate) fn type_info(&self) -> TypeInfo {
match self {
Mutable::Object(..) => TypeInfo::static_type(static_type::OBJECT),
Mutable::Future(..) => TypeInfo::static_type(static_type::FUTURE),
Mutable::Option(..) => TypeInfo::static_type(static_type::OPTION),
Mutable::Result(..) => TypeInfo::static_type(static_type::RESULT),
Mutable::EmptyStruct(empty) => empty.type_info(),
Expand All @@ -2072,7 +2077,6 @@ impl Mutable {
pub(crate) fn type_hash(&self) -> Hash {
match self {
Mutable::Object(..) => static_type::OBJECT.hash,
Mutable::Future(..) => static_type::FUTURE.hash,
Mutable::Result(..) => static_type::RESULT.hash,
Mutable::Option(..) => static_type::OPTION.hash,
Mutable::EmptyStruct(empty) => empty.rtti.hash,
Expand Down
1 change: 0 additions & 1 deletion crates/rune/src/runtime/value/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ impl ser::Serialize for Value {
Mutable::Struct(..) => Err(ser::Error::custom("cannot serialize objects structs")),
Mutable::Variant(..) => Err(ser::Error::custom("cannot serialize variants")),
Mutable::Result(..) => Err(ser::Error::custom("cannot serialize results")),
Mutable::Future(..) => Err(ser::Error::custom("cannot serialize futures")),
},
BorrowRefRepr::Any(value) => match value.type_hash() {
String::HASH => {
Expand Down
2 changes: 1 addition & 1 deletion crates/rune/src/runtime/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2042,7 +2042,7 @@ impl Vm {
let futures = futures_util::stream::FuturesUnordered::new();

for (branch, value) in vm_try!(self.stack.slice_at(addr, len)).iter().enumerate() {
let future = vm_try!(value.clone().into_future_mut());
let future = vm_try!(value.clone().into_any_mut::<Future>());

if !future.is_completed() {
futures.push(SelectFuture::new(self.ip + branch, future));
Expand Down

0 comments on commit 87d9e1f

Please sign in to comment.