blob: b09f60231a958ca135b84c222b5096b73ab7db2d [file] [log] [blame]
Stjepan Glavina1479e862019-08-12 20:18:51 +02001use std::fmt;
2use std::future::Future;
3use std::marker::PhantomData;
4use std::mem;
5use std::ptr::NonNull;
6
7use crate::header::Header;
8use crate::raw::RawTask;
9use crate::JoinHandle;
10
11/// Creates a new task.
12///
Stjepan Glavina7a8962b2019-08-16 11:25:25 +020013/// This constructor returns a [`Task`] reference that runs the future and a [`JoinHandle`] that
Stjepan Glavina1479e862019-08-12 20:18:51 +020014/// awaits its result.
15///
Stjepan Glavina7a8962b2019-08-16 11:25:25 +020016/// When run, the task polls `future`. When woken, it gets scheduled for running by the `schedule`
17/// function. Argument `tag` is an arbitrary piece of data stored inside the task.
Stjepan Glavina1479e862019-08-12 20:18:51 +020018///
Stjepan Glavina7a8962b2019-08-16 11:25:25 +020019/// [`Task`]: struct.Task.html
20/// [`JoinHandle`]: struct.JoinHandle.html
Stjepan Glavina1479e862019-08-12 20:18:51 +020021///
22/// # Examples
23///
24/// ```
25/// # #![feature(async_await)]
Stjepan Glavina7a8962b2019-08-16 11:25:25 +020026/// #
Stjepan Glavina1479e862019-08-12 20:18:51 +020027/// use crossbeam::channel;
28///
29/// // The future inside the task.
30/// let future = async {
31/// println!("Hello, world!");
32/// };
33///
34/// // If the task gets woken, it will be sent into this channel.
35/// let (s, r) = channel::unbounded();
36/// let schedule = move |task| s.send(task).unwrap();
37///
38/// // Create a task with the future and the schedule function.
39/// let (task, handle) = async_task::spawn(future, schedule, ());
40/// ```
Stjepan Glavina1479e862019-08-12 20:18:51 +020041pub fn spawn<F, R, S, T>(future: F, schedule: S, tag: T) -> (Task<T>, JoinHandle<R, T>)
42where
43 F: Future<Output = R> + Send + 'static,
44 R: Send + 'static,
45 S: Fn(Task<T>) + Send + Sync + 'static,
46 T: Send + Sync + 'static,
47{
48 let raw_task = RawTask::<F, R, S, T>::allocate(tag, future, schedule);
49 let task = Task {
50 raw_task,
51 _marker: PhantomData,
52 };
53 let handle = JoinHandle {
54 raw_task,
55 _marker: PhantomData,
56 };
57 (task, handle)
58}
59
Stjepan Glavina7a8962b2019-08-16 11:25:25 +020060/// A task reference that runs its future.
Stjepan Glavina1479e862019-08-12 20:18:51 +020061///
Stjepan Glavina7a8962b2019-08-16 11:25:25 +020062/// The [`Task`] reference "owns" the task itself and is able to run it. Running consumes the
63/// [`Task`] reference and polls its internal future. If the future is still pending after getting
64/// polled, the [`Task`] reference simply won't exist until a [`Waker`] notifies the task. If the
65/// future completes, its result becomes available to the [`JoinHandle`].
Stjepan Glavina1479e862019-08-12 20:18:51 +020066///
Stjepan Glavina7a8962b2019-08-16 11:25:25 +020067/// When the task is woken, the [`Task`] reference is recreated and passed to the schedule
68/// function. In most executors, scheduling simply pushes the [`Task`] reference into a queue of
69/// runnable tasks.
Stjepan Glavina1479e862019-08-12 20:18:51 +020070///
Stjepan Glavina7a8962b2019-08-16 11:25:25 +020071/// If the [`Task`] reference is dropped without being run, the task is cancelled. When cancelled,
72/// the task won't be scheduled again even if a [`Waker`] wakes it. It is possible for the
73/// [`JoinHandle`] to cancel while the [`Task`] reference exists, in which case an attempt to run
74/// the task won't do anything.
Stjepan Glavina1479e862019-08-12 20:18:51 +020075///
76/// [`run()`]: struct.Task.html#method.run
Stjepan Glavina1479e862019-08-12 20:18:51 +020077/// [`JoinHandle`]: struct.JoinHandle.html
78/// [`Task`]: struct.Task.html
Stjepan Glavina1479e862019-08-12 20:18:51 +020079/// [`Waker`]: https://doc.rust-lang.org/std/task/struct.Waker.html
Stjepan Glavina1479e862019-08-12 20:18:51 +020080pub struct Task<T> {
81 /// A pointer to the heap-allocated task.
82 pub(crate) raw_task: NonNull<()>,
83
84 /// A marker capturing the generic type `T`.
85 pub(crate) _marker: PhantomData<T>,
86}
87
88unsafe impl<T> Send for Task<T> {}
89unsafe impl<T> Sync for Task<T> {}
90
91impl<T> Task<T> {
92 /// Schedules the task.
93 ///
94 /// This is a convenience method that simply reschedules the task by passing it to its schedule
95 /// function.
96 ///
97 /// If the task is cancelled, this method won't do anything.
Stjepan Glavina1479e862019-08-12 20:18:51 +020098 pub fn schedule(self) {
99 let ptr = self.raw_task.as_ptr();
100 let header = ptr as *const Header;
101 mem::forget(self);
102
103 unsafe {
104 ((*header).vtable.schedule)(ptr);
105 }
106 }
107
108 /// Runs the task.
109 ///
110 /// This method polls the task's future. If the future completes, its result will become
111 /// available to the [`JoinHandle`]. And if the future is still pending, the task will have to
112 /// be woken in order to be rescheduled and then run again.
113 ///
Stjepan Glavina7a8962b2019-08-16 11:25:25 +0200114 /// If the task was cancelled by a [`JoinHandle`] before it gets run, then this method won't do
115 /// anything.
Stjepan Glavina1479e862019-08-12 20:18:51 +0200116 ///
117 /// It is possible that polling the future panics, in which case the panic will be propagated
118 /// into the caller. It is advised that invocations of this method are wrapped inside
119 /// [`catch_unwind`].
120 ///
121 /// If a panic occurs, the task is automatically cancelled.
122 ///
Stjepan Glavina7a8962b2019-08-16 11:25:25 +0200123 /// [`JoinHandle`]: struct.JoinHandle.html
Stjepan Glavina1479e862019-08-12 20:18:51 +0200124 /// [`catch_unwind`]: https://doc.rust-lang.org/std/panic/fn.catch_unwind.html
Stjepan Glavina1479e862019-08-12 20:18:51 +0200125 pub fn run(self) {
126 let ptr = self.raw_task.as_ptr();
127 let header = ptr as *const Header;
128 mem::forget(self);
129
130 unsafe {
131 ((*header).vtable.run)(ptr);
132 }
133 }
134
135 /// Cancels the task.
136 ///
137 /// When cancelled, the task won't be scheduled again even if a [`Waker`] wakes it. An attempt
Stjepan Glavina7a8962b2019-08-16 11:25:25 +0200138 /// to run it won't do anything.
Stjepan Glavina1479e862019-08-12 20:18:51 +0200139 ///
140 /// [`Waker`]: https://doc.rust-lang.org/std/task/struct.Waker.html
Stjepan Glavina1479e862019-08-12 20:18:51 +0200141 pub fn cancel(&self) {
142 let ptr = self.raw_task.as_ptr();
143 let header = ptr as *const Header;
144
145 unsafe {
146 (*header).cancel();
147 }
148 }
149
150 /// Returns a reference to the tag stored inside the task.
Stjepan Glavina1479e862019-08-12 20:18:51 +0200151 pub fn tag(&self) -> &T {
152 let offset = Header::offset_tag::<T>();
153 let ptr = self.raw_task.as_ptr();
154
155 unsafe {
156 let raw = (ptr as *mut u8).add(offset) as *const T;
157 &*raw
158 }
159 }
160}
161
162impl<T> Drop for Task<T> {
163 fn drop(&mut self) {
164 let ptr = self.raw_task.as_ptr();
165 let header = ptr as *const Header;
166
167 unsafe {
168 // Cancel the task.
169 (*header).cancel();
170
171 // Drop the future.
172 ((*header).vtable.drop_future)(ptr);
173
174 // Drop the task reference.
175 ((*header).vtable.decrement)(ptr);
176 }
177 }
178}
179
180impl<T: fmt::Debug> fmt::Debug for Task<T> {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 let ptr = self.raw_task.as_ptr();
183 let header = ptr as *const Header;
184
185 f.debug_struct("Task")
186 .field("header", unsafe { &(*header) })
187 .field("tag", self.tag())
188 .finish()
189 }
190}