1use std::sync::{Mutex, PoisonError};
5
6use crate::abi::error::{FfiError, MARSHAL_ERROR_CODE};
7
8pub struct Iter<T> {
17 inner: Box<dyn Iterator<Item = T> + Send>,
18}
19
20impl<T> Iter<T> {
21 pub fn new<I>(iter: I) -> Self
26 where
27 I: IntoIterator<Item = T>,
28 I::IntoIter: Send + 'static,
29 {
30 Self {
31 inner: Box::new(iter.into_iter()),
32 }
33 }
34}
35
36impl<T> Iterator for Iter<T> {
37 type Item = T;
38
39 fn next(&mut self) -> Option<T> {
40 self.inner.next()
41 }
42}
43
44impl<T> std::fmt::Debug for Iter<T> {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("Iter").finish_non_exhaustive()
47 }
48}
49
50#[derive(Debug)]
59pub struct IterHandle<T> {
60 inner: Mutex<Option<Iter<T>>>,
61}
62
63#[must_use]
66pub fn iter_into_raw<T>(iter: Iter<T>) -> *mut IterHandle<T> {
67 crate::abi::leak::track(crate::abi::leak::ITERATORS, 1);
68 Box::into_raw(Box::new(IterHandle {
69 inner: Mutex::new(Some(iter)),
70 }))
71}
72
73struct Advancing<'a, T> {
76 handle: &'a IterHandle<T>,
77 iter: Option<Iter<T>>,
78}
79
80impl<T> Drop for Advancing<'_, T> {
81 fn drop(&mut self) {
82 let mut slot = self
83 .handle
84 .inner
85 .lock()
86 .unwrap_or_else(PoisonError::into_inner);
87 *slot = self.iter.take();
88 }
89}
90
91pub unsafe fn iter_next<T>(handle: *const IterHandle<T>) -> Result<Option<T>, FfiError> {
107 let Some(handle) = (unsafe { handle.as_ref() }) else {
109 return Err(FfiError::new(MARSHAL_ERROR_CODE, "iterator is null"));
110 };
111 let taken = handle
112 .inner
113 .lock()
114 .unwrap_or_else(PoisonError::into_inner)
115 .take();
116 let Some(iter) = taken else {
117 return Err(FfiError::new(
118 MARSHAL_ERROR_CODE,
119 "iterator is already being advanced (a concurrent or re-entrant _next)",
120 ));
121 };
122 let mut advancing = Advancing {
123 handle,
124 iter: Some(iter),
125 };
126 let item = advancing.iter.as_mut().and_then(Iterator::next);
127 drop(advancing);
128 Ok(item)
129}
130
131pub unsafe fn iter_destroy<T>(handle: *mut IterHandle<T>) {
140 if handle.is_null() {
141 return;
142 }
143 crate::abi::leak::track(crate::abi::leak::ITERATORS, -1);
144 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
145 drop(unsafe { Box::from_raw(handle) });
148 }));
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 fn next<T>(h: *const IterHandle<T>) -> Option<T> {
156 unsafe { iter_next(h) }.unwrap()
157 }
158
159 #[test]
160 fn pulls_until_exhausted() {
161 let h = iter_into_raw(Iter::new(vec![1, 2]));
162 assert_eq!(next(h), Some(1));
163 assert_eq!(next(h), Some(2));
164 assert_eq!(next(h), None);
165 unsafe {
166 assert!(iter_next::<i32>(std::ptr::null()).is_err());
167 iter_destroy(h);
168 iter_destroy::<i32>(std::ptr::null_mut());
169 }
170 }
171
172 #[test]
173 fn concurrent_next_yields_each_element_once_or_reports_busy() {
174 let h = iter_into_raw(Iter::new(0..10_000u32));
175 let addr = h as usize;
176 let threads: Vec<_> = (0..8)
177 .map(|_| {
178 std::thread::spawn(move || {
179 let h = addr as *const IterHandle<u32>;
180 let mut got = Vec::new();
181 loop {
182 match unsafe { iter_next(h) } {
183 Ok(Some(x)) => got.push(x),
184 Ok(None) => break,
185 Err(e) => {
186 assert_eq!(e.code, MARSHAL_ERROR_CODE);
187 std::thread::yield_now();
188 }
189 }
190 }
191 got
192 })
193 })
194 .collect();
195 let mut all: Vec<u32> = threads
196 .into_iter()
197 .flat_map(|t| t.join().unwrap())
198 .collect();
199 all.sort_unstable();
200 assert_eq!(all, (0..10_000).collect::<Vec<_>>());
201 unsafe { iter_destroy(h) };
202 }
203
204 #[test]
205 fn a_panicking_next_puts_the_iterator_back() {
206 let mut n = 0;
207 let h = iter_into_raw(Iter::new(std::iter::from_fn(move || {
208 n += 1;
209 assert!(n != 2, "second element");
210 Some(n)
211 })));
212 assert_eq!(next(h), Some(1));
213 let addr = h as usize;
214 assert!(std::panic::catch_unwind(move || {
215 let _ = unsafe { iter_next(addr as *const IterHandle<i32>) };
216 })
217 .is_err());
218 assert_eq!(next(h), Some(3));
219 unsafe { iter_destroy(h) };
220 }
221}