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,16 @@ 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+ /// The schedule function should not attempt to run the task nor to drop it. Instead, it should
23+ /// push the task into some kind of queue so that it can be processed later.
24+ ///
25+ /// If you need to spawn a future that does not implement [`Send`], consider using the
26+ /// [`spawn_local`] function instead.
27+ ///
1928/// [`Task`]: struct.Task.html
2029/// [`JoinHandle`]: struct.JoinHandle.html
30+ /// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
31+ /// [`spawn_local`]: fn.spawn_local.html
2132///
2233/// # Examples
2334///
4354 S : Fn ( Task < T > ) + Send + Sync + ' static ,
4455 T : Send + Sync + ' static ,
4556{
46- let raw_task = RawTask :: < F , R , S , T > :: allocate ( tag, future, schedule) ;
57+ let raw_task = RawTask :: < F , R , S , T > :: allocate ( future, schedule, tag) ;
58+ let task = Task {
59+ raw_task,
60+ _marker : PhantomData ,
61+ } ;
62+ let handle = JoinHandle {
63+ raw_task,
64+ _marker : PhantomData ,
65+ } ;
66+ ( task, handle)
67+ }
68+
69+ /// Creates a new local task.
70+ ///
71+ /// This constructor returns a [`Task`] reference that runs the future and a [`JoinHandle`] that
72+ /// awaits its result.
73+ ///
74+ /// When run, the task polls `future`. When woken up, it gets scheduled for running by the
75+ /// `schedule` function. Argument `tag` is an arbitrary piece of data stored inside the task.
76+ ///
77+ /// The schedule function should not attempt to run the task nor to drop it. Instead, it should
78+ /// push the task into some kind of queue so that it can be processed later.
79+ ///
80+ /// Unlike [`spawn`], this function does not require the future to implement [`Send`]. If the
81+ /// [`Task`] reference is run or dropped on a thread it was not created on, a panic will occur.
82+ ///
83+ /// [`Task`]: struct.Task.html
84+ /// [`JoinHandle`]: struct.JoinHandle.html
85+ /// [`spawn`]: fn.spawn.html
86+ /// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
87+ ///
88+ /// # Examples
89+ ///
90+ /// ```
91+ /// use crossbeam::channel;
92+ ///
93+ /// // The future inside the task.
94+ /// let future = async {
95+ /// println!("Hello, world!");
96+ /// };
97+ ///
98+ /// // If the task gets woken up, it will be sent into this channel.
99+ /// let (s, r) = channel::unbounded();
100+ /// let schedule = move |task| s.send(task).unwrap();
101+ ///
102+ /// // Create a task with the future and the schedule function.
103+ /// let (task, handle) = async_task::spawn_local(future, schedule, ());
104+ /// ```
105+ pub fn spawn_local < F , R , S , T > ( future : F , schedule : S , tag : T ) -> ( Task < T > , JoinHandle < R , T > )
106+ where
107+ F : Future < Output = R > + ' static ,
108+ R : ' static ,
109+ S : Fn ( Task < T > ) + Send + Sync + ' static ,
110+ T : Send + Sync + ' static ,
111+ {
112+ thread_local ! {
113+ static ID : ThreadId = thread:: current( ) . id( ) ;
114+ }
115+
116+ struct Checked < F > {
117+ id : ThreadId ,
118+ inner : ManuallyDrop < F > ,
119+ }
120+
121+ impl < F > Drop for Checked < F > {
122+ fn drop ( & mut self ) {
123+ if ID . with ( |id| * id) != self . id {
124+ panic ! ( "local task dropped by a thread that didn't spawn it" ) ;
125+ }
126+ unsafe {
127+ ManuallyDrop :: drop ( & mut self . inner ) ;
128+ }
129+ }
130+ }
131+
132+ impl < F : Future > Future for Checked < F > {
133+ type Output = F :: Output ;
134+
135+ fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
136+ if ID . with ( |id| * id) != self . id {
137+ panic ! ( "local task polled by a thread that didn't spawn it" ) ;
138+ }
139+ unsafe { self . map_unchecked_mut ( |c| & mut * c. inner ) . poll ( cx) }
140+ }
141+ }
142+
143+ let future = Checked {
144+ id : ID . with ( |id| * id) ,
145+ inner : ManuallyDrop :: new ( future) ,
146+ } ;
147+
148+ let raw_task = RawTask :: < _ , R , S , T > :: allocate ( future, schedule, tag) ;
47149 let task = Task {
48150 raw_task,
49151 _marker : PhantomData ,
0 commit comments