1use crate::{AsyncGroup, ErrEntry};
6
7use std::sync::Arc;
8use std::{mem, thread};
9
10#[derive(Debug)]
12pub enum AsyncGroupError {
13 ThreadPanicked(String),
16}
17
18impl AsyncGroup {
19 pub(crate) fn new() -> Self {
20 Self {
21 handlers: Vec::new(),
22 _index: 0,
23 _name: Default::default(),
24 }
25 }
26
27 pub fn add<F>(&mut self, f: F)
41 where
42 F: FnOnce() -> errs::Result<()> + Send + 'static,
43 {
44 self.handlers
45 .push((self._index, self._name.clone(), thread::spawn(f)));
46 }
47
48 pub(crate) fn join_and_collect_errors(mut self, errors: &mut Vec<ErrEntry>) {
49 if self.handlers.is_empty() {
50 return;
51 }
52
53 let vec = mem::take(&mut self.handlers);
54
55 for h in vec.into_iter() {
56 match h.2.join() {
57 Ok(r) => {
58 if let Err(e) = r {
59 errors.push(ErrEntry {
60 index: h.0,
61 name: h.1,
62 err: e,
63 });
64 }
65 }
66 Err(e) => {
67 let s = if let Some(s) = e.downcast_ref::<&'static str>() {
68 s.to_string()
69 } else if let Some(s) = e.downcast_ref::<String>() {
70 s.clone()
71 } else if let Some(s) = e.downcast_ref::<Arc<&str>>() {
72 s.to_string()
73 } else if let Some(s) = e.downcast_ref::<Arc<str>>() {
74 s.to_string()
75 } else {
76 "Unknown panic payload".to_string()
77 };
78 let e = errs::Err::new(AsyncGroupError::ThreadPanicked(s));
79 errors.push(ErrEntry {
80 index: h.0,
81 name: h.1,
82 err: e,
83 });
84 }
85 }
86 }
87 }
88
89 pub(crate) fn join_and_ignore_errors(mut self) {
90 if self.handlers.is_empty() {
91 return;
92 }
93
94 let vec = mem::take(&mut self.handlers);
95
96 for h in vec.into_iter() {
97 let _ = h.2.join();
98 }
99 }
100}
101
102#[cfg_attr(coverage_nightly, coverage(off))]
103#[cfg(test)]
104mod tests_of_async_group {
105 use super::*;
106 use std::{sync, time};
107
108 const BASE_LINE: u32 = line!();
109
110 #[derive(Debug, PartialEq)]
111 enum Reasons {
112 BadString(String),
113 }
114
115 struct MyStruct {
116 string: sync::Arc<sync::Mutex<String>>,
117 fail: bool,
118 }
119
120 impl MyStruct {
121 fn new(s: String, fail: bool) -> Self {
122 Self {
123 string: sync::Arc::new(sync::Mutex::new(s)),
124 fail,
125 }
126 }
127
128 fn process(&self, ag: &mut AsyncGroup) {
129 let s_mutex = self.string.clone();
130 let fail = self.fail;
131 ag.add(move || {
132 let _ = thread::sleep(time::Duration::from_millis(100));
133 {
134 let mut s = s_mutex.lock().unwrap();
135 if fail {
136 return Err(errs::Err::new(Reasons::BadString(s.to_string())));
137 }
138 *s = s.to_uppercase();
139 }
140 Ok(())
141 });
142 }
143
144 fn process_multiple(&self, ag: &mut AsyncGroup) {
145 let s_mutex = self.string.clone();
146 let fail = self.fail;
147 ag.add(move || {
148 let _ = thread::sleep(time::Duration::from_millis(100));
149 {
150 let mut s = s_mutex.lock().unwrap();
151 if fail {
152 return Err(errs::Err::new(Reasons::BadString(s.to_string())));
153 }
154 *s = s.to_uppercase();
155 }
156 Ok(())
157 });
158
159 let s_mutex = self.string.clone();
160 let fail = self.fail;
161 ag.add(move || {
162 let _ = thread::sleep(time::Duration::from_millis(100));
163 {
164 let mut s = s_mutex.lock().unwrap();
165 if fail {
166 return Err(errs::Err::new(Reasons::BadString(s.to_string())));
167 }
168 *s = s.to_uppercase();
169 }
170 Ok(())
171 });
172 }
173 }
174
175 mod tests_of_join_and_collect_errors {
176 use super::*;
177 use std::panic::panic_any;
178
179 #[test]
180 fn zero() {
181 let ag = AsyncGroup::new();
182
183 let mut err_vec = Vec::new();
184 ag.join_and_collect_errors(&mut err_vec);
185
186 assert!(err_vec.is_empty());
187 }
188
189 #[test]
190 fn single_ok() {
191 let mut ag = AsyncGroup::new();
192
193 let struct_a = MyStruct::new("a".to_string(), false);
194 assert_eq!(*struct_a.string.lock().unwrap(), "a");
195
196 ag._index = 12;
197 struct_a.process(&mut ag);
198
199 let mut errors = Vec::new();
200 ag.join_and_collect_errors(&mut errors);
201
202 assert!(errors.is_empty());
203 assert_eq!(*struct_a.string.lock().unwrap(), "A");
204 }
205
206 #[test]
207 fn single_fail() {
208 let mut ag = AsyncGroup::new();
209
210 let struct_a = MyStruct::new("a".to_string(), true);
211 assert_eq!(*struct_a.string.lock().unwrap(), "a");
212
213 ag._index = 12;
214 ag._name = "foo".into();
215 struct_a.process(&mut ag);
216
217 let mut errors = Vec::new();
218 ag.join_and_collect_errors(&mut errors);
219
220 assert_eq!(errors.len(), 1);
221 assert_eq!(*struct_a.string.lock().unwrap(), "a");
222
223 assert_eq!(errors[0].index, 12);
224 assert_eq!(errors[0].name, "foo".into());
225 #[cfg(unix)]
226 assert_eq!(
227 format!("{:?}", errors[0].err),
228 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"a\"), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 28).to_string() + " }"
229 );
230 #[cfg(windows)]
231 assert_eq!(
232 format!("{:?}", errors[0].err),
233 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"a\"), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 28).to_string() + " }"
234 );
235 }
236
237 #[test]
238 fn multiple_ok() {
239 let mut ag = AsyncGroup::new();
240
241 let struct_a = MyStruct::new("a".to_string(), false);
242 assert_eq!(*struct_a.string.lock().unwrap(), "a".to_string());
243
244 let struct_b = MyStruct::new("b".to_string(), false);
245 assert_eq!(*struct_b.string.lock().unwrap(), "b".to_string());
246
247 let struct_c = MyStruct::new("c".to_string(), false);
248 assert_eq!(*struct_c.string.lock().unwrap(), "c".to_string());
249
250 ag._index = 12;
251 ag._name = "foo".into();
252 struct_a.process(&mut ag);
253
254 ag._index = 34;
255 ag._name = "bar".into();
256 struct_b.process(&mut ag);
257
258 ag._index = 56;
259 ag._name = "baz".into();
260 struct_c.process(&mut ag);
261
262 let mut err_vec = Vec::new();
263 ag.join_and_collect_errors(&mut err_vec);
264
265 assert_eq!(err_vec.len(), 0);
266
267 assert_eq!(*struct_a.string.lock().unwrap(), "A");
268 assert_eq!(*struct_b.string.lock().unwrap(), "B");
269 assert_eq!(*struct_c.string.lock().unwrap(), "C");
270 }
271
272 #[test]
273 fn multiple_processes_and_single_fail() {
274 let mut ag = AsyncGroup::new();
275
276 let struct_a = MyStruct::new("a".to_string(), false);
277 assert_eq!(*struct_a.string.lock().unwrap(), "a");
278
279 let struct_b = MyStruct::new("b".to_string(), true);
280 assert_eq!(*struct_b.string.lock().unwrap(), "b");
281
282 let struct_c = MyStruct::new("c".to_string(), false);
283 assert_eq!(*struct_c.string.lock().unwrap(), "c");
284
285 ag._index = 12;
286 ag._name = "foo".into();
287 struct_a.process(&mut ag);
288
289 ag._index = 34;
290 ag._name = "bar".into();
291 struct_b.process(&mut ag);
292
293 ag._index = 56;
294 ag._name = "baz".into();
295 struct_c.process(&mut ag);
296
297 let mut err_vec = Vec::new();
298 ag.join_and_collect_errors(&mut err_vec);
299
300 assert_eq!(err_vec.len(), 1);
301 assert_eq!(err_vec[0].index, 34);
302 assert_eq!(err_vec[0].name, "bar".into());
303 #[cfg(unix)]
304 assert_eq!(
305 format!("{:?}", err_vec[0].err),
306 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"b\"), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 28).to_string() + " }",
307 );
308 #[cfg(windows)]
309 assert_eq!(
310 format!("{:?}", err_vec[0].err),
311 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"b\"), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 28).to_string() + " }",
312 );
313
314 assert_eq!(*struct_a.string.lock().unwrap(), "A");
315 assert_eq!(*struct_b.string.lock().unwrap(), "b");
316 assert_eq!(*struct_c.string.lock().unwrap(), "C");
317 }
318
319 #[test]
320 fn multiple_fail() {
321 let mut ag = AsyncGroup::new();
322
323 let struct_a = MyStruct::new("a".to_string(), true);
324 assert_eq!(*struct_a.string.lock().unwrap(), "a");
325
326 let struct_b = MyStruct::new("b".to_string(), true);
327 assert_eq!(*struct_b.string.lock().unwrap(), "b");
328
329 let struct_c = MyStruct::new("c".to_string(), true);
330 assert_eq!(*struct_c.string.lock().unwrap(), "c");
331
332 ag._index = 12;
333 ag._name = "foo".into();
334 struct_a.process(&mut ag);
335
336 ag._index = 34;
337 ag._name = "bar".into();
338 struct_b.process(&mut ag);
339
340 ag._index = 56;
341 ag._name = "baz".into();
342 struct_c.process(&mut ag);
343
344 let mut err_vec = Vec::new();
345 ag.join_and_collect_errors(&mut err_vec);
346
347 assert_eq!(err_vec.len(), 3);
348
349 assert_eq!(err_vec[0].index, 12);
350 assert_eq!(err_vec[1].index, 34);
351 assert_eq!(err_vec[2].index, 56);
352
353 assert_eq!(err_vec[0].name, "foo".into());
354 assert_eq!(err_vec[1].name, "bar".into());
355 assert_eq!(err_vec[2].name, "baz".into());
356
357 #[cfg(unix)]
358 assert_eq!(
359 format!("{:?}", err_vec[0].err),
360 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"a\"), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 28).to_string() + " }",
361 );
362 #[cfg(windows)]
363 assert_eq!(
364 format!("{:?}", err_vec[0].err),
365 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"a\"), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 28).to_string() + " }",
366 );
367 #[cfg(unix)]
368 assert_eq!(
369 format!("{:?}", err_vec[1].err),
370 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"b\"), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 28).to_string() + " }",
371 );
372 #[cfg(windows)]
373 assert_eq!(
374 format!("{:?}", err_vec[1].err),
375 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"b\"), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 28).to_string() + " }",
376 );
377 #[cfg(unix)]
378 assert_eq!(
379 format!("{:?}", err_vec[2].err),
380 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"c\"), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 28).to_string() + " }",
381 );
382 #[cfg(windows)]
383 assert_eq!(
384 format!("{:?}", err_vec[2].err),
385 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"c\"), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 28).to_string() + " }",
386 );
387
388 assert_eq!(*struct_a.string.lock().unwrap(), "a");
389 assert_eq!(*struct_b.string.lock().unwrap(), "b");
390 assert_eq!(*struct_c.string.lock().unwrap(), "c");
391 }
392
393 #[test]
394 fn data_src_execute_multiple_setup_handles() {
395 let mut ag = AsyncGroup::new();
396
397 let struct_d = MyStruct::new("d".to_string(), false);
398 assert_eq!(*struct_d.string.lock().unwrap(), "d");
399
400 ag._index = 123;
401 ag._name = "foo".into();
402 struct_d.process(&mut ag);
403
404 let mut err_vec = Vec::new();
405 ag.join_and_collect_errors(&mut err_vec);
406
407 assert_eq!(err_vec.len(), 0);
408
409 assert_eq!(*struct_d.string.lock().unwrap(), "D");
410 }
411
412 #[test]
413 fn collect_all_errors_if_data_src_executes_multiple_setup_handles() {
414 let mut ag = AsyncGroup::new();
415
416 let struct_d = MyStruct::new("d".to_string(), true);
417 assert_eq!(*struct_d.string.lock().unwrap(), "d");
418
419 ag._index = 123;
420 ag._name = "foo".into();
421 struct_d.process_multiple(&mut ag);
422
423 let mut err_vec = Vec::new();
424 ag.join_and_collect_errors(&mut err_vec);
425
426 assert_eq!(err_vec.len(), 2);
427
428 assert_eq!(err_vec[0].index, 123);
429 assert_eq!(err_vec[1].index, 123);
430
431 assert_eq!(err_vec[0].name, "foo".into());
432 assert_eq!(err_vec[1].name, "foo".into());
433
434 #[cfg(unix)]
435 assert_eq!(
436 format!("{:?}", err_vec[0].err),
437 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"d\"), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 44).to_string() + " }",
438 );
439 #[cfg(windows)]
440 assert_eq!(
441 format!("{:?}", err_vec[0].err),
442 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"d\"), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 44).to_string() + " }"
443 );
444
445 #[cfg(unix)]
446 assert_eq!(
447 format!("{:?}", err_vec[1].err),
448 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"d\"), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 58).to_string() + " }",
449 );
450 #[cfg(windows)]
451 assert_eq!(
452 format!("{:?}", err_vec[1].err),
453 "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"d\"), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 58).to_string() + " }"
454 );
455
456 assert_eq!(*struct_d.string.lock().unwrap(), "d");
457 }
458
459 #[test]
460 fn async_code_calls_thread_panic_with_a_str() {
461 let mut ag = AsyncGroup::new();
462
463 ag._index = 123;
464 ag._name = "foo".into();
465
466 ag.add(|| {
467 panic!("panic");
468 });
469
470 let mut errors = Vec::<ErrEntry>::new();
471 ag.join_and_collect_errors(&mut errors);
472
473 assert_eq!(errors.len(), 1);
474 assert_eq!(errors[0].index, 123);
475 assert_eq!(errors[0].name, "foo".into());
476 #[cfg(unix)]
477 assert_eq!(format!("{:?}", errors[0].err), "errs::Err { reason = sabi::async_group::AsyncGroupError ThreadPanicked(\"panic\"), file = src/async_group.rs, line = 78 }");
478 #[cfg(windows)]
479 assert_eq!(format!("{:?}", errors[0].err), "errs::Err { reason = sabi::async_group::AsyncGroupError ThreadPanicked(\"panic\"), file = src\\async_group.rs, line = 78 }");
480 }
481
482 #[test]
483 fn async_code_calls_thread_panic_with_a_string() {
484 let mut ag = AsyncGroup::new();
485
486 ag._index = 123;
487 ag._name = "foo".into();
488
489 ag.add(|| {
490 panic_any("panic".to_string());
491 });
492
493 let mut errors = Vec::<ErrEntry>::new();
494 ag.join_and_collect_errors(&mut errors);
495
496 assert_eq!(errors.len(), 1);
497 assert_eq!(errors[0].index, 123);
498 assert_eq!(errors[0].name, "foo".into());
499 #[cfg(unix)]
500 assert_eq!(format!("{:?}", errors[0].err), "errs::Err { reason = sabi::async_group::AsyncGroupError ThreadPanicked(\"panic\"), file = src/async_group.rs, line = 78 }");
501 #[cfg(windows)]
502 assert_eq!(format!("{:?}", errors[0].err), "errs::Err { reason = sabi::async_group::AsyncGroupError ThreadPanicked(\"panic\"), file = src\\async_group.rs, line = 78 }");
503 }
504
505 #[test]
506 fn async_code_calls_thread_panic_with_an_arc_str_ref() {
507 let mut ag = AsyncGroup::new();
508
509 ag._index = 123;
510 ag._name = "foo".into();
511
512 ag.add(|| {
513 panic_any(Arc::new("panic"));
514 });
515
516 let mut errors = Vec::<ErrEntry>::new();
517 ag.join_and_collect_errors(&mut errors);
518
519 assert_eq!(errors.len(), 1);
520 assert_eq!(errors[0].index, 123);
521 assert_eq!(errors[0].name, "foo".into());
522 #[cfg(unix)]
523 assert_eq!(format!("{:?}", errors[0].err), "errs::Err { reason = sabi::async_group::AsyncGroupError ThreadPanicked(\"panic\"), file = src/async_group.rs, line = 78 }");
524 #[cfg(windows)]
525 assert_eq!(format!("{:?}", errors[0].err), "errs::Err { reason = sabi::async_group::AsyncGroupError ThreadPanicked(\"panic\"), file = src\\async_group.rs, line = 78 }");
526 }
527
528 #[test]
529 fn async_code_calls_thread_panic_with_an_arc_str() {
530 let mut ag = AsyncGroup::new();
531
532 ag._index = 123;
533 ag._name = "foo".into();
534
535 ag.add(|| {
536 panic_any(Arc::<str>::from("panic"));
537 });
538
539 let mut errors = Vec::<ErrEntry>::new();
540 ag.join_and_collect_errors(&mut errors);
541
542 assert_eq!(errors.len(), 1);
543 assert_eq!(errors[0].index, 123);
544 assert_eq!(errors[0].name, "foo".into());
545 #[cfg(unix)]
546 assert_eq!(format!("{:?}", errors[0].err), "errs::Err { reason = sabi::async_group::AsyncGroupError ThreadPanicked(\"panic\"), file = src/async_group.rs, line = 78 }");
547 #[cfg(windows)]
548 assert_eq!(format!("{:?}", errors[0].err), "errs::Err { reason = sabi::async_group::AsyncGroupError ThreadPanicked(\"panic\"), file = src\\async_group.rs, line = 78 }");
549 }
550
551 #[test]
552 fn async_code_calls_thread_panic_with_an_unknown_value() {
553 let mut ag = AsyncGroup::new();
554
555 ag._index = 123;
556 ag._name = "foo".into();
557
558 ag.add(|| {
559 panic_any(987);
560 });
561
562 let mut errors = Vec::<ErrEntry>::new();
563 ag.join_and_collect_errors(&mut errors);
564
565 assert_eq!(errors.len(), 1);
566 assert_eq!(errors[0].index, 123);
567 assert_eq!(errors[0].name, "foo".into());
568 #[cfg(unix)]
569 assert_eq!(format!("{:?}", errors[0].err), "errs::Err { reason = sabi::async_group::AsyncGroupError ThreadPanicked(\"Unknown panic payload\"), file = src/async_group.rs, line = 78 }");
570 #[cfg(windows)]
571 assert_eq!(format!("{:?}", errors[0].err), "errs::Err { reason = sabi::async_group::AsyncGroupError ThreadPanicked(\"Unknown panic payload\"), file = src\\async_group.rs, line = 78 }");
572 }
573 }
574
575 mod tests_of_join_and_ignore_errors {
576 use super::*;
577
578 #[test]
579 fn zero() {
580 let ag = AsyncGroup::new();
581
582 ag.join_and_ignore_errors();
583 }
584
585 #[test]
586 fn single_ok() {
587 let mut ag = AsyncGroup::new();
588
589 let struct_a = MyStruct::new("a".to_string(), false);
590 assert_eq!(*struct_a.string.lock().unwrap(), "a");
591
592 ag._index = 123;
593 ag._name = "foo".into();
594 struct_a.process(&mut ag);
595
596 ag.join_and_ignore_errors();
597 assert_eq!(*struct_a.string.lock().unwrap(), "A");
598 }
599
600 #[test]
601 fn single_fail() {
602 let mut ag = AsyncGroup::new();
603
604 let struct_a = MyStruct::new("a".to_string(), true);
605 assert_eq!(*struct_a.string.lock().unwrap(), "a");
606
607 ag._index = 123;
608 ag._name = "foo".into();
609 struct_a.process(&mut ag);
610
611 ag.join_and_ignore_errors();
612 assert_eq!(*struct_a.string.lock().unwrap(), "a");
613 }
614
615 #[test]
616 fn multiple_ok() {
617 let mut ag = AsyncGroup::new();
618
619 let struct_a = MyStruct::new("a".to_string(), false);
620 assert_eq!(*struct_a.string.lock().unwrap(), "a");
621
622 let struct_b = MyStruct::new("b".to_string(), false);
623 assert_eq!(*struct_b.string.lock().unwrap(), "b");
624
625 let struct_c = MyStruct::new("c".to_string(), false);
626 assert_eq!(*struct_c.string.lock().unwrap(), "c");
627
628 ag._index = 123;
629 ag._name = "foo".into();
630 struct_a.process(&mut ag);
631
632 ag._index = 456;
633 ag._name = "bar".into();
634 struct_b.process(&mut ag);
635
636 ag._index = 789;
637 ag._name = "baz".into();
638 struct_c.process(&mut ag);
639
640 ag.join_and_ignore_errors();
641
642 assert_eq!(*struct_a.string.lock().unwrap(), "A");
643 assert_eq!(*struct_b.string.lock().unwrap(), "B");
644 assert_eq!(*struct_c.string.lock().unwrap(), "C");
645 }
646
647 #[test]
648 fn multiple_processes_and_single_fail() {
649 let mut ag = AsyncGroup::new();
650
651 let struct_a = MyStruct::new("a".to_string(), false);
652 assert_eq!(*struct_a.string.lock().unwrap(), "a");
653
654 let struct_b = MyStruct::new("b".to_string(), true);
655 assert_eq!(*struct_b.string.lock().unwrap(), "b");
656
657 let struct_c = MyStruct::new("c".to_string(), false);
658 assert_eq!(*struct_c.string.lock().unwrap(), "c");
659
660 ag._index = 123;
661 ag._name = "foo".into();
662 struct_a.process(&mut ag);
663
664 ag._index = 456;
665 ag._name = "bar".into();
666 struct_b.process(&mut ag);
667
668 ag._index = 789;
669 ag._name = "baz".into();
670 struct_c.process(&mut ag);
671
672 ag.join_and_ignore_errors();
673
674 assert_eq!(*struct_a.string.lock().unwrap(), "A");
675 assert_eq!(*struct_b.string.lock().unwrap(), "b");
676 assert_eq!(*struct_c.string.lock().unwrap(), "C");
677 }
678
679 #[test]
680 fn multiple_fail() {
681 let mut ag = AsyncGroup::new();
682
683 let struct_a = MyStruct::new("a".to_string(), true);
684 assert_eq!(*struct_a.string.lock().unwrap(), "a");
685
686 let struct_b = MyStruct::new("b".to_string(), true);
687 assert_eq!(*struct_b.string.lock().unwrap(), "b");
688
689 let struct_c = MyStruct::new("c".to_string(), true);
690 assert_eq!(*struct_c.string.lock().unwrap(), "c");
691
692 ag._index = 123;
693 ag._name = "foo".into();
694 struct_a.process(&mut ag);
695
696 ag._index = 456;
697 ag._name = "foo".into();
698 struct_b.process(&mut ag);
699
700 ag._index = 789;
701 ag._name = "foo".into();
702 struct_c.process(&mut ag);
703
704 ag.join_and_ignore_errors();
705
706 assert_eq!(*struct_a.string.lock().unwrap(), "a");
707 assert_eq!(*struct_b.string.lock().unwrap(), "b");
708 assert_eq!(*struct_c.string.lock().unwrap(), "c");
709 }
710 }
711}