1 // Copyright 2018 Lyndon Brown
2 //
3 // This file is part of the PulseAudio Rust language binding.
4 //
5 // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not
6 // copy, modify, or distribute this file except in compliance with said license. You can find copies
7 // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at
8 // <http://opensource.org/licenses/MIT> and <http://www.apache.org/licenses/LICENSE-2.0>
9 // respectively.
10 //
11 // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a
12 // fair-use basis, as discussed in the overall project readme (available in the git repository).
13 
14 //! Callback handling.
15 
16 use std::os::raw::c_void;
17 use std::ptr::null_mut;
18 use std::marker::PhantomData;
19 use std::mem::ManuallyDrop;
20 
21 /// List result instance.
22 ///
23 /// Fetching a list can result in a callback being fired for each list item, and then once more to
24 /// signal the end of the list having been reached. This type is used to distinguish such state to a
25 /// closure callback.
26 #[derive(Debug)]
27 pub enum ListResult<T> {
28     /// List item
29     Item(T),
30     /// End of list reached
31     End,
32     /// Failure, an error occurred
33     Error,
34 }
35 
36 /// A saved multi-use callback.
37 ///
38 /// Closures of multi-use callbacks (those that may be called multiple times) need saving, and
39 /// releasing later at an appropriate time (on change of registered callback, or on destruction of
40 /// associated object). This is used for saving the pointer to it for such deferred destruction.
41 pub(crate) struct MultiUseCallback<ClosureProto: ?Sized, ProxyProto> {
42     saved: Option<*mut Box<ClosureProto>>,
43     proxy: PhantomData<*const ProxyProto>,
44 }
45 
46 impl<ClosureProto: ?Sized, ProxyProto> Default for MultiUseCallback<ClosureProto, ProxyProto> {
47     #[inline(always)]
default() -> Self48     fn default() -> Self {
49         MultiUseCallback::<ClosureProto, ProxyProto> { saved: None, proxy: PhantomData }
50     }
51 }
52 
53 impl<ClosureProto: ?Sized, ProxyProto> MultiUseCallback<ClosureProto, ProxyProto> {
54     /// Creates a new instance.
55     ///
56     /// **Note**, an existing instance should always be overwritten with a new one, to ensure the
57     /// old one is correctly freed.
58     #[inline]
new(cb: Option<Box<ClosureProto>>) -> Self59     pub fn new(cb: Option<Box<ClosureProto>>) -> Self {
60         match cb {
61             Some(f) => MultiUseCallback::<ClosureProto, ProxyProto> {
62                 saved: Some(Box::into_raw(Box::new(f))),
63                 proxy: PhantomData,
64             },
65             None => Default::default(),
66         }
67     }
68 
69     /// Returns callback params to give to the C API (a tuple of function pointer and data pointer).
70     #[inline]
get_capi_params(&self, proxy: ProxyProto) -> (Option<ProxyProto>, *mut c_void)71     pub fn get_capi_params(&self, proxy: ProxyProto) -> (Option<ProxyProto>, *mut c_void) {
72         match self.saved {
73             Some(ref f) => (Some(proxy), *f as *mut c_void),
74             None => (None, null_mut::<c_void>()),
75         }
76     }
77 
78     /// Converts void closure pointer back to real type.
79     ///
80     /// For use in callback proxies.
81     ///
82     /// Only a reference is returned, in order to deliberately avoid reclaiming ownership and thus
83     /// triggering of destruction.
84     ///
85     /// Panics if `ptr` is null.
86     #[inline(always)]
get_callback<'a>(ptr: *mut c_void) -> &'a mut Box<ClosureProto>87     pub fn get_callback<'a>(ptr: *mut c_void) -> &'a mut Box<ClosureProto> {
88         assert!(!ptr.is_null());
89         // Note, does NOT destroy closure callback after use - only handles pointer
90         unsafe { &mut *(ptr as *mut Box<ClosureProto>) }
91     }
92 }
93 
94 impl<ClosureProto: ?Sized, ProxyProto> Drop for MultiUseCallback<ClosureProto, ProxyProto> {
95     #[inline]
drop(&mut self)96     fn drop(&mut self) {
97         if self.saved.is_some() {
98             let _to_drop = unsafe { Box::from_raw(self.saved.unwrap()) };
99         }
100     }
101 }
102 
103 /// Converts single-use-callback closure to pointer for C API.
104 ///
105 /// It can be restored in an `extern "C"` callback proxy with `get_su_callback`.
106 ///
107 /// Note: The closure itself needs to exist on the heap, and here we take it as such (wrapped in a
108 /// `Box`); from this you may assume that a pointer is already available. However, this is a pointer
109 /// to a trait object, and these are special in Rust, they are twice the size of a normal pointer
110 /// because in actual fact it is implemented as a pair of pointers (one to a type instance and one
111 /// to a ‘vtable’). We can only pass a normal sized pointer through the C API, so we must further
112 /// box it, producing `Box<Box<Closure>>` which we convert to `*mut Box<Closure>` and then further
113 /// to simply `*mut c_void`.
114 #[inline(always)]
box_closure_get_capi_ptr<ClosureProto: ?Sized>(callback: Box<ClosureProto>) -> *mut c_void115 pub(crate) fn box_closure_get_capi_ptr<ClosureProto: ?Sized>(callback: Box<ClosureProto>)
116     -> *mut c_void
117 {
118     Box::into_raw(Box::new(callback)) as *mut c_void
119 }
120 
121 /// Gets the C API callback params (function pointer and data pointer pair), for an optional
122 /// single-use callback closure.
123 ///
124 /// The proxy function must be specified. If `callback` is `None` then a pair of null pointers will
125 /// be returned. Otherwise, a pair consisting of the given proxy and a pointer for the given closure
126 /// will be returned. The data pointer can be restored to the actual (boxed) closure in the
127 /// `extern "C"` callback proxy with `get_su_callback`.
128 #[inline]
get_su_capi_params<ClosureProto: ?Sized, ProxyProto>( callback: Option<Box<ClosureProto>>, proxy: ProxyProto) -> (Option<ProxyProto>, *mut c_void)129 pub(crate) fn get_su_capi_params<ClosureProto: ?Sized, ProxyProto>(
130     callback: Option<Box<ClosureProto>>, proxy: ProxyProto) -> (Option<ProxyProto>, *mut c_void)
131 {
132     match callback {
133         Some(f) => (Some(proxy), box_closure_get_capi_ptr::<ClosureProto>(f)),
134         None => (None, null_mut::<c_void>()),
135     }
136 }
137 
138 /// Converts void single-use-callback closure pointer back to real type.
139 ///
140 /// For use in callback proxies.
141 ///
142 /// Returns ownership of the closure, thus it can be destroyed after use.
143 ///
144 /// Panics if `ptr` is null.
145 #[inline(always)]
get_su_callback<ClosureProto: ?Sized>(ptr: *mut c_void) -> Box<Box<ClosureProto>>146 pub(crate) fn get_su_callback<ClosureProto: ?Sized>(ptr: *mut c_void) -> Box<Box<ClosureProto>> {
147     assert!(!ptr.is_null());
148     unsafe { Box::from_raw(ptr as *mut Box<ClosureProto>) }
149 }
150 
151 /// Used by list-iteration style callback proxies, which are single use, but make multiple
152 /// executions of the callback, once per item and once to signal end-of-list.
153 #[inline]
callback_for_list_instance<Item, ItemRaw, Conv>(i: *const ItemRaw, eol: i32, userdata: *mut c_void, conv: Conv) where Conv: Fn(*const ItemRaw) -> Item154 pub(crate) fn callback_for_list_instance<Item, ItemRaw, Conv>(i: *const ItemRaw, eol: i32,
155     userdata: *mut c_void, conv: Conv)
156     where Conv: Fn(*const ItemRaw) -> Item // Converter, from raw item pointer to item wrapper
157 {
158     assert!(!userdata.is_null());
159     let mut callback = ManuallyDrop::new(unsafe {
160         Box::from_raw(userdata as *mut Box<dyn FnMut(ListResult<&Item>)>)
161     });
162     // NOTE: The creation of this reference variable is required to fix compiling before 1.46!
163     use std::ops::DerefMut;
164     #[allow(unused_mut)]
165     let mut callback_ref = callback.deref_mut();
166 
167     match eol {
168         // Item instance (NOT end-of-list or error)
169         0 => {
170             assert!(!i.is_null());
171             let item = conv(i); // Convert from raw item pointer to item wrapper
172             (callback_ref)(ListResult::Item(&item));
173             // Deliberately not dropping!
174             return;
175         },
176         // End of list marker
177         i if i > 0 => {
178             (callback_ref)(ListResult::End);
179         },
180         // Error
181         _ => {
182             (callback_ref)(ListResult::Error);
183         },
184     }
185     unsafe { ManuallyDrop::drop(&mut callback) };
186 }
187