1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
/*!
Rust wrapper for Apple's Grand Central Dispatch (GCD).
GCD is an implementation of task parallelism that allows tasks to be submitted
to queues where they are scheduled to execute.
For more information, see Apple's [Grand Central Dispatch reference](
https://developer.apple.com/library/mac/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html).
# Serial Queues
Serial queues execute tasks serially in FIFO order. The application's main
queue is serial and can be accessed through the `Queue::main` function.
```
use dispatch::{Queue, QueueAttribute};
let queue = Queue::create("com.example.rust", QueueAttribute::Serial);
queue.exec_async(|| println!("Hello"));
queue.exec_async(|| println!("World"));
```
# Concurrent Queues
Concurrent dispatch queues execute tasks concurrently. GCD provides global
concurrent queues that can be accessed through the `Queue::global` function.
`Queue` has two methods that can simplify processing data in parallel, `foreach`
and `map`:
```
use dispatch::{Queue, QueuePriority};
let queue = Queue::global(QueuePriority::Default);
let mut nums = vec![1, 2];
queue.for_each(&mut nums, |x| *x += 1);
assert!(nums == [2, 3]);
let nums = queue.map(nums, |x| x.to_string());
assert!(nums[0] == "2");
```
*/
#![warn(missing_docs)]
use std::error::Error;
use std::fmt;
use std::mem;
use std::os::raw::c_void;
use std::time::Duration;
use crate::ffi::*;
pub use crate::group::{Group, GroupGuard};
pub use crate::once::Once;
pub use crate::queue::{Queue, QueueAttribute, QueuePriority, SuspendGuard};
pub use crate::sem::{Semaphore, SemaphoreGuard};
/// Raw foreign function interface for libdispatch.
pub mod ffi;
mod group;
mod queue;
mod once;
mod sem;
/// An error indicating a wait timed out.
#[derive(Clone, Debug)]
pub struct WaitTimeout {
duration: Duration,
}
impl fmt::Display for WaitTimeout {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Wait timed out after duration {:?}", self.duration)
}
}
impl Error for WaitTimeout { }
fn time_after_delay(delay: Duration) -> dispatch_time_t {
delay.as_secs().checked_mul(1_000_000_000).and_then(|i| {
i.checked_add(delay.subsec_nanos() as u64)
}).and_then(|i| {
if i < (i64::max_value() as u64) { Some(i as i64) } else { None }
}).map_or(DISPATCH_TIME_FOREVER, |i| unsafe {
dispatch_time(DISPATCH_TIME_NOW, i)
})
}
fn context_and_function<F>(closure: F) -> (*mut c_void, dispatch_function_t)
where F: FnOnce() {
extern fn work_execute_closure<F>(context: Box<F>) where F: FnOnce() {
(*context)();
}
let closure = Box::new(closure);
let func: extern fn(Box<F>) = work_execute_closure::<F>;
unsafe {
(mem::transmute(closure), mem::transmute(func))
}
}
fn context_and_sync_function<F>(closure: &mut Option<F>) ->
(*mut c_void, dispatch_function_t)
where F: FnOnce() {
extern fn work_read_closure<F>(context: &mut Option<F>) where F: FnOnce() {
// This is always passed Some, so it's safe to unwrap
let closure = context.take().unwrap();
closure();
}
let context: *mut Option<F> = closure;
let func: extern fn(&mut Option<F>) = work_read_closure::<F>;
unsafe {
(context as *mut c_void, mem::transmute(func))
}
}
fn context_and_apply_function<F>(closure: &F) ->
(*mut c_void, extern fn(*mut c_void, usize))
where F: Fn(usize) {
extern fn work_apply_closure<F>(context: &F, iter: usize)
where F: Fn(usize) {
context(iter);
}
let context: *const F = closure;
let func: extern fn(&F, usize) = work_apply_closure::<F>;
unsafe {
(context as *mut c_void, mem::transmute(func))
}
}