1 | use super::*; |
2 | use std::sync::*; |
3 | |
4 | /// A type that you can use to declare and implement an event of a specified delegate type. |
5 | /// |
6 | /// The implementation is thread-safe and designed to avoid contention between events being |
7 | /// raised and delegates being added or removed. |
8 | pub struct Event<T: ComInterface> { |
9 | swap: Mutex<()>, |
10 | change: Mutex<()>, |
11 | delegates: Array<T>, |
12 | } |
13 | |
14 | impl<T: ComInterface> Default for Event<T> { |
15 | fn default() -> Self { |
16 | Self::new() |
17 | } |
18 | } |
19 | |
20 | impl<T: ComInterface> Event<T> { |
21 | /// Creates a new, empty `Event<T>`. |
22 | pub fn new() -> Self { |
23 | Self { delegates: Array::new(), swap: Mutex::default(), change: Mutex::default() } |
24 | } |
25 | |
26 | /// Registers a delegate with the event object. |
27 | pub fn add(&mut self, delegate: &T) -> Result<i64> { |
28 | let mut _lock_free_drop = Array::new(); |
29 | Ok({ |
30 | let _change_lock = self.change.lock().unwrap(); |
31 | let mut new_delegates = Array::with_capacity(self.delegates.len() + 1)?; |
32 | for delegate in self.delegates.as_slice() { |
33 | new_delegates.push(delegate.clone()); |
34 | } |
35 | let delegate = Delegate::new(delegate)?; |
36 | let token = delegate.to_token(); |
37 | new_delegates.push(delegate); |
38 | |
39 | let _swap_lock = self.swap.lock().unwrap(); |
40 | _lock_free_drop = self.delegates.swap(new_delegates); |
41 | token |
42 | }) |
43 | } |
44 | |
45 | /// Revokes a delegate's registration from the event object. |
46 | pub fn remove(&mut self, token: i64) -> Result<()> { |
47 | let mut _lock_free_drop = Array::new(); |
48 | { |
49 | let _change_lock = self.change.lock().unwrap(); |
50 | if self.delegates.is_empty() { |
51 | return Ok(()); |
52 | } |
53 | let mut capacity = self.delegates.len() - 1; |
54 | let mut new_delegates = Array::new(); |
55 | let mut removed = false; |
56 | if capacity == 0 { |
57 | removed = self.delegates.as_slice()[0].to_token() == token; |
58 | } else { |
59 | new_delegates = Array::with_capacity(capacity)?; |
60 | for delegate in self.delegates.as_slice() { |
61 | if !removed && delegate.to_token() == token { |
62 | removed = true; |
63 | continue; |
64 | } |
65 | if capacity == 0 { |
66 | break; |
67 | } |
68 | new_delegates.push(delegate.clone()); |
69 | capacity -= 1; |
70 | } |
71 | } |
72 | if removed { |
73 | let _swap_lock = self.swap.lock().unwrap(); |
74 | _lock_free_drop = self.delegates.swap(new_delegates); |
75 | } |
76 | } |
77 | Ok(()) |
78 | } |
79 | |
80 | /// Clears the event, removing all delegates. |
81 | pub fn clear(&mut self) { |
82 | let mut _lock_free_drop = Array::new(); |
83 | { |
84 | let _change_lock = self.change.lock().unwrap(); |
85 | if self.delegates.is_empty() { |
86 | return; |
87 | } |
88 | let _swap_lock = self.swap.lock().unwrap(); |
89 | _lock_free_drop = self.delegates.swap(Array::new()); |
90 | } |
91 | } |
92 | |
93 | /// Invokes all of the event object's registered delegates with the provided callback. |
94 | pub fn call<F: FnMut(&T) -> Result<()>>(&mut self, mut callback: F) -> Result<()> { |
95 | let lock_free_calls = { |
96 | let _swap_lock = self.swap.lock().unwrap(); |
97 | self.delegates.clone() |
98 | }; |
99 | for delegate in lock_free_calls.as_slice() { |
100 | if let Err(error) = delegate.call(&mut callback) { |
101 | const RPC_E_SERVER_UNAVAILABLE: HRESULT = HRESULT(-2147023174); // HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) |
102 | if matches!(error.code(), crate::imp::RPC_E_DISCONNECTED | crate::imp::JSCRIPT_E_CANTEXECUTE | RPC_E_SERVER_UNAVAILABLE) { |
103 | self.remove(delegate.to_token())?; |
104 | } |
105 | } |
106 | } |
107 | Ok(()) |
108 | } |
109 | } |
110 | |
111 | /// A thread-safe reference-counted array of delegates. |
112 | struct Array<T: ComInterface> { |
113 | buffer: *mut Buffer<T>, |
114 | len: usize, |
115 | _phantom: std::marker::PhantomData<T>, |
116 | } |
117 | |
118 | impl<T: ComInterface> Default for Array<T> { |
119 | fn default() -> Self { |
120 | Self::new() |
121 | } |
122 | } |
123 | |
124 | impl<T: ComInterface> Array<T> { |
125 | /// Creates a new, empty `Array<T>` with no capacity. |
126 | fn new() -> Self { |
127 | Self { buffer: std::ptr::null_mut(), len: 0, _phantom: std::marker::PhantomData } |
128 | } |
129 | |
130 | /// Creates a new, empty `Array<T>` with the specified capacity. |
131 | fn with_capacity(capacity: usize) -> Result<Self> { |
132 | Ok(Self { buffer: Buffer::new(capacity)?, len: 0, _phantom: std::marker::PhantomData }) |
133 | } |
134 | |
135 | /// Swaps the contents of two `Array<T>` objects. |
136 | fn swap(&mut self, mut other: Self) -> Self { |
137 | unsafe { std::ptr::swap(&mut self.buffer, &mut other.buffer) }; |
138 | std::mem::swap(&mut self.len, &mut other.len); |
139 | other |
140 | } |
141 | |
142 | /// Returns `true` if the array contains no delegates. |
143 | fn is_empty(&self) -> bool { |
144 | self.len == 0 |
145 | } |
146 | |
147 | /// Returns the number of delegates in the array. |
148 | fn len(&self) -> usize { |
149 | self.len |
150 | } |
151 | |
152 | /// Appends a delegate to the back of the array. |
153 | fn push(&mut self, delegate: Delegate<T>) { |
154 | unsafe { |
155 | std::ptr::write((*self.buffer).as_mut_ptr().add(self.len), delegate); |
156 | self.len += 1; |
157 | } |
158 | } |
159 | |
160 | /// Returns a slice containing of all delegates. |
161 | fn as_slice(&self) -> &[Delegate<T>] { |
162 | if self.is_empty() { |
163 | &[] |
164 | } else { |
165 | unsafe { std::slice::from_raw_parts((*self.buffer).as_ptr(), self.len) } |
166 | } |
167 | } |
168 | |
169 | /// Returns a mutable slice of all delegates. |
170 | fn as_mut_slice(&mut self) -> &mut [Delegate<T>] { |
171 | if self.is_empty() { |
172 | &mut [] |
173 | } else { |
174 | unsafe { std::slice::from_raw_parts_mut((*self.buffer).as_mut_ptr(), self.len) } |
175 | } |
176 | } |
177 | } |
178 | |
179 | impl<T: ComInterface> Clone for Array<T> { |
180 | fn clone(&self) -> Self { |
181 | if !self.is_empty() { |
182 | unsafe { (*self.buffer).0.add_ref() }; |
183 | } |
184 | Self { buffer: self.buffer, len: self.len, _phantom: std::marker::PhantomData } |
185 | } |
186 | } |
187 | |
188 | impl<T: ComInterface> Drop for Array<T> { |
189 | fn drop(&mut self) { |
190 | unsafe { |
191 | if !self.is_empty() && (*self.buffer).0.release() == 0 { |
192 | std::ptr::drop_in_place(self.as_mut_slice()); |
193 | crate::imp::heap_free(self.buffer as _) |
194 | } |
195 | } |
196 | } |
197 | } |
198 | |
199 | /// A reference-counted buffer. |
200 | #[repr (C)] |
201 | struct Buffer<T>(crate::imp::RefCount, std::marker::PhantomData<T>); |
202 | |
203 | impl<T: ComInterface> Buffer<T> { |
204 | /// Creates a new `Buffer` with the specified size in bytes. |
205 | fn new(len: usize) -> Result<*mut Self> { |
206 | if len == 0 { |
207 | Ok(std::ptr::null_mut()) |
208 | } else { |
209 | let alloc_size = std::mem::size_of::<Self>() + len * std::mem::size_of::<Delegate<T>>(); |
210 | let header = crate::imp::heap_alloc(alloc_size)? as *mut Self; |
211 | unsafe { |
212 | header.write(Self(crate::imp::RefCount::new(1), std::marker::PhantomData)); |
213 | } |
214 | Ok(header) |
215 | } |
216 | } |
217 | |
218 | /// Returns a raw pointer to the buffer's contents. The resulting pointer might be uninititalized. |
219 | fn as_ptr(&self) -> *const Delegate<T> { |
220 | unsafe { (self as *const Self).add(1) as *const _ } |
221 | } |
222 | |
223 | /// Returns a raw mutable pointer to the buffer's contents. The resulting pointer might be uninititalized. |
224 | fn as_mut_ptr(&mut self) -> *mut Delegate<T> { |
225 | unsafe { (self as *mut Self).add(1) as *mut _ } |
226 | } |
227 | } |
228 | |
229 | /// Holds either a direct or indirect reference to a delegate. A direct reference is typically |
230 | /// agile while an indirect reference is an agile wrapper. |
231 | #[derive (Clone)] |
232 | enum Delegate<T> { |
233 | Direct(T), |
234 | Indirect(AgileReference<T>), |
235 | } |
236 | |
237 | impl<T: ComInterface> Delegate<T> { |
238 | /// Creates a new `Delegate<T>`, containing a suitable reference to the specified delegate. |
239 | fn new(delegate: &T) -> Result<Self> { |
240 | if delegate.cast::<crate::imp::IAgileObject>().is_ok() { |
241 | Ok(Self::Direct(delegate.clone())) |
242 | } else { |
243 | Ok(Self::Indirect(AgileReference::new(delegate)?)) |
244 | } |
245 | } |
246 | |
247 | /// Returns an encoded token to identify the delegate. |
248 | fn to_token(&self) -> i64 { |
249 | unsafe { |
250 | match self { |
251 | Self::Direct(delegate) => crate::imp::EncodePointer(std::mem::transmute_copy(delegate)) as i64, |
252 | Self::Indirect(delegate) => crate::imp::EncodePointer(std::mem::transmute_copy(delegate)) as i64, |
253 | } |
254 | } |
255 | } |
256 | |
257 | /// Invokes the delegates with the provided callback. |
258 | fn call<F: FnMut(&T) -> Result<()>>(&self, mut callback: F) -> Result<()> { |
259 | match self { |
260 | Self::Direct(delegate) => callback(delegate), |
261 | Self::Indirect(delegate) => callback(&delegate.resolve()?), |
262 | } |
263 | } |
264 | } |
265 | |