11use std:: fmt;
22use std:: future:: Future ;
33use std:: marker:: PhantomData ;
4- use std:: mem;
4+ use std:: mem:: { self , ManuallyDrop } ;
5+ use std:: pin:: Pin ;
56use std:: ptr:: NonNull ;
7+ use std:: task:: { Context , Poll } ;
8+ use std:: thread:: { self , ThreadId } ;
69
710use crate :: header:: Header ;
811use crate :: raw:: RawTask ;
@@ -16,8 +19,13 @@ use crate::JoinHandle;
1619/// When run, the task polls `future`. When woken up, it gets scheduled for running by the
1720/// `schedule` function. Argument `tag` is an arbitrary piece of data stored inside the task.
1821///
22+ /// If you need to spawn a future that does not implement [`Send`], consider using the
23+ /// [`spawn_local`] function instead.
24+ ///
1925/// [`Task`]: struct.Task.html
2026/// [`JoinHandle`]: struct.JoinHandle.html
27+ /// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
28+ /// [`spawn_local`]: fn.spawn_local.html
2129///
2230/// # Examples
2331///
4351 S : Fn ( Task < T > ) + Send + Sync + ' static ,
4452 T : Send + Sync + ' static ,
4553{
46- let raw_task = RawTask :: < F , R , S , T > :: allocate ( tag, future, schedule) ;
54+ let raw_task = RawTask :: < F , R , S , T > :: allocate ( future, schedule, tag) ;
55+ let task = Task {
56+ raw_task,
57+ _marker : PhantomData ,
58+ } ;
59+ let handle = JoinHandle {
60+ raw_task,
61+ _marker : PhantomData ,
62+ } ;
63+ ( task, handle)
64+ }
65+
66+ /// Creates a new local task.
67+ ///
68+ /// This constructor returns a [`Task`] reference that runs the future and a [`JoinHandle`] that
69+ /// awaits its result.
70+ ///
71+ /// When run, the task polls `future`. When woken up, it gets scheduled for running by the
72+ /// `schedule` function. Argument `tag` is an arbitrary piece of data stored inside the task.
73+ ///
74+ /// Unlike [`spawn`], this function does not require the future to implement [`Send`]. If the
75+ /// [`Task`] reference is run or dropped on a thread it was not created on, a panic will occur.
76+ ///
77+ /// [`Task`]: struct.Task.html
78+ /// [`JoinHandle`]: struct.JoinHandle.html
79+ /// [`spawn`]: fn.spawn.html
80+ /// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
81+ ///
82+ /// # Examples
83+ ///
84+ /// ```
85+ /// use crossbeam::channel;
86+ ///
87+ /// // The future inside the task.
88+ /// let future = async {
89+ /// println!("Hello, world!");
90+ /// };
91+ ///
92+ /// // If the task gets woken up, it will be sent into this channel.
93+ /// let (s, r) = channel::unbounded();
94+ /// let schedule = move |task| s.send(task).unwrap();
95+ ///
96+ /// // Create a task with the future and the schedule function.
97+ /// let (task, handle) = async_task::spawn_local(future, schedule, ());
98+ /// ```
99+ pub fn spawn_local < F , R , S , T > ( future : F , schedule : S , tag : T ) -> ( Task < T > , JoinHandle < R , T > )
100+ where
101+ F : Future < Output = R > + ' static ,
102+ R : ' static ,
103+ S : Fn ( Task < T > ) + Send + Sync + ' static ,
104+ T : Send + Sync + ' static ,
105+ {
106+ thread_local ! {
107+ static ID : ThreadId = thread:: current( ) . id( ) ;
108+ }
109+
110+ struct Checked < F > {
111+ id : ThreadId ,
112+ inner : ManuallyDrop < F > ,
113+ }
114+
115+ impl < F > Drop for Checked < F > {
116+ fn drop ( & mut self ) {
117+ if ID . with ( |id| * id) != self . id {
118+ panic ! ( "local task dropped by a thread that didn't spawn it" ) ;
119+ }
120+ unsafe {
121+ ManuallyDrop :: drop ( & mut self . inner ) ;
122+ }
123+ }
124+ }
125+
126+ impl < F : Future > Future for Checked < F > {
127+ type Output = F :: Output ;
128+
129+ fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
130+ if ID . with ( |id| * id) != self . id {
131+ panic ! ( "local task polled by a thread that didn't spawn it" ) ;
132+ }
133+ unsafe { self . map_unchecked_mut ( |c| & mut * c. inner ) . poll ( cx) }
134+ }
135+ }
136+
137+ let future = Checked {
138+ id : ID . with ( |id| * id) ,
139+ inner : ManuallyDrop :: new ( future) ,
140+ } ;
141+
142+ let raw_task = RawTask :: < _ , R , S , T > :: allocate ( future, schedule, tag) ;
47143 let task = Task {
48144 raw_task,
49145 _marker : PhantomData ,
0 commit comments