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