1
1
use std:: fmt;
2
2
use std:: future:: Future ;
3
3
use std:: marker:: PhantomData ;
4
- use std:: mem;
4
+ use std:: mem:: { self , ManuallyDrop } ;
5
+ use std:: pin:: Pin ;
5
6
use std:: ptr:: NonNull ;
7
+ use std:: task:: { Context , Poll } ;
8
+ use std:: thread:: { self , ThreadId } ;
6
9
7
10
use crate :: header:: Header ;
8
11
use crate :: raw:: RawTask ;
@@ -16,8 +19,13 @@ use crate::JoinHandle;
16
19
/// When run, the task polls `future`. When woken up, it gets scheduled for running by the
17
20
/// `schedule` function. Argument `tag` is an arbitrary piece of data stored inside the task.
18
21
///
22
+ /// If you need to spawn a future that does not implement [`Send`], consider using the
23
+ /// [`spawn_local`] function instead.
24
+ ///
19
25
/// [`Task`]: struct.Task.html
20
26
/// [`JoinHandle`]: struct.JoinHandle.html
27
+ /// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
28
+ /// [`spawn_local`]: fn.spawn_local.html
21
29
///
22
30
/// # Examples
23
31
///
43
51
S : Fn ( Task < T > ) + Send + Sync + ' static ,
44
52
T : Send + Sync + ' static ,
45
53
{
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) ;
47
143
let task = Task {
48
144
raw_task,
49
145
_marker : PhantomData ,
0 commit comments