gio/task.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{boxed::Box as Box_, future::Future, mem::transmute, panic, ptr};
4
5use glib::{
6 prelude::*,
7 signal::{connect_raw, SignalHandlerId},
8 translate::*,
9};
10
11use futures_channel::oneshot;
12
13use crate::{ffi, AsyncResult, Cancellable};
14
15glib::wrapper! {
16 // rustdoc-stripper-ignore-next
17 /// `LocalTask` provides idiomatic access to gio's `GTask` API, for
18 /// instance by being generic over their value type, while not completely departing
19 /// from the underlying C API. `LocalTask` does not require its value to be `Send`
20 /// and `Sync` and thus is useful to to implement gio style asynchronous
21 /// tasks that run in the glib main loop. If you need to run tasks in threads
22 /// see the `Task` type.
23 ///
24 /// The constructors of `LocalTask` and `Task` is marked as unsafe because this API does
25 /// not allow to automatically enforce all the invariants required to be a completely
26 /// safe abstraction. See the `Task` type for more details.
27 #[doc(alias = "GTask")]
28 pub struct LocalTask<V: ValueType>(Object<ffi::GTask, ffi::GTaskClass>) @implements AsyncResult;
29
30 match fn {
31 type_ => || ffi::g_task_get_type(),
32 }
33}
34
35glib::wrapper! {
36 // rustdoc-stripper-ignore-next
37 /// `Task` provides idiomatic access to gio's `GTask` API, for
38 /// instance by being generic over their value type, while not completely departing
39 /// from the underlying C API. `Task` is `Send` and `Sync` and requires its value to
40 /// also be `Send` and `Sync`, thus is useful to to implement gio style asynchronous
41 /// tasks that run in threads. If you need to only run tasks in glib main loop
42 /// see the `LocalTask` type.
43 ///
44 /// The constructors of `LocalTask` and `Task` is marked as unsafe because this API does
45 /// not allow to automatically enforce all the invariants required to be a completely
46 /// safe abstraction. The caller is responsible to ensure the following requirements
47 /// are satisfied
48 ///
49 /// * You should not create a `LocalTask`, upcast it to a `glib::Object` and then
50 /// downcast it to a `Task`, as this will bypass the thread safety requirements
51 /// * You should ensure that the `return_result`, `return_error_if_cancelled` and
52 /// `propagate()` methods are only called once.
53 // rustdoc-stripper-ignore-next-stop
54 /// A `GTask` represents and manages a cancellable ‘task’.
55 ///
56 /// ## Asynchronous operations
57 ///
58 /// The most common usage of `GTask` is as a [`AsyncResult`][crate::AsyncResult], to
59 /// manage data during an asynchronous operation. You call
60 /// [`new()`][Self::new()] in the ‘start’ method, followed by
61 /// [`set_task_data()`][Self::set_task_data()] and the like if you need to keep some
62 /// additional data associated with the task, and then pass the
63 /// task object around through your asynchronous operation.
64 /// Eventually, you will call a method such as
65 /// [`return_pointer()`][Self::return_pointer()] or [`return_error()`][Self::return_error()], which
66 /// will save the value you give it and then invoke the task’s callback
67 /// function in the thread-default main context (see
68 /// [`glib::MainContext::push_thread_default()`][crate::glib::MainContext::push_thread_default()])
69 /// where it was created (waiting until the next iteration of the main
70 /// loop first, if necessary). The caller will pass the `GTask` back to
71 /// the operation’s finish function (as a [`AsyncResult`][crate::AsyncResult]), and you can
72 /// use [`propagate_pointer()`][Self::propagate_pointer()] or the like to extract the
73 /// return value.
74 ///
75 /// Using `GTask` requires the thread-default [`glib::MainContext`][crate::glib::MainContext] from when
76 /// the `GTask` was constructed to be running at least until the task has
77 /// completed and its data has been freed.
78 ///
79 /// If a `GTask` has been constructed and its callback set, it is an error to
80 /// not call `g_task_return_*()` on it. GLib will warn at runtime if this happens
81 /// (since 2.76).
82 ///
83 /// Here is an example for using `GTask` as a [`AsyncResult`][crate::AsyncResult]:
84 /// **⚠️ The following code is in c ⚠️**
85 ///
86 /// ```c
87 /// typedef struct {
88 /// CakeFrostingType frosting;
89 /// char *message;
90 /// } DecorationData;
91 ///
92 /// static void
93 /// decoration_data_free (DecorationData *decoration)
94 /// {
95 /// g_free (decoration->message);
96 /// g_slice_free (DecorationData, decoration);
97 /// }
98 ///
99 /// static void
100 /// baked_cb (Cake *cake,
101 /// gpointer user_data)
102 /// {
103 /// GTask *task = user_data;
104 /// DecorationData *decoration = g_task_get_task_data (task);
105 /// GError *error = NULL;
106 ///
107 /// if (cake == NULL)
108 /// {
109 /// g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_NO_FLOUR,
110 /// "Go to the supermarket");
111 /// g_object_unref (task);
112 /// return;
113 /// }
114 ///
115 /// if (!cake_decorate (cake, decoration->frosting, decoration->message, &error))
116 /// {
117 /// g_object_unref (cake);
118 /// // g_task_return_error() takes ownership of error
119 /// g_task_return_error (task, error);
120 /// g_object_unref (task);
121 /// return;
122 /// }
123 ///
124 /// g_task_return_pointer (task, cake, g_object_unref);
125 /// g_object_unref (task);
126 /// }
127 ///
128 /// void
129 /// baker_bake_cake_async (Baker *self,
130 /// guint radius,
131 /// CakeFlavor flavor,
132 /// CakeFrostingType frosting,
133 /// const char *message,
134 /// GCancellable *cancellable,
135 /// GAsyncReadyCallback callback,
136 /// gpointer user_data)
137 /// {
138 /// GTask *task;
139 /// DecorationData *decoration;
140 /// Cake *cake;
141 ///
142 /// task = g_task_new (self, cancellable, callback, user_data);
143 /// if (radius < 3)
144 /// {
145 /// g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_TOO_SMALL,
146 /// "%ucm radius cakes are silly",
147 /// radius);
148 /// g_object_unref (task);
149 /// return;
150 /// }
151 ///
152 /// cake = _baker_get_cached_cake (self, radius, flavor, frosting, message);
153 /// if (cake != NULL)
154 /// {
155 /// // _baker_get_cached_cake() returns a reffed cake
156 /// g_task_return_pointer (task, cake, g_object_unref);
157 /// g_object_unref (task);
158 /// return;
159 /// }
160 ///
161 /// decoration = g_slice_new (DecorationData);
162 /// decoration->frosting = frosting;
163 /// decoration->message = g_strdup (message);
164 /// g_task_set_task_data (task, decoration, (GDestroyNotify) decoration_data_free);
165 ///
166 /// _baker_begin_cake (self, radius, flavor, cancellable, baked_cb, task);
167 /// }
168 ///
169 /// Cake *
170 /// baker_bake_cake_finish (Baker *self,
171 /// GAsyncResult *result,
172 /// GError **error)
173 /// {
174 /// g_return_val_if_fail (g_task_is_valid (result, self), NULL);
175 ///
176 /// return g_task_propagate_pointer (G_TASK (result), error);
177 /// }
178 /// ```
179 ///
180 /// ## Chained asynchronous operations
181 ///
182 /// `GTask` also tries to simplify asynchronous operations that
183 /// internally chain together several smaller asynchronous
184 /// operations. [`cancellable()`][Self::cancellable()], [`context()`][Self::context()],
185 /// and [`priority()`][Self::priority()] allow you to get back the task’s
186 /// [`Cancellable`][crate::Cancellable], [`glib::MainContext`][crate::glib::MainContext], and
187 /// [I/O priority](iface.AsyncResult.html#io-priority)
188 /// when starting a new subtask, so you don’t have to keep track
189 /// of them yourself. [`attach_source()`][Self::attach_source()] simplifies the case
190 /// of waiting for a source to fire (automatically using the correct
191 /// [`glib::MainContext`][crate::glib::MainContext] and priority).
192 ///
193 /// Here is an example for chained asynchronous operations:
194 /// **⚠️ The following code is in c ⚠️**
195 ///
196 /// ```c
197 /// typedef struct {
198 /// Cake *cake;
199 /// CakeFrostingType frosting;
200 /// char *message;
201 /// } BakingData;
202 ///
203 /// static void
204 /// decoration_data_free (BakingData *bd)
205 /// {
206 /// if (bd->cake)
207 /// g_object_unref (bd->cake);
208 /// g_free (bd->message);
209 /// g_slice_free (BakingData, bd);
210 /// }
211 ///
212 /// static void
213 /// decorated_cb (Cake *cake,
214 /// GAsyncResult *result,
215 /// gpointer user_data)
216 /// {
217 /// GTask *task = user_data;
218 /// GError *error = NULL;
219 ///
220 /// if (!cake_decorate_finish (cake, result, &error))
221 /// {
222 /// g_object_unref (cake);
223 /// g_task_return_error (task, error);
224 /// g_object_unref (task);
225 /// return;
226 /// }
227 ///
228 /// // baking_data_free() will drop its ref on the cake, so we have to
229 /// // take another here to give to the caller.
230 /// g_task_return_pointer (task, g_object_ref (cake), g_object_unref);
231 /// g_object_unref (task);
232 /// }
233 ///
234 /// static gboolean
235 /// decorator_ready (gpointer user_data)
236 /// {
237 /// GTask *task = user_data;
238 /// BakingData *bd = g_task_get_task_data (task);
239 ///
240 /// cake_decorate_async (bd->cake, bd->frosting, bd->message,
241 /// g_task_get_cancellable (task),
242 /// decorated_cb, task);
243 ///
244 /// return G_SOURCE_REMOVE;
245 /// }
246 ///
247 /// static void
248 /// baked_cb (Cake *cake,
249 /// gpointer user_data)
250 /// {
251 /// GTask *task = user_data;
252 /// BakingData *bd = g_task_get_task_data (task);
253 /// GError *error = NULL;
254 ///
255 /// if (cake == NULL)
256 /// {
257 /// g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_NO_FLOUR,
258 /// "Go to the supermarket");
259 /// g_object_unref (task);
260 /// return;
261 /// }
262 ///
263 /// bd->cake = cake;
264 ///
265 /// // Bail out now if the user has already cancelled
266 /// if (g_task_return_error_if_cancelled (task))
267 /// {
268 /// g_object_unref (task);
269 /// return;
270 /// }
271 ///
272 /// if (cake_decorator_available (cake))
273 /// decorator_ready (task);
274 /// else
275 /// {
276 /// GSource *source;
277 ///
278 /// source = cake_decorator_wait_source_new (cake);
279 /// // Attach @source to @task’s GMainContext and have it call
280 /// // decorator_ready() when it is ready.
281 /// g_task_attach_source (task, source, decorator_ready);
282 /// g_source_unref (source);
283 /// }
284 /// }
285 ///
286 /// void
287 /// baker_bake_cake_async (Baker *self,
288 /// guint radius,
289 /// CakeFlavor flavor,
290 /// CakeFrostingType frosting,
291 /// const char *message,
292 /// gint priority,
293 /// GCancellable *cancellable,
294 /// GAsyncReadyCallback callback,
295 /// gpointer user_data)
296 /// {
297 /// GTask *task;
298 /// BakingData *bd;
299 ///
300 /// task = g_task_new (self, cancellable, callback, user_data);
301 /// g_task_set_priority (task, priority);
302 ///
303 /// bd = g_slice_new0 (BakingData);
304 /// bd->frosting = frosting;
305 /// bd->message = g_strdup (message);
306 /// g_task_set_task_data (task, bd, (GDestroyNotify) baking_data_free);
307 ///
308 /// _baker_begin_cake (self, radius, flavor, cancellable, baked_cb, task);
309 /// }
310 ///
311 /// Cake *
312 /// baker_bake_cake_finish (Baker *self,
313 /// GAsyncResult *result,
314 /// GError **error)
315 /// {
316 /// g_return_val_if_fail (g_task_is_valid (result, self), NULL);
317 ///
318 /// return g_task_propagate_pointer (G_TASK (result), error);
319 /// }
320 /// ```
321 ///
322 /// ## Asynchronous operations from synchronous ones
323 ///
324 /// You can use [`run_in_thread()`][Self::run_in_thread()] to turn a synchronous
325 /// operation into an asynchronous one, by running it in a thread.
326 /// When it completes, the result will be dispatched to the thread-default
327 /// main context (see [`glib::MainContext::push_thread_default()`][crate::glib::MainContext::push_thread_default()])
328 /// where the `GTask` was created.
329 ///
330 /// Running a task in a thread:
331 /// **⚠️ The following code is in c ⚠️**
332 ///
333 /// ```c
334 /// typedef struct {
335 /// guint radius;
336 /// CakeFlavor flavor;
337 /// CakeFrostingType frosting;
338 /// char *message;
339 /// } CakeData;
340 ///
341 /// static void
342 /// cake_data_free (CakeData *cake_data)
343 /// {
344 /// g_free (cake_data->message);
345 /// g_slice_free (CakeData, cake_data);
346 /// }
347 ///
348 /// static void
349 /// bake_cake_thread (GTask *task,
350 /// gpointer source_object,
351 /// gpointer task_data,
352 /// GCancellable *cancellable)
353 /// {
354 /// Baker *self = source_object;
355 /// CakeData *cake_data = task_data;
356 /// Cake *cake;
357 /// GError *error = NULL;
358 ///
359 /// cake = bake_cake (baker, cake_data->radius, cake_data->flavor,
360 /// cake_data->frosting, cake_data->message,
361 /// cancellable, &error);
362 /// if (cake)
363 /// g_task_return_pointer (task, cake, g_object_unref);
364 /// else
365 /// g_task_return_error (task, error);
366 /// }
367 ///
368 /// void
369 /// baker_bake_cake_async (Baker *self,
370 /// guint radius,
371 /// CakeFlavor flavor,
372 /// CakeFrostingType frosting,
373 /// const char *message,
374 /// GCancellable *cancellable,
375 /// GAsyncReadyCallback callback,
376 /// gpointer user_data)
377 /// {
378 /// CakeData *cake_data;
379 /// GTask *task;
380 ///
381 /// cake_data = g_slice_new (CakeData);
382 /// cake_data->radius = radius;
383 /// cake_data->flavor = flavor;
384 /// cake_data->frosting = frosting;
385 /// cake_data->message = g_strdup (message);
386 /// task = g_task_new (self, cancellable, callback, user_data);
387 /// g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
388 /// g_task_run_in_thread (task, bake_cake_thread);
389 /// g_object_unref (task);
390 /// }
391 ///
392 /// Cake *
393 /// baker_bake_cake_finish (Baker *self,
394 /// GAsyncResult *result,
395 /// GError **error)
396 /// {
397 /// g_return_val_if_fail (g_task_is_valid (result, self), NULL);
398 ///
399 /// return g_task_propagate_pointer (G_TASK (result), error);
400 /// }
401 /// ```
402 ///
403 /// ## Adding cancellability to uncancellable tasks
404 ///
405 /// Finally, [`run_in_thread()`][Self::run_in_thread()] and
406 /// [`run_in_thread_sync()`][Self::run_in_thread_sync()] can be used to turn an uncancellable
407 /// operation into a cancellable one. If you call
408 /// [`set_return_on_cancel()`][Self::set_return_on_cancel()], passing `TRUE`, then if the task’s
409 /// [`Cancellable`][crate::Cancellable] is cancelled, it will return control back to the
410 /// caller immediately, while allowing the task thread to continue running in the
411 /// background (and simply discarding its result when it finally does finish).
412 /// Provided that the task thread is careful about how it uses
413 /// locks and other externally-visible resources, this allows you
414 /// to make ‘GLib-friendly’ asynchronous and cancellable
415 /// synchronous variants of blocking APIs.
416 ///
417 /// Cancelling a task:
418 /// **⚠️ The following code is in c ⚠️**
419 ///
420 /// ```c
421 /// static void
422 /// bake_cake_thread (GTask *task,
423 /// gpointer source_object,
424 /// gpointer task_data,
425 /// GCancellable *cancellable)
426 /// {
427 /// Baker *self = source_object;
428 /// CakeData *cake_data = task_data;
429 /// Cake *cake;
430 /// GError *error = NULL;
431 ///
432 /// cake = bake_cake (baker, cake_data->radius, cake_data->flavor,
433 /// cake_data->frosting, cake_data->message,
434 /// &error);
435 /// if (error)
436 /// {
437 /// g_task_return_error (task, error);
438 /// return;
439 /// }
440 ///
441 /// // If the task has already been cancelled, then we don’t want to add
442 /// // the cake to the cake cache. Likewise, we don’t want to have the
443 /// // task get cancelled in the middle of updating the cache.
444 /// // g_task_set_return_on_cancel() will return %TRUE here if it managed
445 /// // to disable return-on-cancel, or %FALSE if the task was cancelled
446 /// // before it could.
447 /// if (g_task_set_return_on_cancel (task, FALSE))
448 /// {
449 /// // If the caller cancels at this point, their
450 /// // GAsyncReadyCallback won’t be invoked until we return,
451 /// // so we don’t have to worry that this code will run at
452 /// // the same time as that code does. But if there were
453 /// // other functions that might look at the cake cache,
454 /// // then we’d probably need a GMutex here as well.
455 /// baker_add_cake_to_cache (baker, cake);
456 /// g_task_return_pointer (task, cake, g_object_unref);
457 /// }
458 /// }
459 ///
460 /// void
461 /// baker_bake_cake_async (Baker *self,
462 /// guint radius,
463 /// CakeFlavor flavor,
464 /// CakeFrostingType frosting,
465 /// const char *message,
466 /// GCancellable *cancellable,
467 /// GAsyncReadyCallback callback,
468 /// gpointer user_data)
469 /// {
470 /// CakeData *cake_data;
471 /// GTask *task;
472 ///
473 /// cake_data = g_slice_new (CakeData);
474 ///
475 /// ...
476 ///
477 /// task = g_task_new (self, cancellable, callback, user_data);
478 /// g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
479 /// g_task_set_return_on_cancel (task, TRUE);
480 /// g_task_run_in_thread (task, bake_cake_thread);
481 /// }
482 ///
483 /// Cake *
484 /// baker_bake_cake_sync (Baker *self,
485 /// guint radius,
486 /// CakeFlavor flavor,
487 /// CakeFrostingType frosting,
488 /// const char *message,
489 /// GCancellable *cancellable,
490 /// GError **error)
491 /// {
492 /// CakeData *cake_data;
493 /// GTask *task;
494 /// Cake *cake;
495 ///
496 /// cake_data = g_slice_new (CakeData);
497 ///
498 /// ...
499 ///
500 /// task = g_task_new (self, cancellable, NULL, NULL);
501 /// g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
502 /// g_task_set_return_on_cancel (task, TRUE);
503 /// g_task_run_in_thread_sync (task, bake_cake_thread);
504 ///
505 /// cake = g_task_propagate_pointer (task, error);
506 /// g_object_unref (task);
507 /// return cake;
508 /// }
509 /// ```
510 ///
511 /// ## Porting from `Gio::SimpleAsyncResult`
512 ///
513 /// `GTask`’s API attempts to be simpler than `Gio::SimpleAsyncResult`’s
514 /// in several ways:
515 ///
516 /// - You can save task-specific data with [`set_task_data()`][Self::set_task_data()], and
517 /// retrieve it later with [`task_data()`][Self::task_data()]. This replaces the
518 /// abuse of `Gio::SimpleAsyncResult::set_op_res_gpointer()` for the same
519 /// purpose with `Gio::SimpleAsyncResult`.
520 /// - In addition to the task data, `GTask` also keeps track of the
521 /// [priority](iface.AsyncResult.html#io-priority), [`Cancellable`][crate::Cancellable],
522 /// and [`glib::MainContext`][crate::glib::MainContext] associated with the task, so tasks that
523 /// consist of a chain of simpler asynchronous operations will have easy access
524 /// to those values when starting each sub-task.
525 /// - [`return_error_if_cancelled()`][Self::return_error_if_cancelled()] provides simplified
526 /// handling for cancellation. In addition, cancellation
527 /// overrides any other `GTask` return value by default, like
528 /// `Gio::SimpleAsyncResult` does when
529 /// `Gio::SimpleAsyncResult::set_check_cancellable()` is called.
530 /// (You can use [`set_check_cancellable()`][Self::set_check_cancellable()] to turn off that
531 /// behavior.) On the other hand, [`run_in_thread()`][Self::run_in_thread()]
532 /// guarantees that it will always run your
533 /// `task_func`, even if the task’s [`Cancellable`][crate::Cancellable]
534 /// is already cancelled before the task gets a chance to run;
535 /// you can start your `task_func` with a
536 /// [`return_error_if_cancelled()`][Self::return_error_if_cancelled()] check if you need the
537 /// old behavior.
538 /// - The ‘return’ methods (eg, [`return_pointer()`][Self::return_pointer()])
539 /// automatically cause the task to be ‘completed’ as well, and
540 /// there is no need to worry about the ‘complete’ vs ‘complete in idle’
541 /// distinction. (`GTask` automatically figures out
542 /// whether the task’s callback can be invoked directly, or
543 /// if it needs to be sent to another [`glib::MainContext`][crate::glib::MainContext], or delayed
544 /// until the next iteration of the current [`glib::MainContext`][crate::glib::MainContext].)
545 /// - The ‘finish’ functions for `GTask` based operations are generally
546 /// much simpler than `Gio::SimpleAsyncResult` ones, normally consisting
547 /// of only a single call to [`propagate_pointer()`][Self::propagate_pointer()] or the like.
548 /// Since [`propagate_pointer()`][Self::propagate_pointer()] ‘steals’ the return value from
549 /// the `GTask`, it is not necessary to juggle pointers around to
550 /// prevent it from being freed twice.
551 /// - With `Gio::SimpleAsyncResult`, it was common to call
552 /// `Gio::SimpleAsyncResult::propagate_error()` from the
553 /// `_finish()` wrapper function, and have
554 /// virtual method implementations only deal with successful
555 /// returns. This behavior is deprecated, because it makes it
556 /// difficult for a subclass to chain to a parent class’s async
557 /// methods. Instead, the wrapper function should just be a
558 /// simple wrapper, and the virtual method should call an
559 /// appropriate `g_task_propagate_` function.
560 /// Note that wrapper methods can now use
561 /// [`AsyncResultExt::legacy_propagate_error()`][crate::prelude::AsyncResultExt::legacy_propagate_error()] to do old-style
562 /// `Gio::SimpleAsyncResult` error-returning behavior, and
563 /// `Gio::AsyncResult::is_tagged()` to check if a result is tagged as
564 /// having come from the `_async()` wrapper
565 /// function (for ‘short-circuit’ results, such as when passing
566 /// `0` to [`InputStreamExtManual::read_async()`][crate::prelude::InputStreamExtManual::read_async()]).
567 ///
568 /// ## Thread-safety considerations
569 ///
570 /// Due to some infelicities in the API design, there is a
571 /// thread-safety concern that users of `GTask` have to be aware of:
572 ///
573 /// If the `main` thread drops its last reference to the source object
574 /// or the task data before the task is finalized, then the finalizers
575 /// of these objects may be called on the worker thread.
576 ///
577 /// This is a problem if the finalizers use non-threadsafe API, and
578 /// can lead to hard-to-debug crashes. Possible workarounds include:
579 ///
580 /// - Clear task data in a signal handler for `notify::completed`
581 /// - Keep iterating a main context in the main thread and defer
582 /// dropping the reference to the source object to that main
583 /// context when the task is finalized
584 ///
585 /// ## Properties
586 ///
587 ///
588 /// #### `completed`
589 /// Whether the task has completed, meaning its callback (if set) has been
590 /// invoked.
591 ///
592 /// This can only happen after g_task_return_pointer(),
593 /// g_task_return_error() or one of the other return functions have been called
594 /// on the task. However, it is not guaranteed to happen immediately after
595 /// those functions are called, as the task’s callback may need to be scheduled
596 /// to run in a different thread.
597 ///
598 /// That means it is **not safe** to use this property to track whether a
599 /// return function has been called on the #GTask. Callers must do that
600 /// tracking themselves, typically by linking the lifetime of the #GTask to the
601 /// control flow of their code.
602 ///
603 /// This property is guaranteed to change from [`false`] to [`true`] exactly once.
604 ///
605 /// The #GObject::notify signal for this change is emitted in the same main
606 /// context as the task’s callback, immediately after that callback is invoked.
607 ///
608 /// Readable
609 ///
610 /// # Implements
611 ///
612 /// [`trait@glib::ObjectExt`], [`AsyncResultExt`][trait@crate::prelude::AsyncResultExt]
613 #[doc(alias = "GTask")]
614 pub struct Task<V: ValueType + Send>(Object<ffi::GTask, ffi::GTaskClass>) @implements AsyncResult;
615
616 match fn {
617 type_ => || ffi::g_task_get_type(),
618 }
619}
620
621macro_rules! task_impl {
622 ($name:ident $(, @bound: $bound:tt)? $(, @safety: $safety:tt)?) => {
623 impl <V: Into<glib::Value> + ValueType $(+ $bound)?> $name<V> {
624 #[doc(alias = "g_task_new")]
625 #[allow(unused_unsafe)]
626 pub unsafe fn new<S, P, Q>(
627 source_object: Option<&S>,
628 cancellable: Option<&P>,
629 callback: Q,
630 ) -> Self
631 where
632 S: IsA<glib::Object> $(+ $bound)?,
633 P: IsA<Cancellable>,
634 Q: FnOnce($name<V>, Option<&S>) $(+ $bound)? + 'static,
635 {
636 let callback_data = Box_::new(callback);
637 unsafe extern "C" fn trampoline<
638 S: IsA<glib::Object> $(+ $bound)?,
639 V: ValueType $(+ $bound)?,
640 Q: FnOnce($name<V>, Option<&S>) $(+ $bound)? + 'static,
641 >(
642 source_object: *mut glib::gobject_ffi::GObject,
643 res: *mut ffi::GAsyncResult,
644 user_data: glib::ffi::gpointer,
645 ) {
646 let callback: Box_<Q> = Box::from_raw(user_data as *mut _);
647 let task = AsyncResult::from_glib_none(res)
648 .downcast::<$name<V>>()
649 .unwrap();
650 let source_object = Option::<glib::Object>::from_glib_borrow(source_object);
651 callback(
652 task,
653 source_object.as_ref().as_ref().map(|s| s.unsafe_cast_ref()),
654 );
655 }
656 let callback = trampoline::<S, V, Q>;
657 unsafe {
658 from_glib_full(ffi::g_task_new(
659 source_object.map(|p| p.as_ref()).to_glib_none().0,
660 cancellable.map(|p| p.as_ref()).to_glib_none().0,
661 Some(callback),
662 Box_::into_raw(callback_data) as *mut _,
663 ))
664 }
665 }
666
667 #[doc(alias = "g_task_get_cancellable")]
668 #[doc(alias = "get_cancellable")]
669 pub fn cancellable(&self) -> Option<Cancellable> {
670 unsafe { from_glib_none(ffi::g_task_get_cancellable(self.to_glib_none().0)) }
671 }
672
673 #[doc(alias = "g_task_get_check_cancellable")]
674 #[doc(alias = "get_check_cancellable")]
675 pub fn is_check_cancellable(&self) -> bool {
676 unsafe { from_glib(ffi::g_task_get_check_cancellable(self.to_glib_none().0)) }
677 }
678
679 #[doc(alias = "g_task_set_check_cancellable")]
680 pub fn set_check_cancellable(&self, check_cancellable: bool) {
681 unsafe {
682 ffi::g_task_set_check_cancellable(self.to_glib_none().0, check_cancellable.into_glib());
683 }
684 }
685
686 #[cfg(feature = "v2_60")]
687 #[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
688 #[doc(alias = "g_task_set_name")]
689 pub fn set_name(&self, name: Option<&str>) {
690 unsafe {
691 ffi::g_task_set_name(self.to_glib_none().0, name.to_glib_none().0);
692 }
693 }
694
695 #[doc(alias = "g_task_set_return_on_cancel")]
696 pub fn set_return_on_cancel(&self, return_on_cancel: bool) -> bool {
697 unsafe {
698 from_glib(ffi::g_task_set_return_on_cancel(
699 self.to_glib_none().0,
700 return_on_cancel.into_glib(),
701 ))
702 }
703 }
704
705 #[doc(alias = "g_task_is_valid")]
706 pub fn is_valid(
707 result: &impl IsA<AsyncResult>,
708 source_object: Option<&impl IsA<glib::Object>>,
709 ) -> bool {
710 unsafe {
711 from_glib(ffi::g_task_is_valid(
712 result.as_ref().to_glib_none().0,
713 source_object.map(|p| p.as_ref()).to_glib_none().0,
714 ))
715 }
716 }
717
718 #[doc(alias = "get_priority")]
719 #[doc(alias = "g_task_get_priority")]
720 pub fn priority(&self) -> glib::source::Priority {
721 unsafe { FromGlib::from_glib(ffi::g_task_get_priority(self.to_glib_none().0)) }
722 }
723
724 #[doc(alias = "g_task_set_priority")]
725 pub fn set_priority(&self, priority: glib::source::Priority) {
726 unsafe {
727 ffi::g_task_set_priority(self.to_glib_none().0, priority.into_glib());
728 }
729 }
730
731 #[doc(alias = "g_task_get_completed")]
732 #[doc(alias = "get_completed")]
733 pub fn is_completed(&self) -> bool {
734 unsafe { from_glib(ffi::g_task_get_completed(self.to_glib_none().0)) }
735 }
736
737 #[doc(alias = "g_task_get_context")]
738 #[doc(alias = "get_context")]
739 pub fn context(&self) -> glib::MainContext {
740 unsafe { from_glib_none(ffi::g_task_get_context(self.to_glib_none().0)) }
741 }
742
743 #[cfg(feature = "v2_60")]
744 #[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
745 #[doc(alias = "g_task_get_name")]
746 #[doc(alias = "get_name")]
747 pub fn name(&self) -> Option<glib::GString> {
748 unsafe { from_glib_none(ffi::g_task_get_name(self.to_glib_none().0)) }
749 }
750
751 #[doc(alias = "g_task_get_return_on_cancel")]
752 #[doc(alias = "get_return_on_cancel")]
753 pub fn is_return_on_cancel(&self) -> bool {
754 unsafe { from_glib(ffi::g_task_get_return_on_cancel(self.to_glib_none().0)) }
755 }
756
757 #[doc(alias = "g_task_had_error")]
758 pub fn had_error(&self) -> bool {
759 unsafe { from_glib(ffi::g_task_had_error(self.to_glib_none().0)) }
760 }
761
762 #[doc(alias = "completed")]
763 pub fn connect_completed_notify<F>(&self, f: F) -> SignalHandlerId
764 where
765 F: Fn(&$name<V>) $(+ $bound)? + 'static,
766 {
767 unsafe extern "C" fn notify_completed_trampoline<V, F>(
768 this: *mut ffi::GTask,
769 _param_spec: glib::ffi::gpointer,
770 f: glib::ffi::gpointer,
771 ) where
772 V: ValueType $(+ $bound)?,
773 F: Fn(&$name<V>) + 'static,
774 {
775 let f: &F = &*(f as *const F);
776 f(&from_glib_borrow(this))
777 }
778 unsafe {
779 let f: Box_<F> = Box_::new(f);
780 connect_raw(
781 self.as_ptr() as *mut _,
782 b"notify::completed\0".as_ptr() as *const _,
783 Some(transmute::<*const (), unsafe extern "C" fn()>(
784 notify_completed_trampoline::<V, F> as *const (),
785 )),
786 Box_::into_raw(f),
787 )
788 }
789 }
790
791 // the following functions are marked unsafe since they cannot be called
792 // more than once, but we have no way to enforce that since the task can be cloned
793
794 #[doc(alias = "g_task_return_error_if_cancelled")]
795 #[allow(unused_unsafe)]
796 pub $($safety)? fn return_error_if_cancelled(&self) -> bool {
797 unsafe { from_glib(ffi::g_task_return_error_if_cancelled(self.to_glib_none().0)) }
798 }
799
800 // rustdoc-stripper-ignore-next
801 /// Set the result of the task
802 ///
803 /// # Safety
804 ///
805 /// The value must be read with [`Task::propagate`],
806 /// `g_task_propagate_value` or `g_task_propagate_pointer`.
807 #[doc(alias = "g_task_return_value")]
808 #[doc(alias = "g_task_return_pointer")]
809 #[doc(alias = "g_task_return_error")]
810 #[allow(unused_unsafe)]
811 pub $($safety)? fn return_result(self, result: Result<V, glib::Error>) {
812 #[cfg(not(feature = "v2_64"))]
813 unsafe extern "C" fn value_free(value: *mut libc::c_void) {
814 let _: glib::Value = from_glib_full(value as *mut glib::gobject_ffi::GValue);
815 }
816
817 match result {
818 #[cfg(feature = "v2_64")]
819 Ok(v) => unsafe {
820 ffi::g_task_return_value(
821 self.to_glib_none().0,
822 v.to_value().to_glib_none().0 as *mut _,
823 )
824 },
825 #[cfg(not(feature = "v2_64"))]
826 Ok(v) => unsafe {
827 let v: glib::Value = v.into();
828 ffi::g_task_return_pointer(
829 self.to_glib_none().0,
830 <glib::Value as glib::translate::IntoGlibPtr::<*mut glib::gobject_ffi::GValue>>::into_glib_ptr(v) as glib::ffi::gpointer,
831 Some(value_free),
832 )
833 },
834 Err(e) => unsafe {
835 ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr());
836 },
837 }
838 }
839
840 // rustdoc-stripper-ignore-next
841 /// Set the result of the task as a boolean
842 ///
843 /// # Safety
844 ///
845 /// The value must be read with [`Task::propagate_boolean`],
846 /// or `g_task_propagate_boolean`.
847 #[doc(alias = "g_task_return_boolean")]
848 #[allow(unused_unsafe)]
849 pub $($safety)? fn return_boolean_result(self, result: Result<bool, glib::Error>) {
850 match result {
851 Ok(v) => unsafe { ffi::g_task_return_boolean(self.to_glib_none().0, v as i32) },
852 Err(e) => unsafe { ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr()) },
853 }
854 }
855
856 // rustdoc-stripper-ignore-next
857 /// Set the result of the task as an int
858 ///
859 /// # Safety
860 ///
861 /// The value must be read with [`Task::propagate_int`],
862 /// or `g_task_propagate_int`.
863 #[doc(alias = "g_task_return_int")]
864 #[allow(unused_unsafe)]
865 pub $($safety)? fn return_int_result(self, result: Result<isize, glib::Error>) {
866 match result {
867 Ok(v) => unsafe { ffi::g_task_return_int(self.to_glib_none().0, v) },
868 Err(e) => unsafe { ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr()) },
869 }
870 }
871
872
873 // rustdoc-stripper-ignore-next
874 /// Gets the result of the task and transfers ownership of it
875 ///
876 /// # Safety
877 ///
878 /// This must only be called once, and only if the result was set
879 /// via [`Task::return_result`], `g_task_return_value` or
880 /// `g_task_return_pointer`.
881 #[doc(alias = "g_task_propagate_value")]
882 #[doc(alias = "g_task_propagate_pointer")]
883 #[allow(unused_unsafe)]
884 pub unsafe fn propagate(self) -> Result<V, glib::Error> {
885 let mut error = ptr::null_mut();
886
887 unsafe {
888 #[cfg(feature = "v2_64")]
889 {
890 let mut value = glib::Value::uninitialized();
891 ffi::g_task_propagate_value(
892 self.to_glib_none().0,
893 value.to_glib_none_mut().0,
894 &mut error,
895 );
896
897 if error.is_null() {
898 Ok(V::from_value(&value))
899 } else {
900 Err(from_glib_full(error))
901 }
902 }
903
904 #[cfg(not(feature = "v2_64"))]
905 {
906 let value = ffi::g_task_propagate_pointer(self.to_glib_none().0, &mut error);
907
908 if error.is_null() {
909 let value = Option::<glib::Value>::from_glib_full(
910 value as *mut glib::gobject_ffi::GValue,
911 )
912 .expect("Task::propagate() called before Task::return_result()");
913 Ok(V::from_value(&value))
914 } else {
915 Err(from_glib_full(error))
916 }
917 }
918 }
919 }
920
921 // rustdoc-stripper-ignore-next
922 /// Gets the result of the task as a boolean, or the error
923 ///
924 /// # Safety
925 ///
926 /// This must only be called once, and only if the result was set
927 /// via [`Task::return_boolean_result`], or `g_task_return_boolean`.
928 #[doc(alias = "g_task_propagate_boolean")]
929 #[allow(unused_unsafe)]
930 pub unsafe fn propagate_boolean(self) -> Result<bool, glib::Error> {
931 let mut error = ptr::null_mut();
932
933 unsafe {
934 let res = ffi::g_task_propagate_boolean(self.to_glib_none().0, &mut error);
935
936 if error.is_null() {
937 Ok(res != 0)
938 } else {
939 Err(from_glib_full(error))
940 }
941 }
942 }
943
944 // rustdoc-stripper-ignore-next
945 /// Gets the result of the task as an int, or the error
946 ///
947 /// # Safety
948 ///
949 /// This must only be called once, and only if the result was set
950 /// via [`Task::return_int_result`], or `g_task_return_int`.
951 #[doc(alias = "g_task_propagate_int")]
952 #[allow(unused_unsafe)]
953 pub unsafe fn propagate_int(self) -> Result<isize, glib::Error> {
954 let mut error = ptr::null_mut();
955
956 unsafe {
957 let res = ffi::g_task_propagate_int(self.to_glib_none().0, &mut error);
958
959 if error.is_null() {
960 Ok(res)
961 } else {
962 Err(from_glib_full(error))
963 }
964 }
965 }
966 }
967 }
968}
969
970task_impl!(LocalTask);
971task_impl!(Task, @bound: Send, @safety: unsafe);
972
973impl<V: ValueType + Send> Task<V> {
974 #[doc(alias = "g_task_run_in_thread")]
975 pub fn run_in_thread<S, Q>(&self, task_func: Q)
976 where
977 S: IsA<glib::Object> + Send,
978 Q: FnOnce(Self, Option<&S>, Option<&Cancellable>) + Send + 'static,
979 {
980 let task_func_data = Box_::new(task_func);
981
982 // We store the func pointer into the task data.
983 // We intentionally do not expose a way to set the task data in the bindings.
984 // If we detect that the task data is set, there is not much we can do, so we panic.
985 unsafe {
986 assert!(
987 ffi::g_task_get_task_data(self.to_glib_none().0).is_null(),
988 "Task data was manually set or the task was run thread multiple times"
989 );
990
991 ffi::g_task_set_task_data(
992 self.to_glib_none().0,
993 Box_::into_raw(task_func_data) as *mut _,
994 None,
995 );
996 }
997
998 unsafe extern "C" fn trampoline<V, S, Q>(
999 task: *mut ffi::GTask,
1000 source_object: *mut glib::gobject_ffi::GObject,
1001 user_data: glib::ffi::gpointer,
1002 cancellable: *mut ffi::GCancellable,
1003 ) where
1004 V: ValueType + Send,
1005 S: IsA<glib::Object> + Send,
1006 Q: FnOnce(Task<V>, Option<&S>, Option<&Cancellable>) + Send + 'static,
1007 {
1008 let task = Task::from_glib_none(task);
1009 let source_object = Option::<glib::Object>::from_glib_borrow(source_object);
1010 let cancellable = Option::<Cancellable>::from_glib_borrow(cancellable);
1011 let task_func: Box_<Q> = Box::from_raw(user_data as *mut _);
1012 task_func(
1013 task,
1014 source_object.as_ref().as_ref().map(|s| s.unsafe_cast_ref()),
1015 cancellable.as_ref().as_ref(),
1016 );
1017 }
1018
1019 let task_func = trampoline::<V, S, Q>;
1020 unsafe {
1021 ffi::g_task_run_in_thread(self.to_glib_none().0, Some(task_func));
1022 }
1023 }
1024}
1025
1026unsafe impl<V: ValueType + Send> Send for Task<V> {}
1027unsafe impl<V: ValueType + Send> Sync for Task<V> {}
1028
1029// rustdoc-stripper-ignore-next
1030/// A handle to a task running on the I/O thread pool.
1031///
1032/// Like [`std::thread::JoinHandle`] for a blocking I/O task rather than a thread. The return value
1033/// from the task can be retrieved by awaiting on this handle. Dropping the handle "detaches" the
1034/// task, allowing it to complete but discarding the return value.
1035#[derive(Debug)]
1036pub struct JoinHandle<T> {
1037 rx: oneshot::Receiver<std::thread::Result<T>>,
1038}
1039
1040impl<T> JoinHandle<T> {
1041 #[inline]
1042 fn new() -> (Self, oneshot::Sender<std::thread::Result<T>>) {
1043 let (tx, rx) = oneshot::channel();
1044 (Self { rx }, tx)
1045 }
1046}
1047
1048impl<T> Future for JoinHandle<T> {
1049 type Output = std::thread::Result<T>;
1050 #[inline]
1051 fn poll(
1052 mut self: std::pin::Pin<&mut Self>,
1053 cx: &mut std::task::Context<'_>,
1054 ) -> std::task::Poll<Self::Output> {
1055 std::pin::Pin::new(&mut self.rx)
1056 .poll(cx)
1057 .map(|r| r.unwrap())
1058 }
1059}
1060
1061impl<T> futures_core::FusedFuture for JoinHandle<T> {
1062 #[inline]
1063 fn is_terminated(&self) -> bool {
1064 self.rx.is_terminated()
1065 }
1066}
1067
1068// rustdoc-stripper-ignore-next
1069/// Runs a blocking I/O task on the I/O thread pool.
1070///
1071/// Calls `func` on the internal Gio thread pool for blocking I/O operations. The thread pool is
1072/// shared with other Gio async I/O operations, and may rate-limit the tasks it receives. Callers
1073/// may want to avoid blocking indefinitely by making sure blocking calls eventually time out.
1074///
1075/// This function should not be used to spawn async tasks. Instead, use
1076/// [`glib::MainContext::spawn`] or [`glib::MainContext::spawn_local`] to run a future.
1077pub fn spawn_blocking<T, F>(func: F) -> JoinHandle<T>
1078where
1079 T: Send + 'static,
1080 F: FnOnce() -> T + Send + 'static,
1081{
1082 // use Cancellable::NONE as source obj to fulfill `Send` requirement
1083 let task = unsafe { Task::<bool>::new(Cancellable::NONE, Cancellable::NONE, |_, _| {}) };
1084 let (join, tx) = JoinHandle::new();
1085 task.run_in_thread(move |task, _: Option<&Cancellable>, _| {
1086 let res = panic::catch_unwind(panic::AssertUnwindSafe(func));
1087 let _ = tx.send(res);
1088 unsafe { ffi::g_task_return_pointer(task.to_glib_none().0, ptr::null_mut(), None) }
1089 });
1090
1091 join
1092}
1093
1094#[cfg(test)]
1095mod test {
1096 use super::*;
1097 use crate::{prelude::*, test_util::run_async_local};
1098
1099 #[test]
1100 fn test_int_value_async_result() {
1101 let fut = run_async_local(|tx, l| {
1102 let cancellable = crate::Cancellable::new();
1103 let task = unsafe {
1104 crate::LocalTask::new(
1105 None,
1106 Some(&cancellable),
1107 move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
1108 tx.send(t.propagate()).unwrap();
1109 l.quit();
1110 },
1111 )
1112 };
1113 task.return_result(Ok(100_i32));
1114 });
1115
1116 match fut {
1117 Err(_) => panic!(),
1118 Ok(i) => assert_eq!(i, 100),
1119 }
1120 }
1121
1122 #[test]
1123 fn test_boolean_async_result() {
1124 let fut = run_async_local(|tx, l| {
1125 let cancellable = crate::Cancellable::new();
1126 let task = unsafe {
1127 crate::LocalTask::new(
1128 None,
1129 Some(&cancellable),
1130 move |t: LocalTask<bool>, _b: Option<&glib::Object>| {
1131 tx.send(t.propagate_boolean()).unwrap();
1132 l.quit();
1133 },
1134 )
1135 };
1136 task.return_boolean_result(Ok(true));
1137 });
1138
1139 match fut {
1140 Err(_) => panic!(),
1141 Ok(i) => assert!(i),
1142 }
1143 }
1144
1145 #[test]
1146 fn test_int_async_result() {
1147 let fut = run_async_local(|tx, l| {
1148 let cancellable = crate::Cancellable::new();
1149 let task = unsafe {
1150 crate::LocalTask::new(
1151 None,
1152 Some(&cancellable),
1153 move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
1154 tx.send(t.propagate_int()).unwrap();
1155 l.quit();
1156 },
1157 )
1158 };
1159 task.return_int_result(Ok(100_isize));
1160 });
1161
1162 match fut {
1163 Err(_) => panic!(),
1164 Ok(i) => assert_eq!(i, 100),
1165 }
1166 }
1167
1168 #[test]
1169 fn test_object_async_result() {
1170 use glib::subclass::prelude::*;
1171 pub struct MySimpleObjectPrivate {
1172 pub size: std::cell::RefCell<Option<i64>>,
1173 }
1174
1175 #[glib::object_subclass]
1176 impl ObjectSubclass for MySimpleObjectPrivate {
1177 const NAME: &'static str = "MySimpleObjectPrivate";
1178 type Type = MySimpleObject;
1179
1180 fn new() -> Self {
1181 Self {
1182 size: std::cell::RefCell::new(Some(100)),
1183 }
1184 }
1185 }
1186
1187 impl ObjectImpl for MySimpleObjectPrivate {}
1188
1189 glib::wrapper! {
1190 pub struct MySimpleObject(ObjectSubclass<MySimpleObjectPrivate>);
1191 }
1192
1193 impl MySimpleObject {
1194 pub fn new() -> Self {
1195 glib::Object::new()
1196 }
1197
1198 #[doc(alias = "get_size")]
1199 pub fn size(&self) -> Option<i64> {
1200 *self.imp().size.borrow()
1201 }
1202
1203 pub fn set_size(&self, size: i64) {
1204 self.imp().size.borrow_mut().replace(size);
1205 }
1206 }
1207
1208 impl Default for MySimpleObject {
1209 fn default() -> Self {
1210 Self::new()
1211 }
1212 }
1213
1214 let fut = run_async_local(|tx, l| {
1215 let cancellable = crate::Cancellable::new();
1216 let task = unsafe {
1217 crate::LocalTask::new(
1218 None,
1219 Some(&cancellable),
1220 move |t: LocalTask<glib::Object>, _b: Option<&glib::Object>| {
1221 tx.send(t.propagate()).unwrap();
1222 l.quit();
1223 },
1224 )
1225 };
1226 let my_object = MySimpleObject::new();
1227 my_object.set_size(100);
1228 task.return_result(Ok(my_object.upcast::<glib::Object>()));
1229 });
1230
1231 match fut {
1232 Err(_) => panic!(),
1233 Ok(o) => {
1234 let o = o.downcast::<MySimpleObject>().unwrap();
1235 assert_eq!(o.size(), Some(100));
1236 }
1237 }
1238 }
1239
1240 #[test]
1241 fn test_error() {
1242 let fut = run_async_local(|tx, l| {
1243 let cancellable = crate::Cancellable::new();
1244 let task = unsafe {
1245 crate::LocalTask::new(
1246 None,
1247 Some(&cancellable),
1248 move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
1249 tx.send(t.propagate()).unwrap();
1250 l.quit();
1251 },
1252 )
1253 };
1254 task.return_result(Err(glib::Error::new(
1255 crate::IOErrorEnum::WouldBlock,
1256 "WouldBlock",
1257 )));
1258 });
1259
1260 match fut {
1261 Err(e) => match e.kind().unwrap() {
1262 crate::IOErrorEnum::WouldBlock => {}
1263 _ => panic!(),
1264 },
1265 Ok(_) => panic!(),
1266 }
1267 }
1268
1269 #[test]
1270 fn test_cancelled() {
1271 let fut = run_async_local(|tx, l| {
1272 let cancellable = crate::Cancellable::new();
1273 let task = unsafe {
1274 crate::LocalTask::new(
1275 None,
1276 Some(&cancellable),
1277 move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
1278 tx.send(t.propagate()).unwrap();
1279 l.quit();
1280 },
1281 )
1282 };
1283 cancellable.cancel();
1284 task.return_error_if_cancelled();
1285 });
1286
1287 match fut {
1288 Err(e) => match e.kind().unwrap() {
1289 crate::IOErrorEnum::Cancelled => {}
1290 _ => panic!(),
1291 },
1292 Ok(_) => panic!(),
1293 }
1294 }
1295}