@@ -82,6 +82,7 @@ cfg_if! {
82
82
fn __stacker_switch_stacks( dataptr: * mut u8 ,
83
83
fnptr: * const u8 ,
84
84
new_stack: usize ) ;
85
+ fn getpagesize( ) -> libc:: c_int;
85
86
}
86
87
87
88
thread_local! {
@@ -99,19 +100,70 @@ cfg_if! {
99
100
STACK_LIMIT . with( |s| s. set( l) )
100
101
}
101
102
103
+ struct StackSwitch {
104
+ map: * mut libc:: c_void,
105
+ stack_size: usize ,
106
+ old_stack_limit: usize ,
107
+ }
108
+
109
+ impl Drop for StackSwitch {
110
+ fn drop( & mut self ) {
111
+ unsafe {
112
+ libc:: munmap( self . map, self . stack_size) ;
113
+ }
114
+ set_stack_limit( self . old_stack_limit) ;
115
+ }
116
+ }
117
+
102
118
fn _grow( stack_size: usize , mut f: & mut FnMut ( ) ) {
103
- // Align to 16-bytes (see below for why)
104
- let stack_size = ( stack_size + 15 ) / 16 * 16 ;
119
+ let page_size = unsafe { getpagesize( ) } as usize ;
105
120
106
- // Allocate some new stack for oureslves
107
- let mut stack = Vec :: <u8 >:: with_capacity( stack_size) ;
108
- let new_limit = stack. as_ptr( ) as usize + 32 * 1024 ;
121
+ // Round the stack size up to a multiple of page_size
122
+ let rem = stack_size % page_size;
123
+ let stack_size = if rem == 0 {
124
+ stack_size
125
+ } else {
126
+ stack_size. checked_add( ( page_size - rem) )
127
+ . expect( "stack size calculation overflowed" )
128
+ } ;
109
129
110
- // Save off the old stack limits
111
- let old_limit = get_stack_limit( ) ;
130
+ // We need at least 2 page
131
+ let stack_size = std:: cmp:: max( stack_size, page_size) ;
132
+
133
+ // Add a guard page
134
+ let stack_size = stack_size. checked_add( page_size)
135
+ . expect( "stack size calculation overflowed" ) ;
136
+
137
+ // Allocate some new stack for ourselves
138
+ let map = unsafe {
139
+ libc:: mmap( std:: ptr:: null_mut( ) ,
140
+ stack_size,
141
+ libc:: PROT_NONE ,
142
+ libc:: MAP_PRIVATE |
143
+ libc:: MAP_ANON ,
144
+ 0 ,
145
+ 0 )
146
+ } ;
147
+ if map == -1isize as _ {
148
+ panic!( "unable to allocate stack" )
149
+ }
150
+ let _switch = StackSwitch {
151
+ map,
152
+ stack_size,
153
+ old_stack_limit: get_stack_limit( ) ,
154
+ } ;
155
+ let result = unsafe {
156
+ libc:: mprotect( ( map as usize + page_size) as * mut libc:: c_void,
157
+ stack_size - page_size,
158
+ libc:: PROT_READ | libc:: PROT_WRITE )
159
+ } ;
160
+ if result == -1 {
161
+ panic!( "unable to set stack permissions" )
162
+ }
163
+ let stack_low = map as usize ;
112
164
113
165
// Prepare stack limits for the stack switch
114
- set_stack_limit( new_limit ) ;
166
+ set_stack_limit( stack_low ) ;
115
167
116
168
// Make sure the stack is 16-byte aligned which should be enough for all
117
169
// platforms right now. Allocations on 64-bit are already 16-byte aligned
@@ -127,12 +179,10 @@ cfg_if! {
127
179
unsafe {
128
180
__stacker_switch_stacks( & mut f as * mut & mut FnMut ( ) as * mut u8 ,
129
181
doit as usize as * const _,
130
- stack . as_mut_ptr ( ) as usize + stack_size - offset) ;
182
+ stack_low + stack_size - offset) ;
131
183
}
132
184
133
- // Once we've returned reset bothe stack limits and then return value same
134
- // value the closure returned.
135
- set_stack_limit( old_limit) ;
185
+ // Dropping `switch` frees the memory mapping and restores the old stack limit
136
186
}
137
187
}
138
188
}
@@ -181,11 +231,11 @@ cfg_if! {
181
231
} ,
182
232
} ;
183
233
if info. parent_fiber == 0i32 as _ {
184
- panic!( "Unable to convert thread to fiber" ) ;
234
+ panic!( "unable to convert thread to fiber" ) ;
185
235
}
186
236
let fiber = kernel32:: CreateFiber ( stack_size as _, Some ( fiber_proc) , & mut info as * mut FiberInfo as * mut _) ;
187
237
if fiber == 0i32 as _ {
188
- panic!( "Unable to allocate fiber" ) ;
238
+ panic!( "unable to allocate fiber" ) ;
189
239
}
190
240
kernel32:: SwitchToFiber ( fiber) ;
191
241
kernel32:: DeleteFiber ( fiber) ;
@@ -253,8 +303,6 @@ cfg_if! {
253
303
stackaddr as usize
254
304
}
255
305
} else if #[ cfg( target_os = "macos" ) ] {
256
- use libc:: { c_void, pthread_t, size_t} ;
257
-
258
306
unsafe fn guess_os_stack_limit( ) -> usize {
259
307
libc:: pthread_get_stackaddr_np( libc:: pthread_self( ) ) as usize -
260
308
libc:: pthread_get_stacksize_np( libc:: pthread_self( ) ) as usize
0 commit comments