From 1f9aa1332fc9f0194bac1761ef04e54564e26fc8 Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Tue, 26 Jun 2018 16:40:42 +0200 Subject: [PATCH 1/4] Split libcore/task.rs into submodules --- src/libcore/task/context.rs | 92 ++++++++ src/libcore/task/executor.rs | 44 ++++ src/libcore/task/mod.rs | 33 +++ src/libcore/task/poll.rs | 83 ++++++++ src/libcore/task/spawn_error.rs | 51 +++++ src/libcore/task/task.rs | 98 +++++++++ src/libcore/{task.rs => task/wake.rs} | 294 -------------------------- 7 files changed, 401 insertions(+), 294 deletions(-) create mode 100644 src/libcore/task/context.rs create mode 100644 src/libcore/task/executor.rs create mode 100644 src/libcore/task/mod.rs create mode 100644 src/libcore/task/poll.rs create mode 100644 src/libcore/task/spawn_error.rs create mode 100644 src/libcore/task/task.rs rename src/libcore/{task.rs => task/wake.rs} (52%) diff --git a/src/libcore/task/context.rs b/src/libcore/task/context.rs new file mode 100644 index 0000000000000..c69d45248a597 --- /dev/null +++ b/src/libcore/task/context.rs @@ -0,0 +1,92 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +use fmt; +use super::{Executor, Waker, LocalWaker}; + +/// Information about the currently-running task. +/// +/// Contexts are always tied to the stack, since they are set up specifically +/// when performing a single `poll` step on a task. +pub struct Context<'a> { + local_waker: &'a LocalWaker, + executor: &'a mut Executor, +} + +impl<'a> fmt::Debug for Context<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Context") + .finish() + } +} + +impl<'a> Context<'a> { + /// Create a new task `Context` with the provided `local_waker`, `waker`, and `executor`. + #[inline] + pub fn new(local_waker: &'a LocalWaker, executor: &'a mut Executor) -> Context<'a> { + Context { + local_waker, + executor, + } + } + + /// Get the `LocalWaker` associated with the current task. + #[inline] + pub fn local_waker(&self) -> &'a LocalWaker { + self.local_waker + } + + /// Get the `Waker` associated with the current task. + #[inline] + pub fn waker(&self) -> &'a Waker { + unsafe { &*(self.local_waker as *const LocalWaker as *const Waker) } + } + + /// Get the default executor associated with this task. + /// + /// This method is useful primarily if you want to explicitly handle + /// spawn failures. + #[inline] + pub fn executor(&mut self) -> &mut Executor { + self.executor + } + + /// Produce a context like the current one, but using the given waker instead. + /// + /// This advanced method is primarily used when building "internal + /// schedulers" within a task, where you want to provide some customized + /// wakeup logic. + #[inline] + pub fn with_waker<'b>(&'b mut self, local_waker: &'b LocalWaker) -> Context<'b> { + Context { + local_waker, + executor: self.executor, + } + } + + /// Produce a context like the current one, but using the given executor + /// instead. + /// + /// This advanced method is primarily used when building "internal + /// schedulers" within a task. + #[inline] + pub fn with_executor<'b, E>(&'b mut self, executor: &'b mut E) -> Context<'b> + where E: Executor + { + Context { + local_waker: self.local_waker, + executor: executor, + } + } +} diff --git a/src/libcore/task/executor.rs b/src/libcore/task/executor.rs new file mode 100644 index 0000000000000..fcef6d5ffc84d --- /dev/null +++ b/src/libcore/task/executor.rs @@ -0,0 +1,44 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +use super::{TaskObj, SpawnObjError, SpawnErrorKind}; + +/// A task executor. +/// +/// A *task* is a `()`-producing async value that runs at the top level, and will +/// be `poll`ed until completion. It's also the unit at which wake-up +/// notifications occur. Executors, such as thread pools, allow tasks to be +/// spawned and are responsible for putting tasks onto ready queues when +/// they are woken up, and polling them when they are ready. +pub trait Executor { + /// Spawn the given task, polling it until completion. + /// + /// # Errors + /// + /// The executor may be unable to spawn tasks, either because it has + /// been shut down or is resource-constrained. + fn spawn_obj(&mut self, task: TaskObj) -> Result<(), SpawnObjError>; + + /// Determine whether the executor is able to spawn new tasks. + /// + /// # Returns + /// + /// An `Ok` return means the executor is *likely* (but not guaranteed) + /// to accept a subsequent spawn attempt. Likewise, an `Err` return + /// means that `spawn` is likely, but not guaranteed, to yield an error. + #[inline] + fn status(&self) -> Result<(), SpawnErrorKind> { + Ok(()) + } +} diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs new file mode 100644 index 0000000000000..66ab21d177d83 --- /dev/null +++ b/src/libcore/task/mod.rs @@ -0,0 +1,33 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +//! Types and Traits for working with asynchronous tasks. + +mod context; +pub use self::context::Context; + +mod executor; +pub use self::executor::Executor; + +mod poll; +pub use self::poll::Poll; + +mod spawn_error; +pub use self::spawn_error::{SpawnErrorKind, SpawnObjError}; + +mod task; +pub use self::task::{TaskObj, UnsafeTask}; + +mod wake; +pub use self::wake::{Waker, LocalWaker, UnsafeWake}; diff --git a/src/libcore/task/poll.rs b/src/libcore/task/poll.rs new file mode 100644 index 0000000000000..10c954f0e80ec --- /dev/null +++ b/src/libcore/task/poll.rs @@ -0,0 +1,83 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +/// Indicates whether a value is available or if the current task has been +/// scheduled to receive a wakeup instead. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum Poll { + /// Represents that a value is immediately ready. + Ready(T), + + /// Represents that a value is not ready yet. + /// + /// When a function returns `Pending`, the function *must* also + /// ensure that the current task is scheduled to be awoken when + /// progress can be made. + Pending, +} + +impl Poll { + /// Change the ready value of this `Poll` with the closure provided + pub fn map(self, f: F) -> Poll + where F: FnOnce(T) -> U + { + match self { + Poll::Ready(t) => Poll::Ready(f(t)), + Poll::Pending => Poll::Pending, + } + } + + /// Returns whether this is `Poll::Ready` + pub fn is_ready(&self) -> bool { + match *self { + Poll::Ready(_) => true, + Poll::Pending => false, + } + } + + /// Returns whether this is `Poll::Pending` + pub fn is_pending(&self) -> bool { + !self.is_ready() + } +} + +impl Poll> { + /// Change the success value of this `Poll` with the closure provided + pub fn map_ok(self, f: F) -> Poll> + where F: FnOnce(T) -> U + { + match self { + Poll::Ready(Ok(t)) => Poll::Ready(Ok(f(t))), + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Pending => Poll::Pending, + } + } + + /// Change the error value of this `Poll` with the closure provided + pub fn map_err(self, f: F) -> Poll> + where F: FnOnce(E) -> U + { + match self { + Poll::Ready(Ok(t)) => Poll::Ready(Ok(t)), + Poll::Ready(Err(e)) => Poll::Ready(Err(f(e))), + Poll::Pending => Poll::Pending, + } + } +} + +impl From for Poll { + fn from(t: T) -> Poll { + Poll::Ready(t) + } +} diff --git a/src/libcore/task/spawn_error.rs b/src/libcore/task/spawn_error.rs new file mode 100644 index 0000000000000..5dd9c5be68929 --- /dev/null +++ b/src/libcore/task/spawn_error.rs @@ -0,0 +1,51 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +use fmt; +use super::TaskObj; + +/// Provides the reason that an executor was unable to spawn. +pub struct SpawnErrorKind { + _hidden: (), +} + +impl fmt::Debug for SpawnErrorKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("SpawnErrorKind") + .field(&"shutdown") + .finish() + } +} + +impl SpawnErrorKind { + /// Spawning is failing because the executor has been shut down. + pub fn shutdown() -> SpawnErrorKind { + SpawnErrorKind { _hidden: () } + } + + /// Check whether this error is the `shutdown` error. + pub fn is_shutdown(&self) -> bool { + true + } +} + +/// The result of a failed spawn +#[derive(Debug)] +pub struct SpawnObjError { + /// The kind of error + pub kind: SpawnErrorKind, + + /// The task for which spawning was attempted + pub task: TaskObj, +} diff --git a/src/libcore/task/task.rs b/src/libcore/task/task.rs new file mode 100644 index 0000000000000..dc4ff314e5bd8 --- /dev/null +++ b/src/libcore/task/task.rs @@ -0,0 +1,98 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +use fmt; +use future::Future; +use mem::PinMut; +use super::{Context, Poll}; + +/// A custom trait object for polling tasks, roughly akin to +/// `Box + Send>`. +pub struct TaskObj { + ptr: *mut (), + poll_fn: unsafe fn(*mut (), &mut Context) -> Poll<()>, + drop_fn: unsafe fn(*mut ()), +} + +unsafe impl Send for TaskObj {} + +impl TaskObj { + /// Create a `TaskObj` from a custom trait object representation. + #[inline] + pub fn new(t: T) -> TaskObj { + TaskObj { + ptr: t.into_raw(), + poll_fn: T::poll, + drop_fn: T::drop, + } + } +} + +impl fmt::Debug for TaskObj { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("TaskObj") + .finish() + } +} + +impl Future for TaskObj { + type Output = (); + + #[inline] + fn poll(self: PinMut, cx: &mut Context) -> Poll<()> { + unsafe { + (self.poll_fn)(self.ptr, cx) + } + } +} + +impl Drop for TaskObj { + fn drop(&mut self) { + unsafe { + (self.drop_fn)(self.ptr) + } + } +} + +/// A custom implementation of a task trait object for `TaskObj`, providing +/// a hand-rolled vtable. +/// +/// This custom representation is typically used only in `no_std` contexts, +/// where the default `Box`-based implementation is not available. +/// +/// The implementor must guarantee that it is safe to call `poll` repeatedly (in +/// a non-concurrent fashion) with the result of `into_raw` until `drop` is +/// called. +pub unsafe trait UnsafeTask: Send + 'static { + /// Convert a owned instance into a (conceptually owned) void pointer. + fn into_raw(self) -> *mut (); + + /// Poll the task represented by the given void pointer. + /// + /// # Safety + /// + /// The trait implementor must guarantee that it is safe to repeatedly call + /// `poll` with the result of `into_raw` until `drop` is called; such calls + /// are not, however, allowed to race with each other or with calls to `drop`. + unsafe fn poll(task: *mut (), cx: &mut Context) -> Poll<()>; + + /// Drops the task represented by the given void pointer. + /// + /// # Safety + /// + /// The trait implementor must guarantee that it is safe to call this + /// function once per `into_raw` invocation; that call cannot race with + /// other calls to `drop` or `poll`. + unsafe fn drop(task: *mut ()); +} diff --git a/src/libcore/task.rs b/src/libcore/task/wake.rs similarity index 52% rename from src/libcore/task.rs rename to src/libcore/task/wake.rs index 1a6018ffb65ad..4fd45be56fbfb 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task/wake.rs @@ -12,82 +12,8 @@ reason = "futures in libcore are unstable", issue = "50547")] -//! Types and Traits for working with asynchronous tasks. - use fmt; use ptr::NonNull; -use future::Future; -use mem::PinMut; - -/// Indicates whether a value is available or if the current task has been -/// scheduled to receive a wakeup instead. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub enum Poll { - /// Represents that a value is immediately ready. - Ready(T), - - /// Represents that a value is not ready yet. - /// - /// When a function returns `Pending`, the function *must* also - /// ensure that the current task is scheduled to be awoken when - /// progress can be made. - Pending, -} - -impl Poll { - /// Change the ready value of this `Poll` with the closure provided - pub fn map(self, f: F) -> Poll - where F: FnOnce(T) -> U - { - match self { - Poll::Ready(t) => Poll::Ready(f(t)), - Poll::Pending => Poll::Pending, - } - } - - /// Returns whether this is `Poll::Ready` - pub fn is_ready(&self) -> bool { - match *self { - Poll::Ready(_) => true, - Poll::Pending => false, - } - } - - /// Returns whether this is `Poll::Pending` - pub fn is_pending(&self) -> bool { - !self.is_ready() - } -} - -impl Poll> { - /// Change the success value of this `Poll` with the closure provided - pub fn map_ok(self, f: F) -> Poll> - where F: FnOnce(T) -> U - { - match self { - Poll::Ready(Ok(t)) => Poll::Ready(Ok(f(t))), - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), - Poll::Pending => Poll::Pending, - } - } - - /// Change the error value of this `Poll` with the closure provided - pub fn map_err(self, f: F) -> Poll> - where F: FnOnce(E) -> U - { - match self { - Poll::Ready(Ok(t)) => Poll::Ready(Ok(t)), - Poll::Ready(Err(e)) => Poll::Ready(Err(f(e))), - Poll::Pending => Poll::Pending, - } - } -} - -impl From for Poll { - fn from(t: T) -> Poll { - Poll::Ready(t) - } -} /// A `Waker` is a handle for waking up a task by notifying its executor that it /// is ready to be run. @@ -347,223 +273,3 @@ pub unsafe trait UnsafeWake: Send + Sync { self.wake() } } - -/// Information about the currently-running task. -/// -/// Contexts are always tied to the stack, since they are set up specifically -/// when performing a single `poll` step on a task. -pub struct Context<'a> { - local_waker: &'a LocalWaker, - executor: &'a mut Executor, -} - -impl<'a> fmt::Debug for Context<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Context") - .finish() - } -} - -impl<'a> Context<'a> { - /// Create a new task `Context` with the provided `local_waker`, `waker`, and `executor`. - #[inline] - pub fn new(local_waker: &'a LocalWaker, executor: &'a mut Executor) -> Context<'a> { - Context { - local_waker, - executor, - } - } - - /// Get the `LocalWaker` associated with the current task. - #[inline] - pub fn local_waker(&self) -> &'a LocalWaker { - self.local_waker - } - - /// Get the `Waker` associated with the current task. - #[inline] - pub fn waker(&self) -> &'a Waker { - unsafe { &*(self.local_waker as *const LocalWaker as *const Waker) } - } - - /// Get the default executor associated with this task. - /// - /// This method is useful primarily if you want to explicitly handle - /// spawn failures. - #[inline] - pub fn executor(&mut self) -> &mut Executor { - self.executor - } - - /// Produce a context like the current one, but using the given waker instead. - /// - /// This advanced method is primarily used when building "internal - /// schedulers" within a task, where you want to provide some customized - /// wakeup logic. - #[inline] - pub fn with_waker<'b>(&'b mut self, local_waker: &'b LocalWaker) -> Context<'b> { - Context { - local_waker, - executor: self.executor, - } - } - - /// Produce a context like the current one, but using the given executor - /// instead. - /// - /// This advanced method is primarily used when building "internal - /// schedulers" within a task. - #[inline] - pub fn with_executor<'b, E>(&'b mut self, executor: &'b mut E) -> Context<'b> - where E: Executor - { - Context { - local_waker: self.local_waker, - executor: executor, - } - } -} - -/// A task executor. -/// -/// A *task* is a `()`-producing async value that runs at the top level, and will -/// be `poll`ed until completion. It's also the unit at which wake-up -/// notifications occur. Executors, such as thread pools, allow tasks to be -/// spawned and are responsible for putting tasks onto ready queues when -/// they are woken up, and polling them when they are ready. -pub trait Executor { - /// Spawn the given task, polling it until completion. - /// - /// # Errors - /// - /// The executor may be unable to spawn tasks, either because it has - /// been shut down or is resource-constrained. - fn spawn_obj(&mut self, task: TaskObj) -> Result<(), SpawnObjError>; - - /// Determine whether the executor is able to spawn new tasks. - /// - /// # Returns - /// - /// An `Ok` return means the executor is *likely* (but not guaranteed) - /// to accept a subsequent spawn attempt. Likewise, an `Err` return - /// means that `spawn` is likely, but not guaranteed, to yield an error. - #[inline] - fn status(&self) -> Result<(), SpawnErrorKind> { - Ok(()) - } -} - -/// A custom trait object for polling tasks, roughly akin to -/// `Box + Send>`. -pub struct TaskObj { - ptr: *mut (), - poll_fn: unsafe fn(*mut (), &mut Context) -> Poll<()>, - drop_fn: unsafe fn(*mut ()), -} - -impl fmt::Debug for TaskObj { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("TaskObj") - .finish() - } -} - -unsafe impl Send for TaskObj {} - -/// A custom implementation of a task trait object for `TaskObj`, providing -/// a hand-rolled vtable. -/// -/// This custom representation is typically used only in `no_std` contexts, -/// where the default `Box`-based implementation is not available. -/// -/// The implementor must guarantee that it is safe to call `poll` repeatedly (in -/// a non-concurrent fashion) with the result of `into_raw` until `drop` is -/// called. -pub unsafe trait UnsafeTask: Send + 'static { - /// Convert a owned instance into a (conceptually owned) void pointer. - fn into_raw(self) -> *mut (); - - /// Poll the task represented by the given void pointer. - /// - /// # Safety - /// - /// The trait implementor must guarantee that it is safe to repeatedly call - /// `poll` with the result of `into_raw` until `drop` is called; such calls - /// are not, however, allowed to race with each other or with calls to `drop`. - unsafe fn poll(task: *mut (), cx: &mut Context) -> Poll<()>; - - /// Drops the task represented by the given void pointer. - /// - /// # Safety - /// - /// The trait implementor must guarantee that it is safe to call this - /// function once per `into_raw` invocation; that call cannot race with - /// other calls to `drop` or `poll`. - unsafe fn drop(task: *mut ()); -} - -impl TaskObj { - /// Create a `TaskObj` from a custom trait object representation. - #[inline] - pub fn new(t: T) -> TaskObj { - TaskObj { - ptr: t.into_raw(), - poll_fn: T::poll, - drop_fn: T::drop, - } - } -} - -impl Future for TaskObj { - type Output = (); - - #[inline] - fn poll(self: PinMut, cx: &mut Context) -> Poll<()> { - unsafe { - (self.poll_fn)(self.ptr, cx) - } - } -} - -impl Drop for TaskObj { - fn drop(&mut self) { - unsafe { - (self.drop_fn)(self.ptr) - } - } -} - -/// Provides the reason that an executor was unable to spawn. -pub struct SpawnErrorKind { - _hidden: (), -} - -impl fmt::Debug for SpawnErrorKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("SpawnErrorKind") - .field(&"shutdown") - .finish() - } -} - -impl SpawnErrorKind { - /// Spawning is failing because the executor has been shut down. - pub fn shutdown() -> SpawnErrorKind { - SpawnErrorKind { _hidden: () } - } - - /// Check whether this error is the `shutdown` error. - pub fn is_shutdown(&self) -> bool { - true - } -} - -/// The result of a failed spawn -#[derive(Debug)] -pub struct SpawnObjError { - /// The kind of error - pub kind: SpawnErrorKind, - - /// The task for which spawning was attempted - pub task: TaskObj, -} From 433e6b31a75eea5ce45493acc63eae462d740338 Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Tue, 26 Jun 2018 17:06:20 +0200 Subject: [PATCH 2/4] Add `LocalTaskObj` --- src/liballoc/boxed.rs | 18 ++++++++- src/libcore/task/mod.rs | 4 +- src/libcore/task/spawn_error.rs | 33 ++++++++++++++- src/libcore/task/task.rs | 71 +++++++++++++++++++++++++++++++-- 4 files changed, 118 insertions(+), 8 deletions(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index ea60c7775af59..6a05ef680889a 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -66,7 +66,7 @@ use core::marker::{Unpin, Unsize}; use core::mem::{self, PinMut}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ptr::{self, NonNull, Unique}; -use core::task::{Context, Poll, UnsafeTask, TaskObj}; +use core::task::{Context, Poll, UnsafeTask, TaskObj, LocalTaskObj}; use core::convert::From; use raw_vec::RawVec; @@ -933,7 +933,7 @@ impl<'a, F: ?Sized + Future> Future for PinBox { } #[unstable(feature = "futures_api", issue = "50547")] -unsafe impl + Send + 'static> UnsafeTask for PinBox { +unsafe impl + 'static> UnsafeTask for PinBox { fn into_raw(self) -> *mut () { PinBox::into_raw(self) as *mut () } @@ -962,3 +962,17 @@ impl + Send + 'static> From> for TaskObj { TaskObj::new(PinBox::from(boxed)) } } + +#[unstable(feature = "futures_api", issue = "50547")] +impl + 'static> From> for LocalTaskObj { + fn from(boxed: PinBox) -> Self { + LocalTaskObj::new(boxed) + } +} + +#[unstable(feature = "futures_api", issue = "50547")] +impl + 'static> From> for LocalTaskObj { + fn from(boxed: Box) -> Self { + LocalTaskObj::new(PinBox::from(boxed)) + } +} diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index 66ab21d177d83..36370b6b37c3d 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -24,10 +24,10 @@ mod poll; pub use self::poll::Poll; mod spawn_error; -pub use self::spawn_error::{SpawnErrorKind, SpawnObjError}; +pub use self::spawn_error::{SpawnErrorKind, SpawnObjError, SpawnLocalObjError}; mod task; -pub use self::task::{TaskObj, UnsafeTask}; +pub use self::task::{TaskObj, LocalTaskObj, UnsafeTask}; mod wake; pub use self::wake::{Waker, LocalWaker, UnsafeWake}; diff --git a/src/libcore/task/spawn_error.rs b/src/libcore/task/spawn_error.rs index 5dd9c5be68929..57bb9ebeb30d9 100644 --- a/src/libcore/task/spawn_error.rs +++ b/src/libcore/task/spawn_error.rs @@ -13,7 +13,8 @@ issue = "50547")] use fmt; -use super::TaskObj; +use mem; +use super::{TaskObj, LocalTaskObj}; /// Provides the reason that an executor was unable to spawn. pub struct SpawnErrorKind { @@ -49,3 +50,33 @@ pub struct SpawnObjError { /// The task for which spawning was attempted pub task: TaskObj, } + +/// The result of a failed spawn +#[derive(Debug)] +pub struct SpawnLocalObjError { + /// The kind of error + pub kind: SpawnErrorKind, + + /// The task for which spawning was attempted + pub task: LocalTaskObj, +} + +impl SpawnLocalObjError { + /// Converts the `SpawnLocalObjError` into a `SpawnObjError` + /// To make this operation safe one has to ensure that the `UnsafeTask` + /// instance from which the `LocalTaskObj` stored inside was created + /// actually implements `Send`. + pub unsafe fn as_spawn_obj_error(self) -> SpawnObjError { + // Safety: Both structs have the same memory layout + mem::transmute::(self) + } +} + +impl From for SpawnLocalObjError { + fn from(error: SpawnObjError) -> SpawnLocalObjError { + unsafe { + // Safety: Both structs have the same memory layout + mem::transmute::(error) + } + } +} diff --git a/src/libcore/task/task.rs b/src/libcore/task/task.rs index dc4ff314e5bd8..9896d7f5ff221 100644 --- a/src/libcore/task/task.rs +++ b/src/libcore/task/task.rs @@ -14,7 +14,7 @@ use fmt; use future::Future; -use mem::PinMut; +use mem::{self, PinMut}; use super::{Context, Poll}; /// A custom trait object for polling tasks, roughly akin to @@ -30,7 +30,7 @@ unsafe impl Send for TaskObj {} impl TaskObj { /// Create a `TaskObj` from a custom trait object representation. #[inline] - pub fn new(t: T) -> TaskObj { + pub fn new(t: T) -> TaskObj { TaskObj { ptr: t.into_raw(), poll_fn: T::poll, @@ -65,6 +65,71 @@ impl Drop for TaskObj { } } +/// A custom trait object for polling tasks, roughly akin to +/// `Box>`. +/// Contrary to `TaskObj`, `LocalTaskObj` does not have a `Send` bound. +pub struct LocalTaskObj { + ptr: *mut (), + poll_fn: unsafe fn(*mut (), &mut Context) -> Poll<()>, + drop_fn: unsafe fn(*mut ()), +} + +impl LocalTaskObj { + /// Create a `LocalTaskObj` from a custom trait object representation. + #[inline] + pub fn new(t: T) -> LocalTaskObj { + LocalTaskObj { + ptr: t.into_raw(), + poll_fn: T::poll, + drop_fn: T::drop, + } + } + + /// Converts the `LocalTaskObj` into a `TaskObj` + /// To make this operation safe one has to ensure that the `UnsafeTask` + /// instance from which this `LocalTaskObj` was created actually implements + /// `Send`. + pub unsafe fn as_task_obj(self) -> TaskObj { + // Safety: Both structs have the same memory layout + mem::transmute::(self) + } +} + +impl fmt::Debug for LocalTaskObj { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("LocalTaskObj") + .finish() + } +} + +impl From for LocalTaskObj { + fn from(task: TaskObj) -> LocalTaskObj { + unsafe { + // Safety: Both structs have the same memory layout + mem::transmute::(task) + } + } +} + +impl Future for LocalTaskObj { + type Output = (); + + #[inline] + fn poll(self: PinMut, cx: &mut Context) -> Poll<()> { + unsafe { + (self.poll_fn)(self.ptr, cx) + } + } +} + +impl Drop for LocalTaskObj { + fn drop(&mut self) { + unsafe { + (self.drop_fn)(self.ptr) + } + } +} + /// A custom implementation of a task trait object for `TaskObj`, providing /// a hand-rolled vtable. /// @@ -74,7 +139,7 @@ impl Drop for TaskObj { /// The implementor must guarantee that it is safe to call `poll` repeatedly (in /// a non-concurrent fashion) with the result of `into_raw` until `drop` is /// called. -pub unsafe trait UnsafeTask: Send + 'static { +pub unsafe trait UnsafeTask: 'static { /// Convert a owned instance into a (conceptually owned) void pointer. fn into_raw(self) -> *mut (); From c055fef0104f017b9f6aaa25ba0fc69ccb34ae8e Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Tue, 26 Jun 2018 21:06:20 +0200 Subject: [PATCH 3/4] Nested `LocalTaskObj` in `TaskObj`, remove `SpawnErrorObj` conversions --- src/libcore/task/spawn_error.rs | 20 -------- src/libcore/task/task.rs | 89 +++++++++++++-------------------- 2 files changed, 34 insertions(+), 75 deletions(-) diff --git a/src/libcore/task/spawn_error.rs b/src/libcore/task/spawn_error.rs index 57bb9ebeb30d9..42d37efbe1977 100644 --- a/src/libcore/task/spawn_error.rs +++ b/src/libcore/task/spawn_error.rs @@ -60,23 +60,3 @@ pub struct SpawnLocalObjError { /// The task for which spawning was attempted pub task: LocalTaskObj, } - -impl SpawnLocalObjError { - /// Converts the `SpawnLocalObjError` into a `SpawnObjError` - /// To make this operation safe one has to ensure that the `UnsafeTask` - /// instance from which the `LocalTaskObj` stored inside was created - /// actually implements `Send`. - pub unsafe fn as_spawn_obj_error(self) -> SpawnObjError { - // Safety: Both structs have the same memory layout - mem::transmute::(self) - } -} - -impl From for SpawnLocalObjError { - fn from(error: SpawnObjError) -> SpawnLocalObjError { - unsafe { - // Safety: Both structs have the same memory layout - mem::transmute::(error) - } - } -} diff --git a/src/libcore/task/task.rs b/src/libcore/task/task.rs index 9896d7f5ff221..c5a41873db427 100644 --- a/src/libcore/task/task.rs +++ b/src/libcore/task/task.rs @@ -14,57 +14,9 @@ use fmt; use future::Future; -use mem::{self, PinMut}; +use mem::PinMut; use super::{Context, Poll}; -/// A custom trait object for polling tasks, roughly akin to -/// `Box + Send>`. -pub struct TaskObj { - ptr: *mut (), - poll_fn: unsafe fn(*mut (), &mut Context) -> Poll<()>, - drop_fn: unsafe fn(*mut ()), -} - -unsafe impl Send for TaskObj {} - -impl TaskObj { - /// Create a `TaskObj` from a custom trait object representation. - #[inline] - pub fn new(t: T) -> TaskObj { - TaskObj { - ptr: t.into_raw(), - poll_fn: T::poll, - drop_fn: T::drop, - } - } -} - -impl fmt::Debug for TaskObj { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("TaskObj") - .finish() - } -} - -impl Future for TaskObj { - type Output = (); - - #[inline] - fn poll(self: PinMut, cx: &mut Context) -> Poll<()> { - unsafe { - (self.poll_fn)(self.ptr, cx) - } - } -} - -impl Drop for TaskObj { - fn drop(&mut self) { - unsafe { - (self.drop_fn)(self.ptr) - } - } -} - /// A custom trait object for polling tasks, roughly akin to /// `Box>`. /// Contrary to `TaskObj`, `LocalTaskObj` does not have a `Send` bound. @@ -90,8 +42,7 @@ impl LocalTaskObj { /// instance from which this `LocalTaskObj` was created actually implements /// `Send`. pub unsafe fn as_task_obj(self) -> TaskObj { - // Safety: Both structs have the same memory layout - mem::transmute::(self) + TaskObj(self) } } @@ -104,10 +55,7 @@ impl fmt::Debug for LocalTaskObj { impl From for LocalTaskObj { fn from(task: TaskObj) -> LocalTaskObj { - unsafe { - // Safety: Both structs have the same memory layout - mem::transmute::(task) - } + task.0 } } @@ -130,6 +78,37 @@ impl Drop for LocalTaskObj { } } +/// A custom trait object for polling tasks, roughly akin to +/// `Box + Send>`. +pub struct TaskObj(LocalTaskObj); + +unsafe impl Send for TaskObj {} + +impl TaskObj { + /// Create a `TaskObj` from a custom trait object representation. + #[inline] + pub fn new(t: T) -> TaskObj { + TaskObj(LocalTaskObj::new(t)) + } +} + +impl fmt::Debug for TaskObj { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("TaskObj") + .finish() + } +} + +impl Future for TaskObj { + type Output = (); + + #[inline] + fn poll(self: PinMut, cx: &mut Context) -> Poll<()> { + let pinned_field = unsafe { PinMut::map_unchecked(self, |x| &mut x.0) }; + pinned_field.poll(cx) + } +} + /// A custom implementation of a task trait object for `TaskObj`, providing /// a hand-rolled vtable. /// From b39ea1d18f19f9cee3ad271a802b3157f9827f51 Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Tue, 26 Jun 2018 21:13:36 +0200 Subject: [PATCH 4/4] Move spawn errors into executor.rs --- src/libcore/task/executor.rs | 48 ++++++++++++++++++++++++- src/libcore/task/mod.rs | 7 ++-- src/libcore/task/spawn_error.rs | 62 --------------------------------- 3 files changed, 50 insertions(+), 67 deletions(-) delete mode 100644 src/libcore/task/spawn_error.rs diff --git a/src/libcore/task/executor.rs b/src/libcore/task/executor.rs index fcef6d5ffc84d..73bf80d2f9997 100644 --- a/src/libcore/task/executor.rs +++ b/src/libcore/task/executor.rs @@ -12,7 +12,8 @@ reason = "futures in libcore are unstable", issue = "50547")] -use super::{TaskObj, SpawnObjError, SpawnErrorKind}; +use fmt; +use super::{TaskObj, LocalTaskObj}; /// A task executor. /// @@ -42,3 +43,48 @@ pub trait Executor { Ok(()) } } + +/// Provides the reason that an executor was unable to spawn. +pub struct SpawnErrorKind { + _hidden: (), +} + +impl fmt::Debug for SpawnErrorKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("SpawnErrorKind") + .field(&"shutdown") + .finish() + } +} + +impl SpawnErrorKind { + /// Spawning is failing because the executor has been shut down. + pub fn shutdown() -> SpawnErrorKind { + SpawnErrorKind { _hidden: () } + } + + /// Check whether this error is the `shutdown` error. + pub fn is_shutdown(&self) -> bool { + true + } +} + +/// The result of a failed spawn +#[derive(Debug)] +pub struct SpawnObjError { + /// The kind of error + pub kind: SpawnErrorKind, + + /// The task for which spawning was attempted + pub task: TaskObj, +} + +/// The result of a failed spawn +#[derive(Debug)] +pub struct SpawnLocalObjError { + /// The kind of error + pub kind: SpawnErrorKind, + + /// The task for which spawning was attempted + pub task: LocalTaskObj, +} diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index 36370b6b37c3d..d167a37410553 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -18,14 +18,13 @@ mod context; pub use self::context::Context; mod executor; -pub use self::executor::Executor; +pub use self::executor::{ + Executor, SpawnErrorKind, SpawnObjError, SpawnLocalObjError +}; mod poll; pub use self::poll::Poll; -mod spawn_error; -pub use self::spawn_error::{SpawnErrorKind, SpawnObjError, SpawnLocalObjError}; - mod task; pub use self::task::{TaskObj, LocalTaskObj, UnsafeTask}; diff --git a/src/libcore/task/spawn_error.rs b/src/libcore/task/spawn_error.rs deleted file mode 100644 index 42d37efbe1977..0000000000000 --- a/src/libcore/task/spawn_error.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![unstable(feature = "futures_api", - reason = "futures in libcore are unstable", - issue = "50547")] - -use fmt; -use mem; -use super::{TaskObj, LocalTaskObj}; - -/// Provides the reason that an executor was unable to spawn. -pub struct SpawnErrorKind { - _hidden: (), -} - -impl fmt::Debug for SpawnErrorKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("SpawnErrorKind") - .field(&"shutdown") - .finish() - } -} - -impl SpawnErrorKind { - /// Spawning is failing because the executor has been shut down. - pub fn shutdown() -> SpawnErrorKind { - SpawnErrorKind { _hidden: () } - } - - /// Check whether this error is the `shutdown` error. - pub fn is_shutdown(&self) -> bool { - true - } -} - -/// The result of a failed spawn -#[derive(Debug)] -pub struct SpawnObjError { - /// The kind of error - pub kind: SpawnErrorKind, - - /// The task for which spawning was attempted - pub task: TaskObj, -} - -/// The result of a failed spawn -#[derive(Debug)] -pub struct SpawnLocalObjError { - /// The kind of error - pub kind: SpawnErrorKind, - - /// The task for which spawning was attempted - pub task: LocalTaskObj, -}