Skip to content

Commit 252f843

Browse files
committed
Experiment with using Retained
1 parent 41b5cd9 commit 252f843

File tree

9 files changed

+565
-438
lines changed

9 files changed

+565
-438
lines changed

crates/dispatch2/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ targets = [
3737
]
3838

3939
[features]
40-
default = ["std"]
40+
default = ["std", "objc2"]
4141

4242
# Currently not possible to turn off, put here for forwards compatibility.
4343
std = ["alloc", "bitflags/std", "block2?/std", "libc?/std", "objc2?/std"]

crates/dispatch2/src/ffi.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,33 @@
22
33
#![allow(missing_docs, non_camel_case_types)]
44

5-
use core::ffi::{c_long, c_uint, c_ulong, c_void};
6-
use std::ptr::addr_of;
5+
use std::{
6+
ffi::{c_long, c_uint, c_ulong, c_void},
7+
ptr::addr_of,
8+
};
79

810
#[cfg(feature = "objc2")]
911
use objc2::encode::{Encode, Encoding, RefEncode};
12+
use objc2::runtime::{NSObjectProtocol, ProtocolObject};
1013

1114
// Try to generate as much as possible.
1215
pub use crate::generated::*;
1316

1417
macro_rules! create_opaque_type {
1518
($type_name: ident, $typedef_name: ident) => {
16-
#[repr(C)]
17-
#[derive(Copy, Clone, Debug)]
1819
#[allow(missing_docs)]
19-
pub struct $type_name {
20-
/// opaque value
21-
_inner: [u8; 0],
22-
}
20+
pub type $type_name = ProtocolObject<dyn NSObjectProtocol>;
2321

2422
#[allow(missing_docs)]
2523
pub type $typedef_name = *mut $type_name;
2624

25+
/*
2726
#[cfg(feature = "objc2")]
2827
// SAFETY: Dispatch types are internally objects.
2928
unsafe impl RefEncode for $type_name {
3029
const ENCODING_REF: Encoding = Encoding::Object;
3130
}
31+
*/
3232
};
3333
}
3434

@@ -108,10 +108,11 @@ create_opaque_type!(dispatch_io_s, dispatch_io_t);
108108

