Skip to content

Commit

Permalink
create queue with QoS class, resolve #7
Browse files Browse the repository at this point in the history
  • Loading branch information
flier committed Sep 3, 2018
1 parent 89194e8 commit 5c29ee5
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 4 deletions.
28 changes: 25 additions & 3 deletions src/ffi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(missing_docs, non_camel_case_types, improper_ctypes)]

use std::os::raw::{c_char, c_int, c_long, c_ulong, c_void};
use std::os::raw::{c_char, c_int, c_long, c_uint, c_ulong, c_void};

use block::{Block, BlockArguments, ConcreteBlock, IntoConcreteBlock};
use libc::{mode_t, off_t, timespec};
Expand Down Expand Up @@ -42,7 +42,7 @@ pub type dispatch_io_close_flags_t = u64;
pub type dispatch_io_interval_flags_t = u64;
pub type dispatch_queue_attr_t = *const dispatch_object_s;
pub type dispatch_block_flags_t = u64;
pub type dispatch_qos_class_t = u64;
pub type dispatch_qos_class_t = c_uint;

#[cfg_attr(any(target_os = "macos", target_os = "ios"), link(name = "System", kind = "dylib"))]
#[cfg_attr(
Expand All @@ -58,13 +58,28 @@ extern "C" {
pub fn _Block_copy(block: *const c_void) -> *mut c_void;
pub fn _Block_release(block: *mut c_void);

pub fn qos_class_self() -> dispatch_qos_class_t;
pub fn qos_class_main() -> dispatch_qos_class_t;

pub fn dispatch_queue_attr_make_initially_inactive(
attr: dispatch_queue_attr_t,
) -> dispatch_queue_attr_t;
pub fn dispatch_queue_attr_make_with_qos_class(
attr: dispatch_queue_attr_t,
qos_class: dispatch_qos_class_t,
relative_priority: c_int,
) -> dispatch_queue_attr_t;

pub fn dispatch_get_global_queue(identifier: c_long, flags: c_ulong) -> dispatch_queue_t;
pub fn dispatch_queue_create(
label: *const c_char,
attr: dispatch_queue_attr_t,
) -> dispatch_queue_t;
// dispatch_queue_attr_t dispatch_queue_attr_make_with_qos_class ( dispatch_queue_attr_t attr, dispatch_qos_class_t qos_class, int relative_priority );
pub fn dispatch_queue_get_label(queue: dispatch_queue_t) -> *const c_char;
pub fn dispatch_queue_get_qos_class(
queue: dispatch_queue_t,
relative_priority_ptr: *mut c_int,
) -> dispatch_qos_class_t;
pub fn dispatch_set_target_queue(object: dispatch_object_t, queue: dispatch_queue_t);
pub fn dispatch_main();

Expand Down Expand Up @@ -280,6 +295,13 @@ pub fn dispatch_get_main_queue() -> dispatch_queue_t {
unsafe { &_dispatch_main_q as *const _ as dispatch_queue_t }
}

pub const QOS_CLASS_USER_INTERACTIVE: dispatch_qos_class_t = 0x21;
pub const QOS_CLASS_USER_INITIATED: dispatch_qos_class_t = 0x19;
pub const QOS_CLASS_DEFAULT: dispatch_qos_class_t = 0x15;
pub const QOS_CLASS_UTILITY: dispatch_qos_class_t = 0x11;
pub const QOS_CLASS_BACKGROUND: dispatch_qos_class_t = 0x09;
pub const QOS_CLASS_UNSPECIFIED: dispatch_qos_class_t = 0x00;

pub const DISPATCH_QUEUE_SERIAL: dispatch_queue_attr_t = 0 as dispatch_queue_attr_t;
pub static DISPATCH_QUEUE_CONCURRENT: &'static dispatch_object_s =
unsafe { &_dispatch_queue_attr_concurrent };
Expand Down
79 changes: 78 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ mod blk;
mod data;
#[cfg(target_os = "macos")]
mod io;
mod qos;
#[cfg(target_os = "macos")]
mod sem;
mod time;
Expand All @@ -86,17 +87,42 @@ pub use data::{
};
#[cfg(target_os = "macos")]
pub use ffi::{DISPATCH_IO_STOP, DISPATCH_IO_STRICT_INTERVAL};
pub use qos::QosClass;
#[cfg(target_os = "macos")]
pub use sem::Semaphore;
pub use time::{after, at, now, Timeout, WaitTimeout};

/// The type of a dispatch queue.
#[derive(Clone, Debug, Hash, PartialEq)]
#[derive(Debug, Hash, PartialEq)]
pub enum QueueAttribute {
/// The queue executes blocks serially in FIFO order.
Serial,
/// The queue executes blocks concurrently.
Concurrent,
/// Attribute for dispatch queues.
Value(dispatch_queue_attr_t),
}

impl Clone for QueueAttribute {
fn clone(&self) -> Self {
match *self {
QueueAttribute::Serial => QueueAttribute::Serial,
QueueAttribute::Concurrent => QueueAttribute::Concurrent,
QueueAttribute::Value(attr) => {
unsafe { dispatch_retain(attr as *mut _) };

QueueAttribute::Value(attr)
}
}
}
}

impl Drop for QueueAttribute {
fn drop(&mut self) {
if let &mut QueueAttribute::Value(attr) = self {
unsafe { dispatch_release(attr as *mut _) }
}
}
}

impl QueueAttribute {
Expand All @@ -105,6 +131,7 @@ impl QueueAttribute {
match *self {
QueueAttribute::Serial => DISPATCH_QUEUE_SERIAL,
QueueAttribute::Concurrent => DISPATCH_QUEUE_CONCURRENT,
QueueAttribute::Value(attr) => attr,
}
}

Expand All @@ -115,6 +142,30 @@ impl QueueAttribute {
// Back then, the attr for dispatch_queue_create must be NULL.
ptr::null()
}

/// Returns an attribute value which may be provided to `Queue::create` or `Queue::with_target_queue`,
/// in order to make the created queue initially inactive.
#[cfg(target_os = "macos")]
pub fn inactive(self) -> Self {
let attr = unsafe { dispatch_queue_attr_make_initially_inactive(self.as_raw()) };

QueueAttribute::Value(attr)
}

/// Returns an attribute value which may be provided to `Queue::create` or `Queue::with_target_queue`,
/// in order to assign a QOS class and relative priority to the queue.
#[cfg(target_os = "macos")]
pub fn with_qos_class(self, qos_class: QosClass, relative_priority: i32) -> Self {
let attr = unsafe {
dispatch_queue_attr_make_with_qos_class(
self.as_raw(),
qos_class as dispatch_qos_class_t,
relative_priority,
)
};

QueueAttribute::Value(attr)
}
}

/// The priority of a global concurrent queue.
Expand Down Expand Up @@ -263,6 +314,16 @@ impl Queue {
str::from_utf8(label.to_bytes()).unwrap()
}

/// Returns the QOS class and relative priority of the given queue.
pub fn qos_class(&self) -> (QosClass, i32) {
let mut relative_priority = 0;

let qos_class =
unsafe { dispatch_queue_get_qos_class(self.ptr, &mut relative_priority) }.into();

(qos_class, relative_priority)
}

/// Submits a closure for execution on self and waits until it completes.
pub fn sync<T, F>(&self, work: F) -> T
where
Expand Down Expand Up @@ -694,9 +755,25 @@ mod tests {
let q = Queue::create("", QueueAttribute::Serial);
let mut num = 0;

q.sync(|| num = 1);
assert_eq!(num, 1);
assert_eq!(q.qos_class(), (QosClass::Unspecified, 0));

assert_eq!(q.sync(|| num), 1);
}

#[test]
fn test_serial_queue_with_qos_class() {
let q = Queue::create(
"",
QueueAttribute::Serial.with_qos_class(QosClass::UserInteractive, 0),
);
let mut num = 0;

q.sync(|| num = 1);
assert_eq!(num, 1);

assert_eq!(q.qos_class(), (QosClass::UserInteractive, 0));
assert_eq!(q.sync(|| num), 1);
}

Expand Down
48 changes: 48 additions & 0 deletions src/qos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use std::mem;

use ffi::*;

/// An abstract thread quality of service (QOS) classification.
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub enum QosClass {
/// A QOS class which indicates work performed by this thread is interactive with the user.
UserInteractive = QOS_CLASS_USER_INTERACTIVE,
/// A QOS class which indicates work performed by this thread was initiated by the user
/// and that the user is likely waiting for the results.
UserInitiated = QOS_CLASS_USER_INITIATED,
/// A default QOS class used by the system in cases where more specific QOS class information is not available.
Default = QOS_CLASS_DEFAULT,
/// A QOS class which indicates work performed by this thread may or may not be initiated by the user
/// and that the user is unlikely to be immediately waiting for the results.
Utility = QOS_CLASS_UTILITY,
/// A QOS class which indicates work performed by this thread was not initiated by the user
/// and that the user may be unaware of the results.
Background = QOS_CLASS_BACKGROUND,
/// A QOS class value which indicates the absence or removal of QOS class information.
Unspecified = QOS_CLASS_UNSPECIFIED,
}

impl Default for QosClass {
fn default() -> Self {
QosClass::Default
}
}

impl From<u32> for QosClass {
fn from(v: u32) -> Self {
unsafe { mem::transmute(v) }
}
}

impl QosClass {
/// Returns the requested QOS class of the current thread.
pub fn current() -> Self {
unsafe { qos_class_self() }.into()
}

/// Returns the initial requested QOS class of the main thread.
pub fn main() -> Self {
unsafe { qos_class_main() }.into()
}
}

0 comments on commit 5c29ee5

Please sign in to comment.