1use std::{
7 ffi::c_void, marker::PhantomData, marker::Send as MarkerSend, marker::Sync as MarkerSync, mem,
8};
9
10use crate::{arc, define_opts, ns, objc};
11
12#[cfg(feature = "custom-allocator")]
13use crate::cf;
14
15#[derive(Debug)]
18pub struct NoEsc;
19#[derive(Debug)]
20pub struct Esc;
21#[derive(Debug)]
22pub struct Send;
23#[derive(Debug)]
24pub struct Sync;
25
26pub type NoEscBlock<F> = Block<F, NoEsc>;
29pub type EscBlock<F> = Block<F, Esc>;
30pub type SendBlock<F> = Block<F, Send>;
31pub type SyncBlock<F> = Block<F, Sync>;
32
33pub type CompletionBlock = EscBlock<fn()>;
34pub type WorkBlock<Attr = Sync> = Block<fn(), Attr>;
35
36pub type ErrCh<E = ns::Error> = EscBlock<fn(error: Option<&E>)>;
38
39pub type ResultCh<T> = EscBlock<fn(Option<&T>, Option<&ns::Error>)>;
41
42#[derive(Debug)]
43#[repr(transparent)]
44pub struct Block<Sig, Attr = NoEsc>(ns::Id, PhantomData<(Sig, Attr)>);
45
46#[derive(Debug)]
47#[repr(transparent)]
48pub struct StackBlock<'a, Closure, Sig>(Layout1Mut<'a, Closure>, PhantomData<Sig>);
49
50#[derive(Debug)]
51#[repr(transparent)]
52pub struct StaticBlock<Sig>(Layout1, PhantomData<Sig>);
53
54impl<Sig> std::ops::Deref for Block<Sig, NoEsc> {
55 type Target = ns::Id;
56
57 fn deref(&self) -> &Self::Target {
58 unsafe { std::mem::transmute(self) }
59 }
60}
61
62impl<Sig, Attr> objc::Obj for Block<Sig, Attr> {
63 #[inline]
64 unsafe fn retain(id: &Self) -> arc::R<Self> {
65 unsafe { std::mem::transmute(_Block_copy(std::mem::transmute(id))) }
66 }
67
68 #[inline]
69 unsafe fn release(id: &mut Self) {
70 unsafe { _Block_release(std::mem::transmute(id)) }
71 }
72}
73
74impl<'a, Closure, Sig> std::ops::Deref for StackBlock<'a, Closure, Sig> {
75 type Target = Block<Sig, NoEsc>;
76
77 fn deref(&self) -> &Self::Target {
78 unsafe { std::mem::transmute(self) }
79 }
80}
81
82impl<'a, Closure, Sig> std::ops::DerefMut for StackBlock<'a, Closure, Sig> {
83 fn deref_mut(&mut self) -> &mut Self::Target {
84 unsafe { std::mem::transmute(self) }
85 }
86}
87
88macro_rules! call {
89 ($($a:ident: $t:ident),*) => {
90 impl<$($t,)* R, Attr> Block<fn($($t,)*) -> R, Attr> {
91 pub fn call(&mut self, $($a: $t),*) -> R {
92 let layout: &Layout1 = unsafe { std::mem::transmute(&self.0) };
93 let f: extern "C" fn(literal: &mut Self $(, $t)*) -> R = unsafe { std::mem::transmute(layout.invoke) };
94 f(self $(, $a)*)
95 }
96 }
97 };
98}
99
100macro_rules! invoke {
101 ($name:ident: $($a:ident: $t:ident),*) => {
102 extern "C" fn $name<$($t,)* R>(&mut self, $($a: $t),*) -> R
103 where
104 Closure: FnMut($($t,)*) -> R,
105 {
106 (self.closure)($($a,)*)
107 }
108 };
109}
110
111macro_rules! new {
112 ($name:ident, $invoke:ident: $($t:ident),* $(+ $l:lifetime)* $(+ $trait:ident)*) => {
113 pub fn $name<$($t,)* R, Closure>(closure: Closure) -> arc::R<Self>
114 where
115 Sig: Fn($($t,)*) -> R, for<'c> Closure: FnMut($($t,)*) -> R $(+ $l)* $(+ $trait)*,
117 {
118 let res = Layout2Mut::new(Layout2Mut::<Closure>::$invoke as _, closure);
119 unsafe { std::mem::transmute(res) }
120 }
121 };
122}
123
124macro_rules! with_fn {
125 ($name:ident: $($t:ident),* ) => {
126 pub const fn $name<$($t,)* R>(func: extern "C" fn (*const c_void, $($t,)*) -> R) -> StaticBlock<Sig>
127 {
128 let res = Layout1::with(func as _);
129 StaticBlock(res, PhantomData)
130 }
131 };
132}
133
134macro_rules! stack {
135 ($name:ident, $invoke:ident: $($t:ident),*) => {
136 #[inline]
137 pub const unsafe fn $name<$($t,)* R, Closure>(closure: &mut Closure) -> StackBlock<'_, Closure, Sig>
138 where
139 Sig: Fn($($t,)*) -> R, for<'c> Closure: FnMut($($t,)*) -> R
141 {
142 let layout = Layout1Mut::new(Layout1Mut::<Closure>::$invoke as _, closure);
143 StackBlock(layout, PhantomData)
144 }
145 };
146}
147
148impl<Sig, Attr> Block<Sig, Attr> {
149 with_fn!(with_fn0:);
150 with_fn!(with_fn1: A);
151 with_fn!(with_fn2: A, B);
152 with_fn!(with_fn3: A, B, C);
153 with_fn!(with_fn4: A, B, C, D);
154 with_fn!(with_fn5: A, B, C, D, E);
155 with_fn!(with_fn6: A, B, C, D, E, F);
156}
157
158impl<Sig> Block<Sig, NoEsc> {
159 stack!(stack0, invoke0:);
160 stack!(stack1, invoke1: A);
161 stack!(stack2, invoke2: A, B);
162 stack!(stack3, invoke3: A, B, C);
163 stack!(stack4, invoke4: A, B, C, D);
164 stack!(stack5, invoke5: A, B, C, D, E);
165 stack!(stack6, invoke6: A, B, C, D, E, F);
166
167 new!(new0, invoke0:);
168 new!(new1, invoke1: A);
169 new!(new2, invoke2: A, B);
170 new!(new3, invoke3: A, B, C);
171 new!(new4, invoke4: A, B, C, D);
172 new!(new5, invoke5: A, B, C, D, E);
173 new!(new6, invoke6: A, B, C, D, E, F);
174}
175
176impl<Sig> Block<Sig, Esc> {
177 new!(new0, invoke0: + 'static);
178 new!(new1, invoke1: A + 'static);
179 new!(new2, invoke2: A, B + 'static);
180 new!(new3, invoke3: A, B, C + 'static);
181 new!(new4, invoke4: A, B, C, D + 'static);
182 new!(new5, invoke5: A, B, C, D, E + 'static);
183 new!(new6, invoke6: A, B, C, D, E, F + 'static);
184
185 pub fn as_noesc_mut(&mut self) -> &mut Block<Sig, NoEsc> {
186 unsafe { std::mem::transmute(self) }
187 }
188}
189
190impl<Sig> Block<Sig, Send> {
191 new!(new0, invoke0: + 'static + MarkerSend);
192 new!(new1, invoke1: A + 'static + MarkerSend);
193 new!(new2, invoke2: A, B + 'static + MarkerSend);
194 new!(new3, invoke3: A, B, C + 'static + MarkerSend);
195 new!(new4, invoke4: A, B, C, D + 'static + MarkerSend);
196 new!(new5, invoke5: A, B, C, D, E + 'static + MarkerSend);
197 new!(new6, invoke6: A, B, C, D, E, F + 'static + MarkerSend);
198
199 pub fn as_esc_mut(&mut self) -> &mut Block<Sig, Esc> {
200 unsafe { std::mem::transmute(self) }
201 }
202
203 pub fn as_noesc_mut(&mut self) -> &mut Block<Sig, NoEsc> {
204 unsafe { std::mem::transmute(self) }
205 }
206}
207
208impl<Sig> Block<Sig, Sync> {
209 new!(new0, invoke0: + 'static + MarkerSync);
210 new!(new1, invoke1: A + 'static + MarkerSync);
211 new!(new2, invoke2: A, B + 'static + MarkerSync);
212 new!(new3, invoke3: A, B, C + 'static + MarkerSync);
213 new!(new4, invoke4: A, B, C, D + 'static + MarkerSync);
214 new!(new5, invoke5: A, B, C, D, E + 'static + MarkerSync);
215 new!(new6, invoke6: A, B, C, D, E, F + 'static + MarkerSync);
216
217 pub fn as_send_mut(&mut self) -> &mut Block<Sig, Send> {
218 unsafe { std::mem::transmute(self) }
219 }
220
221 pub fn as_esc_mut(&mut self) -> &mut Block<Sig, Esc> {
222 unsafe { std::mem::transmute(self) }
223 }
224
225 pub fn as_noesc_mut(&mut self) -> &mut Block<Sig, NoEsc> {
226 unsafe { std::mem::transmute(self) }
227 }
228}
229
230impl<Sig> StaticBlock<Sig> {
231 with_fn!(new0:);
232 with_fn!(new1: A);
233 with_fn!(new2: A, B);
234 with_fn!(new3: A, B, C);
235 with_fn!(new4: A, B, C, D);
236 with_fn!(new5: A, B, C, D, E);
237 with_fn!(new6: A, B, C, D, E, F);
238
239 pub fn as_sync_mut(&mut self) -> &mut Block<Sig, Sync> {
240 unsafe { std::mem::transmute(self) }
241 }
242
243 pub fn as_send_mut(&mut self) -> &mut Block<Sig, Send> {
244 unsafe { std::mem::transmute(self) }
245 }
246
247 pub fn as_esc_mut(&mut self) -> &mut Block<Sig, Esc> {
248 unsafe { std::mem::transmute(self) }
249 }
250
251 pub fn as_noesc_mut(&mut self) -> &mut Block<Sig, NoEsc> {
252 unsafe { std::mem::transmute(self) }
253 }
254}
255
256call!();
257call!(a:A);
258call!(a:A, b: B);
259call!(a:A, b: B, c: C);
260call!(a:A, b: B, c: C, d: D);
261call!(a:A, b: B, c: C, d: D, e: E);
262call!(a:A, b: B, c: C, d: D, e: E, f: F);
263call!(a:A, b: B, c: C, d: D, e: E, f: F, g: G);
264call!(a:A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
265call!(a:A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
266call!(a:A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
267call!(a:A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
268
269impl<A, Attr> Block<fn(a: Option<&A>), Attr> {
270 #[inline]
271 pub fn handle(&mut self, a: Option<&A>) {
272 let layout: &Layout1 = unsafe { std::mem::transmute(&self.0) };
273 let f: extern "C" fn(literal: &mut Self, a: Option<&A>) =
274 unsafe { std::mem::transmute(layout.invoke) };
275 f(self, a)
276 }
277}
278
279impl<A, B, Attr> Block<fn(a: Option<&A>, b: Option<&B>), Attr> {
280 #[inline]
281 pub fn handle(&mut self, a: Option<&A>, b: Option<&B>) {
282 let layout: &Layout1 = unsafe { std::mem::transmute(&self.0) };
283 let f: extern "C" fn(literal: &mut Self, a: Option<&A>, b: Option<&B>) =
284 unsafe { std::mem::transmute(layout.invoke) };
285 f(self, a, b)
286 }
287}
288
289define_opts!(pub Flags(i32));
294
295impl Flags {
296 pub const NONE: Self = Self(0);
297
298 pub const DEALLOCATING: Self = Self(1);
300
301 pub const REFCOUNT_MASK: Self = Self(0xfffei32);
303
304 pub const NEEDS_FREE: Self = Self(1 << 24);
315 pub const HAS_COPY_DISPOSE: Self = Self(1 << 25);
317 const RETAINED_NEEDS_FREE: Self = Self(2 | Self::NEEDS_FREE.0);
325 const RETAINED_NEEDS_DROP: Self = Self(2 | Self::NEEDS_FREE.0 | Self::HAS_COPY_DISPOSE.0);
326}
327
328#[derive(Debug)]
329#[repr(C)]
330pub struct Desc1 {
331 reserved: usize,
332 size: usize,
333}
334
335#[derive(Debug)]
336#[repr(C)]
337pub struct Desc2<T: Sized> {
338 descriptor1: Desc1,
339 copy: extern "C" fn(dest: *mut c_void, src: *mut c_void),
340 dispose: extern "C" fn(literal: &mut T),
341}
342
343#[derive(Debug)]
344#[repr(C)]
345pub struct Layout1 {
346 isa: &'static objc::Class<ns::Id>,
347 flags: Flags,
348 reserved: i32,
349 invoke: *const c_void,
350 descriptor: &'static Desc1,
351}
352
353#[derive(Debug)]
354#[repr(C)]
355pub struct Layout1Mut<'a, Closure> {
356 isa: &'static objc::Class<ns::Id>,
357 flags: Flags,
358 reserved: i32,
359 invoke: *const c_void,
360 descriptor: &'a Desc1,
361 closure: &'a mut Closure,
362}
363
364#[derive(Debug)]
365#[repr(C)]
366struct Layout2Mut<'a, F: Sized + 'a> {
367 isa: &'static objc::Class<ns::Id>,
368 flags: Flags,
369 reserved: i32,
370 invoke: *const c_void,
371 descriptor: &'a Desc2<Self>,
372 closure: mem::ManuallyDrop<F>,
373}
374
375impl Layout1 {
376 const DESCRIPTOR: Desc1 = Desc1 {
377 reserved: 0,
378 size: std::mem::size_of::<Self>(),
379 };
380
381 pub const fn with(invoke: *const c_void) -> Self {
382 Self {
383 isa: unsafe { &_NSConcreteStackBlock },
384 flags: Flags::NONE,
385 reserved: 0,
386 invoke,
387 descriptor: &Self::DESCRIPTOR,
388 }
389 }
390}
391
392impl<'a, Closure> Layout1Mut<'a, Closure> {
393 const DESCRIPTOR_1: Desc1 = Desc1 {
394 reserved: 0,
395 size: std::mem::size_of::<&'static objc::Class<ns::Id>>()
396 + std::mem::size_of::<Flags>()
397 + std::mem::size_of::<i32>()
398 + std::mem::size_of::<*const c_void>()
399 + std::mem::size_of::<&'static Desc1>()
400 + std::mem::size_of::<&'static c_void>(), };
402
403 invoke! {invoke0: }
404 invoke! {invoke1: a: A}
405 invoke! {invoke2: a: A, b: B}
406 invoke! {invoke3: a: A, b: B, c: C}
407 invoke! {invoke4: a: A, b: B, c: C, d: D}
408 invoke! {invoke5: a: A, b: B, c: C, d: D, e: E}
409 invoke! {invoke6: a: A, b: B, c: C, d: D, e: E, f: F}
410
411 const fn new(invoke: *const c_void, f: &'a mut Closure) -> Self {
412 Self {
413 isa: unsafe { &_NSConcreteStackBlock },
414 flags: Flags::NONE,
415 reserved: 0,
416 invoke,
417 descriptor: &Self::DESCRIPTOR_1,
418 closure: f,
419 }
420 }
421}
422
423extern "C" fn no_copy(_dest: *mut c_void, _src: *mut c_void) {
424 panic!("copy should not be called");
425}
426
427impl<'a, Closure: 'a + Sized> Layout2Mut<'a, Closure> {
428 const DESCRIPTOR_2: Desc2<Self> = Desc2 {
429 descriptor1: Desc1 {
430 reserved: 0,
431 size: std::mem::size_of::<Self>(),
432 },
433 copy: no_copy,
434 dispose: Self::dispose,
435 };
436
437 extern "C" fn dispose(block: &mut Self) {
438 debug_assert!(mem::needs_drop::<Closure>());
439 unsafe {
440 mem::ManuallyDrop::drop(&mut block.closure);
441 }
442 }
443
444 invoke! {invoke0: }
445 invoke! {invoke1: a: A}
446 invoke! {invoke2: a: A, b: B}
447 invoke! {invoke3: a: A, b: B, c: C}
448 invoke! {invoke4: a: A, b: B, c: C, d: D}
449 invoke! {invoke5: a: A, b: B, c: C, d: D, e: E}
450 invoke! {invoke6: a: A, b: B, c: C, d: D, e: E, f: F}
451
452 fn new(invoke: *const c_void, closure: Closure) -> &'a mut Self {
453 let flags = if mem::needs_drop::<Closure>() {
454 Flags::RETAINED_NEEDS_DROP
455 } else {
456 Flags::RETAINED_NEEDS_FREE
457 };
458
459 #[cfg(not(feature = "custom-allocator"))]
460 {
461 let block = Box::new(Self {
465 isa: unsafe { &_NSConcreteMallocBlock },
466 flags,
467 reserved: 0,
468 invoke,
469 descriptor: &Self::DESCRIPTOR_2,
470 closure: mem::ManuallyDrop::new(closure),
471 });
472 Box::leak(block)
473 }
474 #[cfg(feature = "custom-allocator")]
475 {
476 let block = Self {
483 isa: unsafe { &_NSConcreteMallocBlock },
484 flags,
485 reserved: 0,
486 invoke,
487 descriptor: &Self::DESCRIPTOR_2,
488 closure: mem::ManuallyDrop::new(closure),
489 };
490
491 let layout = std::alloc::Layout::new::<Self>();
492
493 unsafe {
494 let ptr = cf::Allocator::allocate_size(layout.size());
495 *(ptr as *mut Self) = block;
496 std::mem::transmute(ptr)
497 }
498 }
499 }
500}
501
502#[link(name = "System", kind = "dylib")]
503unsafe extern "C-unwind" {
504 static _NSConcreteStackBlock: objc::Class<ns::Id>;
506 static _NSConcreteMallocBlock: objc::Class<ns::Id>;
507
508 fn _Block_copy(block: *const c_void) -> *const c_void;
509 fn _Block_release(block: *const c_void);
510}
511
512#[cfg(test)]
513mod tests {
514
515 use crate::{blocks, dispatch};
516
517 #[derive(Debug)]
518 struct Foo;
519
520 impl Drop for Foo {
521 fn drop(&mut self) {
522 println!("dropped foo");
523 }
524 }
525
526 #[test]
527 fn simple_block() {
528 let foo = Foo;
529 let mut b = dispatch::Block::<blocks::Send>::new0(move || println!("nice {foo:?}"));
531
532 let q = dispatch::Queue::new();
533 q.async_b(&mut b);
534 q.async_b(&mut b);
535 q.async_mut(|| println!("nice"));
536 q.sync_mut(|| println!("fuck"));
537 println!("finished");
540 }
541}
542
543#[cfg(feature = "async")]
544use parking_lot::Mutex;
545
546#[cfg(feature = "async")]
547use std::sync::Arc;
548
549#[cfg(feature = "async")]
550pub(crate) struct Shared<T> {
551 ready: Option<T>,
552 pending: Option<std::task::Waker>,
553}
554
555#[cfg(feature = "async")]
556impl<T> Shared<T> {
557 pub(crate) fn new() -> Arc<Mutex<Self>> {
558 Arc::new(Mutex::new(Self {
559 ready: None,
560 pending: None,
561 }))
562 }
563
564 pub fn ready(&mut self, result: T) {
565 self.ready = Some(result);
566
567 if let Some(waker) = self.pending.take() {
568 waker.wake();
569 }
570 }
571}
572
573#[cfg(feature = "async")]
574pub struct Completion<R>(pub(crate) Arc<Mutex<Shared<R>>>);
575
576#[cfg(feature = "async")]
577impl<T> std::future::Future for Completion<T> {
578 type Output = T;
579
580 fn poll(
581 self: std::pin::Pin<&mut Self>,
582 cx: &mut std::task::Context<'_>,
583 ) -> std::task::Poll<Self::Output> {
584 let mut lock = self.0.lock();
585
586 if let Some(r) = lock.ready.take() {
587 std::task::Poll::Ready(r)
588 } else {
589 lock.pending = Some(cx.waker().clone());
590 std::task::Poll::Pending
591 }
592 }
593}
594
595#[cfg(feature = "async")]
596pub fn comp0() -> (Completion<()>, arc::R<CompletionBlock>) {
597 let shared = Shared::new();
598 (
599 Completion(shared.clone()),
600 CompletionBlock::new0(move || shared.lock().ready(())),
601 )
602}
603
604#[cfg(feature = "async")]
605pub fn comp1<R: std::marker::Send>() -> (Completion<R>, arc::R<Block<fn(R), Send>>) {
606 let shared = Shared::new();
607 (
608 Completion(shared.clone()),
609 SendBlock::new1(move |v: R| shared.lock().ready(v)),
610 )
611}
612
613#[cfg(feature = "async")]
614pub fn retained1<R: arc::Retain + std::marker::Send>()
615-> (Completion<arc::R<R>>, arc::R<Block<fn(&R), Send>>) {
616 let shared = Shared::new();
617 (
618 Completion(shared.clone()),
619 SendBlock::new1(move |v: &R| shared.lock().ready(v.retained())),
620 )
621}
622
623#[cfg(feature = "async")]
624pub fn ok<'a>() -> (Completion<Result<(), arc::R<ns::Error>>>, arc::R<ErrCh>) {
625 let shared = Shared::new();
626 (
627 Completion(shared.clone()),
628 ErrCh::new1(move |error: Option<&ns::Error>| {
629 shared.lock().ready(match error {
630 None => Ok(()),
631 Some(err) => Err(err.retained()),
632 });
633 }),
634 )
635}
636
637#[cfg(feature = "async")]
638pub fn result<T: arc::Retain + std::marker::Send>() -> (
639 Completion<Result<arc::R<T>, arc::R<ns::Error>>>,
640 arc::R<ResultCh<T>>,
641) {
642 let shared = Shared::new();
643 (
644 Completion(shared.clone()),
645 ResultCh::<T>::new2(move |value: Option<&T>, error: Option<&ns::Error>| {
646 let res = match error {
647 None => Ok(unsafe { value.unwrap_unchecked().retained() }),
648 Some(err) => Err(err.retained()),
649 };
650
651 shared.lock().ready(res);
652 }),
653 )
654}