-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make Queue sendable #681
base: master
Are you sure you want to change the base?
Make Queue sendable #681
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, it is defined as DISPATCH_DECL(dispatch_queue)
, and DISPATCH_DECL
is defined here as OS_OBJECT_DECL_SUBCLASS_SWIFT(name, dispatch_object)
, i.e. clearly sendable.
Could you mark it as Sync
too, as well as mark the other things that originate in DISPATCH_DECL
?
15cf6cd
to
60eee69
Compare
Sure thing. Since many types here hold Note that since |
3cf2017
to
ed62125
Compare
Ah, but // swift -swift-version 6 foo.swift
import Dispatch
import AppKit
let queue = DispatchQueue(label: "foo")
let queue2: Sendable = queue // DispatchQueue is sendable
let obj: DispatchObject = queue
let obj2: Sendable = obj // But DispatchObject is not |
You’re right my bad. It’s a bit tricky though that all of the dispatch types in dispatch2 resort to wrapping their raw pointer in This reminds me of To be frank I thought this was simpler but a proper solution probably requires a rethink of this system. I lean towards Does any of this make sense to you? |
I am thinking of using something akin to |
9460f81
to
417db62
Compare
This turned a bit into experiment to refactor all that + use Also defining objects as |
236221f
to
252f843
Compare
Yeah, I've been pondering this too, but I think the "proper" dependency chain has to be that Objective-C support in And yeah, that does require Looking at Clang's source code, this is similar to what they do, they have a distinction between three different types of retained pointers:
I'll try to see if I can get the type to implement it myself during the holidays, but... There's a few things that I'm hoping to get done there too, so might not make it. |
I seem to remember you leaving a comment here about investigating in Hopper that In any case, I don't think that's true, I've investigated the performance a bit now, my very non-scientific results:
Benchmarking code
use criterion::{criterion_group, criterion_main, Criterion};
pub fn criterion_benchmark(c: &mut Criterion) {
let obj = dispatch2::Queue::new("abc", dispatch2::QueueAttribute::Concurrent);
let mut group = c.benchmark_group("retain/release");
group.bench_function("dispatch", |b| {
b.iter(|| {
unsafe { dispatch2::ffi::dispatch_retain(obj.as_raw().cast()) };
unsafe { dispatch2::ffi::dispatch_release(obj.as_raw().cast()) };
})
});
group.bench_function("objc", |b| {
b.iter(|| {
unsafe { objc2::ffi::objc_retain(obj.as_raw().cast()) };
unsafe { objc2::ffi::objc_release(obj.as_raw().cast()) };
})
});
group.bench_function("objc chained", |b| {
b.iter(|| {
unsafe { objc2::ffi::objc_release(objc2::ffi::objc_retain(obj.as_raw().cast())) };
})
});
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches); I.e. This matches my intuition from reading the source code too, which basically boils down to:
My claim about startup time is false though, |
Nice performance testing!
My memory failed me. I had a fresh look at it this morning and the call to So that means that a custom
So I assume such conversion should maintain the performance guarantee and instead of adopting the object pointer, it should actually wrap the more performant implementation of Also similar memory management exists in network framework ( Could be handy for |
Thanks! Not something I'm too familiar with, so nice to get some validation ;). Also, just for completeness I'll note that these amounts are really small in any case, and in real-world use-cases likely negligible (Swift doesn't care about the distinction, for example).
This design has been explored a bit in I find it very nice from a technical perspective, but then again, I think it leads to a design that has more indirection, and is more difficult for users to understand:
I think abstraction is a good idea when there really is a general pattern to abstract over. But in this case there is a fairly limited set of different
I'd rather make the default case fast, i.e. use and return (Also, let's say that you take Thanks for the input here so far btw, it forces me to write down my reasoning for some decisions that I've made implicitly in my head. |
d76b73f
to
127b993
Compare
That's why I stuck with
Exactly.
These are good points. But shouldn't these operations like autorelease work flawlessly with os_objects given that they are objc types themselves? So then it's up to runtime to figure out whether it can take a short cut or take a detour over objc2 message.
That might work but perhaps not all that important right now.
Yeah in my latest effort I copy-catted it as
Yeah. It's a difficult to guarantee consistency when the API allows passing raw pointers. Perhaps we should not focus on that as objc messages work universally for these kinds of objects. |
Quick question: Why do we need to define dispatch objects with #[repr(C)]
#[derive(Copy, Clone, Debug)]
#[allow(missing_docs)]
pub struct $type_name {
- /// opaque value
- _inner: [u8; 0],
} |
Two reasons:
It might take me a few days to review your PR btw (holidays), but the approach overall looks good, though in the end I'd probably like it split out into separate PRs (one for the removal of |
Didn't think of that. That's undesired.
No rush.
I took a bit of time and looked into returning Also I am not entirely sure |
After poking around I figured how to cast between transparent struct and pointer. I assume this is the end result that you wanted to see from the beginning. |
I somewhat gave up on trying to avoid a retain or release of global queues. I think that those calls are no-op anyway so perhaps it doesn't matter. |
Hey, checking back to say that I haven't forgotten this PR, have just been making a few too many promises that I can't uphold, and I'm really behind on the next release (which I've promised in more places, so I'm trying to focus on that one), sorry about the continued delay :/ |
Having
Queue
in async contexts is currently problematic. I think it's fair to say thatQueue
can be moved between threads and we should should mark itSend
to make it easier to use it.