1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Abstraction of `STDIN` into iterator of [`String`] wrapped in [`rc::Rc`]
use std::{
io, rc,
sync::atomic::{AtomicBool, Ordering::Relaxed},
};
/// Errors possible during [`PipeInIterator::try_new`]
#[derive(Debug)]
pub enum Error {
/// A [`PipeInIterator`] has already been constructed
AlreadyConstructed,
}
/// Abstraction of `STDIN` into iterator of [`String`] wrapped in [`rc::Rc`]
///
/// Example
/// -------
///
/// ```no_run
#[doc = include_str!("examples/pipe_in.rs")]
/// ```
pub struct PipeInIterator<'a> {
_stdin: *mut io::Stdin,
lock: io::StdinLock<'a>,
string_buffer: rc::Rc<String>,
}
impl<'a> PipeInIterator<'a> {
/// Initialize [`PipeInIterator`]
///
/// # Warning
///
/// If `STDIN` is already locked the thread may block until the previous
/// lock is released.
///
/// # Errors
///
/// This function should error if the lock is already held by the current
/// thread
///
/// # Panics
///
/// This function might panic when called if the lock is already held by
/// the current thread.
///
/// (See [`std::sync::Mutex`])
pub fn try_new() -> Result<PipeInIterator<'a>, Error> {
match set_lock_state() {
Ok(false) => (), // If old value was `false` and able to be changed to `true` then the function can run
Err(true) => return Err(Error::AlreadyConstructed), // If the value couldn't be written because the value was already true
Ok(true) | Err(false) => {
unreachable!("1669400008 - compare_exchange(false, true, ...)")
}
}
// Ignore the borrow checker by hiding STDIN behind a pointer
let buffer = String::new();
let stdin = Box::into_raw(Box::new(io::stdin()));
Ok(Self {
lock: unsafe { &*stdin }.lock(),
_stdin: stdin,
string_buffer: rc::Rc::new(buffer),
})
}
}
impl<'a> Iterator for PipeInIterator<'a> {
type Item = rc::Rc<String>;
fn next(&mut self) -> Option<Self::Item> {
use io::BufRead;
let data = rc::Rc::make_mut(&mut self.string_buffer);
data.clear();
let read_line_res = self.lock.read_line(data);
match read_line_res {
Ok(0) => None,
Ok(_) => {
data.truncate(data.trim_end().len());
Some(self.string_buffer.clone())
}
Err(error) => {
println!("error: {error}");
None
}
}
}
}
impl<'a> Drop for PipeInIterator<'a> {
fn drop(&mut self) {
release_lock_state();
}
}
static mut LOCKED: AtomicBool = AtomicBool::new(false);
fn set_lock_state() -> Result<bool, bool> {
// https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html#method.compare_exchange
unsafe { LOCKED.compare_exchange(false, true, Relaxed, Relaxed) }
}
fn release_lock_state() {
unsafe { LOCKED.store(false, Relaxed) }
}
#[cfg(test)]
mod test {
use super::*;
fn check_lock_state() -> bool {
unsafe { LOCKED.load(Relaxed) }
}
#[test]
fn test1656796720() {
assert!(!check_lock_state());
assert!(!check_lock_state());
set_lock_state().expect("1669403390");
assert!(check_lock_state());
assert!(check_lock_state());
release_lock_state();
assert!(!check_lock_state());
assert!(!check_lock_state());
}
}