Skip to content

Commit a2232d4

Browse files
committed
Audit Dispatch functions for safety
Part of #77.
1 parent f3bc0ca commit a2232d4

File tree

10 files changed

+256
-86
lines changed

10 files changed

+256
-86
lines changed

crates/dispatch2/src/ffi.rs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -226,16 +226,3 @@ enum_with_val! {
226226
#[cfg_attr(target_vendor = "apple", link(name = "System", kind = "dylib"))]
227227
#[cfg_attr(not(target_vendor = "apple"), link(name = "dispatch", kind = "dylib"))]
228228
extern "C" {}
229-
230-
// `dispatch_main` is marked DISPATCH_NOTHROW.
231-
extern "C" {
232-
/// Executes blocks submitted to the main queue.
233-
pub fn dispatch_main() -> !;
234-
}
235-
236-
// Inline function in the header
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 }
241-
}

crates/dispatch2/src/group.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,6 @@ dispatch_object!(
1717
dispatch_object_not_data!(unsafe DispatchGroup);
1818

1919
impl DispatchGroup {
20-
/// Creates a new [`DispatchGroup`].
21-
pub fn new() -> Option<DispatchRetained<Self>> {
22-
// SAFETY: Valid to call.
23-
// TODO: Properly allow NULL again (`dispatch_group_create` is incorrectly mapped).
24-
Some(unsafe { dispatch_group_create() })
25-
}
26-
2720
/// Submit a function to a [`DispatchQueue`] and associates it with the [`DispatchGroup`].
2821
pub fn exec_async<F>(&self, queue: &DispatchQueue, work: F)
2922
where
@@ -34,7 +27,7 @@ impl DispatchGroup {
3427
let work_boxed = Box::into_raw(Box::new(work)).cast::<c_void>();
3528

3629
// Safety: All parameters cannot be null.
37-
unsafe { Self::async_f(self, queue, work_boxed, function_wrapper::<F>) };
30+
unsafe { Self::exec_async_f(self, queue, work_boxed, function_wrapper::<F>) };
3831
}
3932

4033
/// Wait synchronously for the previously submitted functions to finish.
@@ -51,8 +44,7 @@ impl DispatchGroup {
5144
DISPATCH_TIME_FOREVER
5245
};
5346

54-
// Safety: object cannot be null and timeout is valid.
55-
let result = unsafe { dispatch_group_wait(self, timeout) };
47+
let result = dispatch_group_wait(self, timeout);
5648

5749
match result {
5850
0 => Ok(()),
@@ -75,7 +67,7 @@ impl DispatchGroup {
7567

7668
/// Explicitly indicates that the function has entered the [`DispatchGroup`].
7769
pub fn enter(&self) -> DispatchGroupGuard {
78-
// Safety: object cannot be null.
70+
// SAFETY: TODO: Is it a soundness requirement that this is paired with leave?
7971
unsafe { dispatch_group_enter(self) };
8072

8173
DispatchGroupGuard(self.retain())
@@ -96,7 +88,7 @@ impl DispatchGroupGuard {
9688

9789
impl Drop for DispatchGroupGuard {
9890
fn drop(&mut self) {
99-
// SAFETY: Dispatch group cannot be null.
91+
// SAFETY: TODO: Is it a soundness requirement that this is paired with enter?
10092
unsafe { DispatchGroup::leave(&self.0) };
10193
}
10294
}

crates/dispatch2/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ pub use self::main_thread_bound::{run_on_main, MainThreadBound};
113113
pub use self::object::{DispatchObject, QualityOfServiceClassFloorError};
114114
pub use self::once::DispatchOnce;
115115
pub use self::queue::{
116-
DispatchQueue, DispatchQueueAttr, GlobalQueueIdentifier, QueueAfterError, QueueAttribute,
117-
QueuePriority,
116+
dispatch_main, DispatchQueue, DispatchQueueAttr, GlobalQueueIdentifier, QueueAfterError,
117+
QueueAttribute, QueuePriority,
118118
};
119119
pub use self::retained::DispatchRetained;
120120
pub use self::semaphore::{DispatchSemaphore, DispatchSemaphoreGuard};

crates/dispatch2/src/object.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub unsafe trait DispatchObject {
2525
///
2626
/// This extends the duration in which the object is alive by detaching it
2727
/// from the lifetime information carried by the reference.
28+
#[doc(alias = "dispatch_retain")]
2829
fn retain(&self) -> DispatchRetained<Self> {
2930
let ptr: NonNull<Self> = NonNull::from(self);
3031
// SAFETY:
@@ -94,19 +95,19 @@ pub unsafe trait DispatchObject {
9495
/// Activate the object.
9596
fn activate(&mut self) {
9697
// Safety: object cannot be null.
97-
unsafe { dispatch_activate(self.as_raw()) };
98+
dispatch_activate(self.as_raw());
9899
}
99100

100101
/// Suspend the invocation of functions on the object.
101102
fn suspend(&self) {
102103
// Safety: object cannot be null.
103-
unsafe { dispatch_suspend(self.as_raw()) };
104+
dispatch_suspend(self.as_raw());
104105
}
105106

106107
/// Resume the invocation of functions on the object.
107108
fn resume(&self) {
108109
// Safety: object cannot be null.
109-
unsafe { dispatch_resume(self.as_raw()) };
110+
dispatch_resume(self.as_raw());
110111
}
111112

112113
#[doc(hidden)]

crates/dispatch2/src/queue.rs

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -102,37 +102,40 @@ impl DispatchQueue {
102102
pub fn new(label: &str, queue_attribute: QueueAttribute) -> DispatchRetained<Self> {
103103
let label = CString::new(label).expect("Invalid label!");
104104

105-
// Safety: label and queue_attribute can only be valid.
106-
unsafe { dispatch_queue_create(label.as_ptr(), queue_attribute.into()) }
105+
// SAFETY: The label is a valid C string.
106+
unsafe { Self::__new(label.as_ptr(), queue_attribute.into()) }
107107
}
108108

109109
/// Create a new [`DispatchQueue`] with a given target [`DispatchQueue`].
110110
pub fn new_with_target(
111111
label: &str,
112112
queue_attribute: QueueAttribute,
113-
target: &DispatchQueue,
113+
target: Option<&DispatchQueue>,
114114
) -> DispatchRetained<Self> {
115115
let label = CString::new(label).expect("Invalid label!");
116116

117-
// Safety: label, queue_attribute and target can only be valid.
118-
// NOTE: dispatch_queue_create_with_target is in charge of retaining the target DispatchQueue.
119-
unsafe {
120-
dispatch_queue_create_with_target(label.as_ptr(), queue_attribute.into(), Some(target))
121-
}
117+
// SAFETY: The label is a valid C string.
118+
unsafe { Self::__new_with_target(label.as_ptr(), queue_attribute.into(), target) }
122119
}
123120

124121
/// Return a system-defined global concurrent [`DispatchQueue`] with the priority derived from [GlobalQueueIdentifier].
125122
pub fn global_queue(identifier: GlobalQueueIdentifier) -> DispatchRetained<Self> {
126123
let raw_identifier = identifier.to_identifier();
127124

128125
// Safety: raw_identifier cannot be invalid, flags is reserved.
129-
unsafe { dispatch_get_global_queue(raw_identifier, 0) }
126+
dispatch_get_global_queue(raw_identifier, 0)
130127
}
131128

132129
/// Return the main queue.
133-
pub fn main() -> DispatchRetained<Self> {
134-
// Safety: raw_identifier cannot be invalid, flags is reserved.
135-
dispatch_get_main_queue().retain()
130+
// TODO: Mark this as `const` once in MSRV.
131+
#[inline]
132+
#[doc(alias = "dispatch_get_main_queue")]
133+
pub fn main() -> &'static Self {
134+
// Inline function in the header
135+
136+
// SAFETY: The main queue is safe to access from anywhere, and is
137+
// valid forever.
138+
unsafe { &_dispatch_main_q }
136139
}
137140

138141
/// Submit a function for synchronous execution on the [`DispatchQueue`].
@@ -147,7 +150,7 @@ impl DispatchQueue {
147150
// it here.
148151
//
149152
// Safety: object cannot be null and work is wrapped to avoid ABI incompatibility.
150-
unsafe { dispatch_sync_f(self, work_boxed, function_wrapper::<F>) }
153+
unsafe { Self::exec_sync_f(self, work_boxed, function_wrapper::<F>) }
151154
}
152155

153156
/// Submit a function for asynchronous execution on the [`DispatchQueue`].
@@ -160,7 +163,7 @@ impl DispatchQueue {
160163
let work_boxed = Box::into_raw(Box::new(work)).cast();
161164

162165
// Safety: object cannot be null and work is wrapped to avoid ABI incompatibility.
163-
unsafe { dispatch_async_f(self, work_boxed, function_wrapper::<F>) }
166+
unsafe { Self::exec_async_f(self, work_boxed, function_wrapper::<F>) }
164167
}
165168

166169
/// Enqueue a function for execution at the specified time on the [`DispatchQueue`].
@@ -173,9 +176,7 @@ impl DispatchQueue {
173176
let work_boxed = Box::into_raw(Box::new(work)).cast();
174177

175178
// Safety: object cannot be null and work is wrapped to avoid ABI incompatibility.
176-
unsafe {
177-
dispatch_after_f(when, self, work_boxed, function_wrapper::<F>);
178-
}
179+
unsafe { Self::exec_after_f(when, self, work_boxed, function_wrapper::<F>) };
179180

180181
Ok(())
181182
}
@@ -190,7 +191,7 @@ impl DispatchQueue {
190191
let work_boxed = Box::into_raw(Box::new(work)).cast();
191192

192193
// Safety: object cannot be null and work is wrapped to avoid ABI incompatibility.
193-
unsafe { dispatch_barrier_async_f(self, work_boxed, function_wrapper::<F>) }
194+
unsafe { Self::barrier_async_f(self, work_boxed, function_wrapper::<F>) }
194195
}
195196

196197
/// Enqueue a barrier function for synchronous execution on the [`DispatchQueue`] and wait until that function completes.
@@ -201,7 +202,7 @@ impl DispatchQueue {
201202
let work_boxed = Box::into_raw(Box::new(work)).cast();
202203

203204
// Safety: object cannot be null and work is wrapped to avoid ABI incompatibility.
204-
unsafe { dispatch_barrier_sync_f(self, work_boxed, function_wrapper::<F>) }
205+
unsafe { Self::barrier_sync_f(self, work_boxed, function_wrapper::<F>) }
205206
}
206207

207208
/// Submit a function for synchronous execution and mark the function as a barrier for subsequent concurrent tasks.
@@ -214,7 +215,7 @@ impl DispatchQueue {
214215
let work_boxed = Box::into_raw(Box::new(work)).cast();
215216

216217
// Safety: object cannot be null and work is wrapped to avoid ABI incompatibility.
217-
unsafe { dispatch_barrier_async_and_wait_f(self, work_boxed, function_wrapper::<F>) }
218+
unsafe { Self::barrier_async_and_wait_f(self, work_boxed, function_wrapper::<F>) }
218219
}
219220

220221
/// Sets a function at the given key that will be executed at [`DispatchQueue`] destruction.
@@ -254,6 +255,18 @@ dispatch_object!(
254255

255256
dispatch_object_not_data!(unsafe DispatchQueueAttr);
256257

258+
/// Executes blocks submitted to the main queue.
259+
pub fn dispatch_main() -> ! {
260+
extern "C" {
261+
// `dispatch_main` is marked DISPATCH_NOTHROW.
262+
fn dispatch_main() -> !;
263+
}
264+
265+
// SAFETY: TODO: Must this be run on the main thread? Do we need to take
266+
// `MainThreadMarker`?
267+
unsafe { dispatch_main() }
268+
}
269+
257270
#[cfg(test)]
258271
mod tests {
259272
use super::*;

crates/dispatch2/src/semaphore.rs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,6 @@ dispatch_object!(
1616
dispatch_object_not_data!(unsafe DispatchSemaphore);
1717

1818
impl DispatchSemaphore {
19-
/// Creates a new [`DispatchSemaphore`] with an initial value.
20-
///
21-
/// Returns None if value is negative or if creation failed.
22-
pub fn new(value: isize) -> Option<DispatchRetained<Self>> {
23-
// Per documentation creating a semaphore with a negative size isn't allowed.
24-
if value < 0 {
25-
return None;
26-
}
27-
28-
// SAFETY: The value is valid.
29-
// TODO: Allow this to be NULL.
30-
Some(unsafe { dispatch_semaphore_create(value) })
31-
}
32-
3319
/// Attempt to acquire the [`DispatchSemaphore`] and return a [`DispatchSemaphoreGuard`].
3420
///
3521
/// # Errors
@@ -48,7 +34,7 @@ impl DispatchSemaphore {
4834
};
4935

5036
// Safety: DispatchSemaphore cannot be null.
51-
let result = unsafe { Self::wait(self, timeout) };
37+
let result = Self::wait(self, timeout);
5238

5339
match result {
5440
0 => Ok(DispatchSemaphoreGuard(self.retain())),
@@ -67,7 +53,7 @@ impl DispatchSemaphoreGuard {
6753
let this = ManuallyDrop::new(self);
6854

6955
// SAFETY: DispatchSemaphore cannot be null.
70-
let result = unsafe { DispatchSemaphore::signal(&this.0) };
56+
let result = this.0.signal();
7157

7258
result != 0
7359
}
@@ -76,6 +62,6 @@ impl DispatchSemaphoreGuard {
7662
impl Drop for DispatchSemaphoreGuard {
7763
fn drop(&mut self) {
7864
// SAFETY: DispatchSemaphore cannot be null.
79-
unsafe { DispatchSemaphore::signal(&self.0) };
65+
self.0.signal();
8066
}
8167
}

crates/dispatch2/src/utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ impl TryFrom<Duration> for dispatch_time_t {
1313
secs.checked_mul(1_000_000_000)
1414
.and_then(|x| x.checked_add(i64::from(value.subsec_nanos())))
1515
.map(|delta| {
16-
// Safety: delta cannot overflow
17-
unsafe { dispatch_time(DISPATCH_TIME_NOW, delta) }
16+
// delta cannot overflow
17+
dispatch_time(DISPATCH_TIME_NOW, delta)
1818
})
1919
.ok_or(())
2020
}

crates/dispatch2/src/workloop.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,10 @@ impl DispatchWorkloop {
2929

3030
/// Configure how the [`DispatchWorkloop`] manage the autorelease pools for the functions it executes.
3131
pub fn set_autorelease_frequency(&self, frequency: DispatchAutoReleaseFrequency) {
32-
// Safety: object and frequency can only be valid.
33-
unsafe {
34-
dispatch_workloop_set_autorelease_frequency(
35-
self,
36-
dispatch_autorelease_frequency_t::from(frequency),
37-
);
38-
}
32+
dispatch_workloop_set_autorelease_frequency(
33+
self,
34+
dispatch_autorelease_frequency_t::from(frequency),
35+
)
3936
}
4037
}
4138

0 commit comments

Comments
 (0)