109109
/// A dispatch queue that executes blocks serially in FIFO order.
110110
pub const DISPATCH_QUEUE_SERIAL: dispatch_queue_attr_t = core::ptr::null_mut();
111+
111112
/// A dispatch queue that executes blocks concurrently.
112-
pub static DISPATCH_QUEUE_CONCURRENT: &dispatch_queue_attr_s = {
113+
pub const DISPATCH_QUEUE_CONCURRENT: dispatch_queue_attr_t = {
113114
// Safety: immutable external definition
114-
unsafe { &_dispatch_queue_attr_concurrent }
115+
unsafe { &_dispatch_queue_attr_concurrent as *const _ as dispatch_queue_attr_t }
115116
};
116117

117118
pub const DISPATCH_APPLY_AUTO: dispatch_queue_t = core::ptr::null_mut();

crates/dispatch2/src/group.rs

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,27 @@
11
//! Dispatch group definition.
22
3-
use std::time::Duration;
3+
use std::{ffi::c_void, time::Duration};
44

5-
use core::ffi::c_void;
5+
use objc2::rc::Retained;
66

7-
use super::object::DispatchObject;
8-
use super::queue::Queue;
9-
use super::utils::function_wrapper;
10-
use super::{ffi::*, WaitError};
7+
use super::{ffi::*, function_wrapper, queue::Queue, AsRawDispatchObject, WaitError};
118

129
/// Dispatch group.
13-
#[derive(Debug, Clone)]
10+
#[derive(Debug)]
1411
pub struct Group {
15-
dispatch_object: DispatchObject<dispatch_group_s>,
12+
inner: Retained<dispatch_group_s>,
1613
}
1714

18-
/// Dispatch group guard.
19-
#[derive(Debug)]
20-
pub struct GroupGuard(Group, bool);
21-
2215
impl Group {
2316
/// Creates a new [Group].
2417
pub fn new() -> Option<Self> {
2518
// Safety: valid to call.
2619
let object = unsafe { dispatch_group_create() };
2720

28-
if object.is_null() {
29-
return None;
30-
}
21+
// Safety: retained accepts null pointer.
22+
let inner = unsafe { Retained::from_raw(object)? };
3123

32-
// Safety: object cannot be null.
33-
let dispatch_object = unsafe { DispatchObject::new_owned(object.cast()) };
34-
35-
Some(Group { dispatch_object })
24+
Some(Group { inner })
3625
}
3726

3827
/// Submit a function to a [Queue] and associates it with the [Group].
@@ -104,25 +93,44 @@ impl Group {
10493
GroupGuard(self.clone(), false)
10594
}
10695

107-
/// Set the finalizer function for the object.
108-
pub fn set_finalizer<F>(&mut self, destructor: F)
109-
where
110-
F: Send + FnOnce(),
111-
{
112-
self.dispatch_object.set_finalizer(destructor);
113-
}
114-
11596
/// Get the raw [dispatch_group_t] value.
11697
///
11798
/// # Safety
11899
///
119100
/// - Object shouldn't be released manually.
120-
pub const unsafe fn as_raw(&self) -> dispatch_group_t {
121-
// SAFETY: Upheld by caller
122-
unsafe { self.dispatch_object.as_raw() }
101+
pub fn as_raw(&self) -> dispatch_group_t {
102+
// Safety: Upheld by caller
103+
Retained::as_ptr(&self.inner).cast_mut()
123104
}
124105
}
125106

107+
impl Clone for Group {
108+
fn clone(&self) -> Self {
109+
Self {
110+
// Safety: pointer must be valid.
111+
inner: unsafe {
112+
Retained::retain(self.as_raw()).expect("failed to retain dispatch_group")
113+
},
114+
}
115+
}
116+
}
117+
118+
impl AsRawDispatchObject for Group {
119+
fn as_raw_object(&self) -> dispatch_object_t {
120+
self.as_raw()
121+
}
122+
}
123+
124+
// Safety: group is inherently safe to move between threads.
125+
unsafe impl Send for Group {}
126+
127+
// Safety: group is inherently safe to share between threads.
128+
unsafe impl Sync for Group {}
129+
130+
/// Dispatch group guard.
131+
#[derive(Debug)]
132+
pub struct GroupGuard(Group, bool);
133+
126134
impl GroupGuard {
127135
/// Explicitly indicates that the function in the [Group] finished executing.
128136
pub fn leave(mut self) {

crates/dispatch2/src/lib.rs

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,24 @@
2424
// Update in Cargo.toml as well.
2525
#![doc(html_root_url = "https://docs.rs/dispatch2/0.1.0")]
2626

27-
use self::ffi::dispatch_qos_class_t;
28-
2927
pub mod ffi;
3028
#[allow(clippy::undocumented_unsafe_blocks)]
3129
mod generated;
3230
pub mod group;
3331
pub mod object;
3432
pub mod queue;
3533
pub mod semaphore;
36-
mod utils;
34+
pub mod workloop;
35+
36+
pub use group::*;
37+
pub use object::*;
38+
pub use queue::*;
39+
pub use semaphore::*;
40+
pub use workloop::*;
41+
42+
use std::{ffi::c_void, time::Duration};
43+
44+
use ffi::{dispatch_qos_class_t, dispatch_time, dispatch_time_t, DISPATCH_TIME_NOW};
3745

3846
/// Wait error.
3947
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
@@ -46,14 +54,15 @@ pub enum WaitError {
4654
}
4755

4856
/// Quality of service that specify the priorities for executing tasks.
49-
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
57+
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
5058
#[non_exhaustive]
5159
pub enum QualityOfServiceClass {
5260
/// Quality of service for user-interactive tasks.
5361
UserInteractive,
5462
/// Quality of service for tasks that prevent the user from actively using your app.
5563
UserInitiated,
5664
/// Default Quality of service.
65+
#[default]
5766
Default,
5867
/// Quality of service for tasks that the user does not track actively.
5968
Utility,
@@ -79,7 +88,44 @@ impl From<QualityOfServiceClass> for dispatch_qos_class_t {
7988
}
8089
}
8190

82-
pub use group::*;
83-
pub use object::*;
84-
pub use queue::*;
85-
pub use semaphore::*;
91+
impl TryFrom<Duration> for dispatch_time_t {
92+
type Error = TryFromDurationError;
93+
94+
fn try_from(value: Duration) -> Result<Self, Self::Error> {
95+
let secs = value.as_secs() as i64;
96+
97+
secs.checked_mul(1_000_000_000)
98+
.and_then(|x| x.checked_add(i64::from(value.subsec_nanos())))
99+
.map(|delta| {
100+
// Safety: delta cannot overflow
101+
unsafe { dispatch_time(DISPATCH_TIME_NOW, delta) }
102+
})
103+
.ok_or(Self::Error::TimeOverflow)
104+
}
105+
}
106+
107+
/// Error returned by [Queue::after].
108+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
109+
#[non_exhaustive]
110+
pub enum TryFromDurationError {
111+
/// The given timeout value will result in an overflow when converting to dispatch time.
112+
TimeOverflow,
113+
}
114+
115+
/// Error returned by [Queue::set_qos_class_floor] or [WorkloopQueue::set_qos_class_floor].
116+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
117+
#[non_exhaustive]
118+
pub enum QualityOfServiceClassFloorError {
119+
/// The relative priority is invalid.
120+
InvalidRelativePriority,
121+
}
122+
123+
pub(crate) extern "C" fn function_wrapper<F>(work_boxed: *mut c_void)
124+
where
125+
F: FnOnce(),
126+
{
127+
// Safety: we reconstruct from a Box.
128+
let work = unsafe { Box::from_raw(work_boxed.cast::<F>()) };
129+
130+
(*work)();
131+
}

0 commit comments

Comments
 (0)