base_coroutine/stack/
mod.rs1use std::error::Error;
9use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
10use std::io;
11use std::ops::Deref;
12use std::os::raw::c_void;
13
14mod sys;
15
16#[derive(Debug)]
18pub enum StackError {
19 ExceedsMaximumSize(usize),
21
22 IoError(io::Error),
24}
25
26impl Display for StackError {
27 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
28 match *self {
29 StackError::ExceedsMaximumSize(size) => {
30 write!(
31 fmt,
32 "Requested more than max size of {} bytes for a stack",
33 size
34 )
35 }
36 StackError::IoError(ref e) => std::fmt::Display::fmt(&e, fmt),
37 }
38 }
39}
40
41impl Error for StackError {
42 fn source(&self) -> Option<&(dyn Error + 'static)> {
43 match *self {
44 StackError::ExceedsMaximumSize(_) => None,
45 StackError::IoError(ref e) => Some(e),
46 }
47 }
48}
49
50#[derive(Debug)]
55pub struct Stack {
56 top: *mut c_void,
57 bottom: *mut c_void,
58}
59
60impl Stack {
61 #[inline]
66 pub(crate) unsafe fn new(top: *mut c_void, bottom: *mut c_void) -> Stack {
67 debug_assert!(top >= bottom);
68
69 Stack { top, bottom }
70 }
71
72 #[inline]
74 pub fn top(&self) -> *mut c_void {
75 self.top
76 }
77
78 #[inline]
80 pub fn bottom(&self) -> *mut c_void {
81 self.bottom
82 }
83
84 #[inline]
86 pub fn len(&self) -> usize {
87 self.top as usize - self.bottom as usize
88 }
89
90 #[inline]
91 pub fn is_empty(&self) -> bool {
92 self.len() == 0
93 }
94
95 #[inline]
97 pub fn min_size() -> usize {
98 sys::min_stack_size()
99 }
100
101 #[inline]
103 pub fn max_size() -> usize {
104 sys::max_stack_size(true)
105 }
106
107 #[inline]
113 pub fn default_size() -> usize {
114 sys::default_stack_size()
115 }
116
117 fn allocate(mut size: usize) -> Result<Stack, StackError> {
119 let page_size = sys::page_size();
120 let min_stack_size = sys::min_stack_size();
121 let max_stack_size = sys::max_stack_size(false);
122 let add = page_size << 1;
125
126 if size < min_stack_size {
127 size = min_stack_size;
128 }
129
130 size = (size - 1) & !(page_size - 1);
131
132 if let Some(size) = size.checked_add(add) {
133 if size <= max_stack_size {
134 let mut ret = unsafe { sys::allocate_stack(size) };
135
136 if let Ok(stack) = ret {
137 ret = unsafe { sys::protect_stack(&stack) };
138 }
139
140 return ret.map_err(StackError::IoError);
141 }
142 }
143
144 Err(StackError::ExceedsMaximumSize(max_stack_size - add))
145 }
146}
147
148unsafe impl Send for Stack {}
149
150#[derive(Debug)]
161pub struct ProtectedFixedSizeStack(Stack);
162
163impl ProtectedFixedSizeStack {
164 pub fn new(size: usize) -> Result<ProtectedFixedSizeStack, StackError> {
169 Stack::allocate(size).map(ProtectedFixedSizeStack)
170 }
171}
172
173impl Deref for ProtectedFixedSizeStack {
174 type Target = Stack;
175
176 fn deref(&self) -> &Stack {
177 &self.0
178 }
179}
180
181impl Default for ProtectedFixedSizeStack {
182 fn default() -> ProtectedFixedSizeStack {
183 ProtectedFixedSizeStack::new(Stack::default_size()).unwrap_or_else(|err| {
184 panic!("Failed to allocate ProtectedFixedSizeStack with {:?}", err)
185 })
186 }
187}
188
189impl Drop for ProtectedFixedSizeStack {
190 fn drop(&mut self) {
191 let page_size = sys::page_size();
192 let guard = (self.0.bottom() as usize - page_size) as *mut c_void;
193 let size_with_guard = self.0.len() + page_size;
194 unsafe {
195 sys::deallocate_stack(guard, size_with_guard);
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn stack_size_too_small() {
206 let stack = ProtectedFixedSizeStack::new(0).unwrap();
207 assert_eq!(stack.len(), Stack::min_size());
208 unsafe { std::ptr::write_bytes(stack.bottom() as *mut u8, 0x1d, stack.len()) };
209 }
210
211 #[test]
212 fn stack_size_too_large() {
213 let stack_size = Stack::max_size();
214
215 match ProtectedFixedSizeStack::new(stack_size) {
216 Err(StackError::ExceedsMaximumSize(..)) => panic!(),
217 _ => {}
218 }
219
220 let stack_size = stack_size + 1;
221
222 match ProtectedFixedSizeStack::new(stack_size) {
223 Err(StackError::ExceedsMaximumSize(..)) => {}
224 _ => panic!(),
225 }
226 }
227}