Skip to content

Commit a6b29cd

Browse files
committed
Automatically retain dispatch objects
And merge some high-level binding types with their `ffi` counterparts. This allows bindings to use nullability information, and unlocks using dispatch2 types in framework crates. Part of #77.
1 parent 169b74c commit a6b29cd

16 files changed

+272
-204
lines changed

crates/dispatch2/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2424
(e.g. `DispatchGroup`, `DispatchQueue` and `DispatchOnce`).
2525

2626
Some of the old names are kept (but deprecated) for easier migration.
27+
- Handle memory management in `ffi` module automatically.
28+
- Merged some high-level binding types with their `ffi` "_t"/"_s" counterparts.
29+
E.g. `dispatch_group_t`, `dispatch_group_s` and `DispatchGroup` are now one
30+
type.
2731

2832
### Removed
2933
- **BREAKING**: Removed `TargetQueueError` and the error case in

crates/dispatch2/src/data.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
dispatch_object!(
2+
/// Dispatch data.
3+
#[doc(alias = "dispatch_data_t")]
4+
#[doc(alias = "dispatch_data_s")]
5+
pub struct DispatchData;
6+
);
7+
8+
#[cfg(test)]
9+
mod tests {
10+
use super::*;
11+
12+
// Intentionally not Send + Sync, as `DispatchData` can contain things
13+
// like `NSData`.
14+
static_assertions::assert_not_impl_any!(DispatchData: Send, Sync);
15+
}

crates/dispatch2/src/ffi.rs

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@
33
#![allow(missing_docs, non_camel_case_types)]
44

55
use core::ffi::{c_long, c_uint, c_ulong, c_void};
6-
use core::ptr::addr_of;
76

87
#[cfg(feature = "objc2")]
98
use objc2::encode::{Encode, Encoding, RefEncode};
109

1110
// Try to generate as much as possible.
1211
pub use crate::generated::*;
12+
pub(crate) use crate::*;
13+
14+
pub(crate) type dispatch_data_s = DispatchData;
15+
pub(crate) type dispatch_queue_attr_s = DispatchQueueAttr;
16+
pub(crate) type dispatch_queue_s = DispatchQueue;
1317

