Skip to content

Commit 14a338e

Browse files
committed
Add tests and example for BreakLoop handle
* Added unit tests to verify handle validity and behavior after capture drop or lock poisoning. * Introduced examples/break_loop.rs demonstrating thread‑safe capture abort using breakloop_handle().
1 parent f2d60f4 commit 14a338e

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

examples/breakloop.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use std::{thread, time::Duration};
2+
3+
fn main() {
4+
// Get the default device
5+
let device = pcap::Device::lookup()
6+
.expect("device lookup failed")
7+
.expect("no device available");
8+
println!("Using device {}", device.name);
9+
10+
// Setup capture
11+
let mut cap = pcap::Capture::from_device(device)
12+
.unwrap()
13+
.immediate_mode(true)
14+
.open()
15+
.unwrap();
16+
println!("Using device");
17+
18+
let break_handle = cap.breakloop_handle();
19+
20+
// Start capture in a seperate thread
21+
let capture_thread = thread::spawn(move || {
22+
while let Ok(_) = cap.next_packet() {
23+
println!("got packet!");
24+
}
25+
println!("capture loop exited");
26+
});
27+
28+
// Send break_handle to a separate thread (e.g. user input, signal handler, etc.)
29+
thread::spawn(move || {
30+
thread::sleep(Duration::from_secs(1));
31+
println!("break loop called!");
32+
break_handle.breakloop();
33+
});
34+
35+
capture_thread.join().unwrap();
36+
}

src/capture/activated/mod.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,63 @@ mod tests {
680680
}
681681
}
682682

683+
#[test]
684+
fn test_breakloop_capture_dropped() {
685+
let _m = RAWMTX.lock();
686+
687+
let mut value: isize = 1234;
688+
let pcap = as_pcap_t(&mut value);
689+
690+
let test_capture = test_capture::<Active>(pcap);
691+
let mut capture: Capture<dyn Activated> = test_capture.capture.into();
692+
693+
let ctx = raw::pcap_breakloop_context();
694+
ctx.expect()
695+
.withf_st(move |h| *h == pcap)
696+
.return_const(())
697+
.times(1);
698+
699+
let break_handle = capture.breakloop_handle();
700+
701+
break_handle.breakloop();
702+
assert!(*break_handle.handle_valid.read().unwrap());
703+
704+
drop(capture);
705+
706+
break_handle.breakloop(); // this call does not trigger mock after drop
707+
assert!(!*break_handle.handle_valid.read().unwrap());
708+
}
709+
710+
#[test]
711+
fn test_breakloop_poison_lock() {
712+
let _m = RAWMTX.lock();
713+
714+
let mut value: isize = 1234;
715+
let pcap = as_pcap_t(&mut value);
716+
717+
let test_capture = test_capture::<Active>(pcap);
718+
let mut capture: Capture<dyn Activated> = test_capture.capture.into();
719+
720+
let ctx = raw::pcap_breakloop_context();
721+
ctx.expect()
722+
.withf_st(move |h| *h == pcap)
723+
.return_const(())
724+
.times(1);
725+
726+
let break_handle = capture.breakloop_handle();
727+
728+
break_handle.breakloop();
729+
assert!(*break_handle.handle_valid.read().unwrap());
730+
731+
let _ = catch_unwind(|| {
732+
let _guard = break_handle.handle_valid.write().unwrap();
733+
panic!("panic to poison lock");
734+
});
735+
736+
assert!(break_handle.handle_valid.is_poisoned());
737+
break_handle.breakloop(); // this call does not trigger mock after poison
738+
}
739+
683740
#[test]
684741
fn test_savefile() {
685742
let _m = RAWMTX.lock();

src/capture/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,33 @@ mod tests {
258258
assert_eq!(capture.as_ptr(), capture.handle.as_ptr());
259259
}
260260

261+
#[test]
262+
fn test_capture_drop_poison() {
263+
let _m = RAWMTX.lock();
264+
265+
let mut value: isize = 1234;
266+
let pcap = as_pcap_t(&mut value);
267+
268+
let test_capture = test_capture::<Active>(pcap);
269+
let capture: Capture<dyn Activated> = test_capture.capture.into();
270+
271+
let lock = Arc::clone(&capture.handle_valid);
272+
let _ = std::panic::catch_unwind(|| {
273+
let _guard = capture.handle_valid.write().unwrap();
274+
panic!("panic to poison lock");
275+
});
276+
277+
drop(capture);
278+
279+
// Ensure handle_valid is set to false on drop even when poisoned
280+
let handle_valid = *lock
281+
.read()
282+
.unwrap_or_else(|e| e.into_inner());
283+
284+
assert!(lock.is_poisoned());
285+
assert!(!handle_valid);
286+
}
287+
261288
#[test]
262289
#[cfg(windows)]
263290
fn test_min_to_copy() {

0 commit comments

Comments
 (0)