1418
macro_rules! create_opaque_type {
15-
($type_name: ident, $typedef_name: ident) => {
19+
($type_name: ident) => {
1620
#[repr(C)]
1721
#[derive(Copy, Clone, Debug)]
1822
#[allow(missing_docs)]
@@ -21,9 +25,6 @@ macro_rules! create_opaque_type {
2125
_inner: [u8; 0],
2226
}
2327

24-
#[allow(missing_docs)]
25-
pub type $typedef_name = *mut $type_name;
26-
2728
#[cfg(feature = "objc2")]
2829
// SAFETY: Dispatch types are internally objects.
2930
unsafe impl RefEncode for $type_name {
@@ -66,9 +67,15 @@ macro_rules! enum_with_val {
6667
}
6768
}
6869

69-
create_opaque_type!(dispatch_object_s, dispatch_object_t);
70-
create_opaque_type!(dispatch_data_s, dispatch_data_t);
71-
create_opaque_type!(dispatch_source_type_s, dispatch_source_type_t);
70+
create_opaque_type!(dispatch_object_s);
71+
72+
#[allow(missing_docs)]
73+
pub type dispatch_object_t = *mut dispatch_object_s;
74+
75+
create_opaque_type!(dispatch_source_type_s);
76+
77+
#[allow(missing_docs)]
78+
pub type dispatch_source_type_t = *mut dispatch_source_type_s;
7279

7380
/// The prototype of functions submitted to dispatch queues.
7481
///
@@ -94,29 +101,17 @@ unsafe impl RefEncode for dispatch_time_t {
94101
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
95102
}
96103

97-
create_opaque_type!(dispatch_group_s, dispatch_group_t);
98-
create_opaque_type!(dispatch_queue_global_s, dispatch_queue_global_t);
99-
create_opaque_type!(dispatch_queue_serial_s, dispatch_queue_serial_t);
100-
create_opaque_type!(dispatch_queue_main_s, dispatch_queue_main_t);
101-
create_opaque_type!(dispatch_queue_concurrent_s, dispatch_queue_concurrent_t);
102-
create_opaque_type!(dispatch_queue_attr_s, dispatch_queue_attr_t);
103-
create_opaque_type!(dispatch_semaphore_s, dispatch_semaphore_t);
104-
create_opaque_type!(dispatch_source_s, dispatch_source_t);
105-
create_opaque_type!(dispatch_queue_s, dispatch_queue_t);
106-
create_opaque_type!(dispatch_workloop_s, dispatch_workloop_t);
107-
create_opaque_type!(dispatch_io_s, dispatch_io_t);
108-
109104
/// A dispatch queue that executes blocks serially in FIFO order.
110-
pub const DISPATCH_QUEUE_SERIAL: dispatch_queue_attr_t = core::ptr::null_mut();
105+
pub const DISPATCH_QUEUE_SERIAL: Option<&DispatchQueueAttr> = None;
111106
/// A dispatch queue that executes blocks concurrently.
112-
pub static DISPATCH_QUEUE_CONCURRENT: &dispatch_queue_attr_s = {
107+
pub static DISPATCH_QUEUE_CONCURRENT: Option<&DispatchQueueAttr> = {
113108
// Safety: immutable external definition
114-
unsafe { &_dispatch_queue_attr_concurrent }
109+
unsafe { Some(&_dispatch_queue_attr_concurrent) }
115110
};
116111

117-
pub const DISPATCH_APPLY_AUTO: dispatch_queue_t = core::ptr::null_mut();
118-
pub const DISPATCH_TARGET_QUEUE_DEFAULT: dispatch_queue_t = core::ptr::null_mut();
119-
pub const DISPATCH_CURRENT_QUEUE_LABEL: dispatch_queue_t = core::ptr::null_mut();
112+
pub const DISPATCH_APPLY_AUTO: Option<&DispatchQueue> = None;
113+
pub const DISPATCH_TARGET_QUEUE_DEFAULT: Option<&DispatchQueue> = None;
114+
pub const DISPATCH_CURRENT_QUEUE_LABEL: Option<&DispatchQueue> = None;
120115

121116
pub const DISPATCH_TIME_NOW: dispatch_time_t = dispatch_time_t(0);
122117
pub const DISPATCH_TIME_FOREVER: dispatch_time_t = dispatch_time_t(u64::MAX);
@@ -239,8 +234,8 @@ extern "C" {
239234
}
240235

241236
// Inline function in the header
242-
#[allow(unused_unsafe)] // MSRV. Also, we'd like to mark this as `const`
243-
pub extern "C" fn dispatch_get_main_queue() -> dispatch_queue_main_t {
244-
// SAFETY: Always safe to get pointer from static, only needed for MSRV.
245-
unsafe { addr_of!(_dispatch_main_q) as dispatch_queue_main_t }
237+
// TODO: Mark this as `const`
238+
pub extern "C" fn dispatch_get_main_queue() -> &'static DispatchQueue {
239+
// SAFETY: The main queue is safe to access from anywhere.
240+
unsafe { &_dispatch_main_q }
246241
}

crates/dispatch2/src/group.rs

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use alloc::boxed::Box;
22
use core::ffi::c_void;
3-
use core::ptr::NonNull;
43
use core::time::Duration;
54

65
use crate::{DispatchObject, DispatchQueue, DispatchRetained};
@@ -10,6 +9,8 @@ use super::{ffi::*, WaitError};
109

1110
dispatch_object!(
1211
/// Dispatch group.
12+
#[doc(alias = "dispatch_group_t")]
13+
#[doc(alias = "dispatch_group_s")]
1314
pub struct DispatchGroup;
1415
);
1516

@@ -18,13 +19,9 @@ dispatch_object_not_data!(unsafe DispatchGroup);
1819
impl DispatchGroup {
1920
/// Creates a new [`DispatchGroup`].
2021
pub fn new() -> Option<DispatchRetained<Self>> {
21-
// Safety: valid to call.
22-
let ptr = unsafe { dispatch_group_create() };
23-
24-
NonNull::new(ptr).map(|ptr| {
25-
// SAFETY: The object came from a "create" method.
26-
unsafe { DispatchRetained::from_raw(ptr.cast()) }
27-
})
22+
// SAFETY: Valid to call.
23+
// TODO: Properly allow NULL again (`dispatch_group_create` is incorrectly mapped).
24+
Some(unsafe { dispatch_group_create() })
2825
}
2926

3027
/// Submit a function to a [`DispatchQueue`] and associates it with the [`DispatchGroup`].
@@ -38,12 +35,7 @@ impl DispatchGroup {
3835

3936
// Safety: All parameters cannot be null.
4037
unsafe {
41-
dispatch_group_async_f(
42-
self.as_raw(),
43-
queue.as_raw(),
44-
work_boxed,
45-
function_wrapper::<F>,
46-
);
38+
dispatch_group_async_f(self, queue, work_boxed, function_wrapper::<F>);
4739
}
4840
}
4941

@@ -62,7 +54,7 @@ impl DispatchGroup {
6254
};
6355

6456
// Safety: object cannot be null and timeout is valid.
65-
let result = unsafe { dispatch_group_wait(self.as_raw(), timeout) };
57+
let result = unsafe { dispatch_group_wait(self, timeout) };
6658

6759
match result {
6860
0 => Ok(()),
@@ -79,32 +71,17 @@ impl DispatchGroup {
7971

8072
// Safety: All parameters cannot be null.
8173
unsafe {
82-
dispatch_group_notify_f(
83-
self.as_raw(),
84-
queue.as_raw(),
85-
work_boxed,
86-
function_wrapper::<F>,
87-
);
74+
dispatch_group_notify_f(self, queue, work_boxed, function_wrapper::<F>);
8875
}
8976
}
9077

9178
/// Explicitly indicates that the function has entered the [`DispatchGroup`].
9279
pub fn enter(&self) -> DispatchGroupGuard {
9380
// Safety: object cannot be null.
94-
unsafe { dispatch_group_enter(self.as_raw()) };
81+
unsafe { dispatch_group_enter(self) };
9582

9683
DispatchGroupGuard(self.retain())
9784
}
98-
99-
/// Get the raw [dispatch_group_t] value.
100-
///
101-
/// # Safety
102-
///
103-
/// - Object shouldn't be released manually.
104-
pub const unsafe fn as_raw(&self) -> dispatch_group_t {
105-
let ptr: *const Self = self;
106-
ptr as dispatch_group_t
107-
}
10885
}
10986

11087
/// Dispatch group guard.
@@ -122,6 +99,6 @@ impl DispatchGroupGuard {
12299
impl Drop for DispatchGroupGuard {
123100
fn drop(&mut self) {
124101
// SAFETY: Dispatch group cannot be null.
125-
unsafe { dispatch_group_leave(self.0.as_raw()) };
102+
unsafe { dispatch_group_leave(&self.0) };
126103
}
127104
}

crates/dispatch2/src/io.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
dispatch_object!(
2+
/// Dispatch IO.
3+
#[doc(alias = "dispatch_io_t")]
4+
#[doc(alias = "dispatch_io_s")]
5+
pub struct DispatchIO;
6+
);
7+
8+
dispatch_object_not_data!(unsafe DispatchIO);

crates/dispatch2/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,20 @@ use core::marker::{PhantomData, PhantomPinned};
4242

4343
use self::ffi::dispatch_qos_class_t;
4444

45+
mod data;
4546
pub mod ffi;
4647
#[allow(clippy::undocumented_unsafe_blocks)]
4748
mod generated;
4849
mod group;
50+
mod io;
4951
#[cfg(feature = "objc2")]
5052
mod main_thread_bound;
5153
mod object;
5254
mod once;
5355
mod queue;
5456
mod retained;
5557
mod semaphore;
58+
mod source;
5659
mod utils;
5760
mod workloop;
5861

@@ -100,16 +103,20 @@ impl From<QualityOfServiceClass> for dispatch_qos_class_t {
100103
}
101104
}
102105

106+
pub use self::data::DispatchData;
103107
pub use self::group::{DispatchGroup, DispatchGroupGuard};
108+
pub use self::io::DispatchIO;
104109
#[cfg(feature = "objc2")]
105110
pub use self::main_thread_bound::{run_on_main, MainThreadBound};
106111
pub use self::object::{DispatchObject, QualityOfServiceClassFloorError};
107112
pub use self::once::DispatchOnce;
108113
pub use self::queue::{
109-
DispatchQueue, GlobalQueueIdentifier, QueueAfterError, QueueAttribute, QueuePriority,
114+
DispatchQueue, DispatchQueueAttr, GlobalQueueIdentifier, QueueAfterError, QueueAttribute,
115+
QueuePriority,
110116
};
111117
pub use self::retained::DispatchRetained;
112118
pub use self::semaphore::{DispatchSemaphore, DispatchSemaphoreGuard};
119+
pub use self::source::DispatchSource;
113120
pub use self::workloop::{DispatchAutoReleaseFrequency, DispatchWorkloop};
114121

115122
// Helper type

crates/dispatch2/src/object.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub unsafe trait DispatchObject {
6262
#[doc(alias = "dispatch_set_target_queue")]
6363
unsafe fn set_target_queue(&self, queue: &DispatchQueue) {
6464
// SAFETY: `object` and `queue` cannot be null, rest is upheld by caller.
65-
unsafe { dispatch_set_target_queue(self.as_raw(), queue.as_raw()) };
65+
unsafe { dispatch_set_target_queue(self.as_raw(), Some(queue)) };
6666
}
6767

6868
/// Set the QOS class floor on a dispatch queue, source or workloop.
@@ -110,8 +110,7 @@ pub unsafe trait DispatchObject {
110110
}
111111

112112
#[doc(hidden)]
113-
fn as_raw(&self) -> dispatch_object_t {
114-
let ptr: *const Self = self;
115-
ptr as dispatch_object_t
113+
fn as_raw(&self) -> NonNull<dispatch_object_s> {
114+
NonNull::from(self).cast()
116115
}
117116
}

0 commit comments

Comments
 (0)