@@ -47,7 +47,10 @@ use std::cell::Cell;
47
47
pub fn maybe_grow < R , F : FnOnce ( ) -> R > ( red_zone : usize , stack_size : usize , callback : F ) -> R {
48
48
// if we can't guess the remaining stack (unsupported on some platforms) we immediately grow
49
49
// the stack and then cache the new stack size (which we do know now because we allocated it.
50
- let enough_space = remaining_stack ( ) . map_or ( false , |remaining| remaining >= red_zone) ;
50
+ let enough_space = match remaining_stack ( ) {
51
+ Some ( remaining) => remaining >= red_zone,
52
+ None => false ,
53
+ } ;
51
54
if enough_space {
52
55
callback ( )
53
56
} else {
@@ -59,11 +62,24 @@ pub fn maybe_grow<R, F: FnOnce() -> R>(red_zone: usize, stack_size: usize, callb
59
62
/// The closure will still be on the same thread as the caller of `grow`.
60
63
/// This will allocate a new stack with at least `stack_size` bytes.
61
64
pub fn grow < R , F : FnOnce ( ) -> R > ( stack_size : usize , callback : F ) -> R {
65
+ // To avoid monomorphizing `_grow()` and everything it calls,
66
+ // we convert the generic callback to a dynamic one.
67
+ let mut opt_callback = Some ( callback) ;
62
68
let mut ret = None ;
63
69
let ret_ref = & mut ret;
64
- _grow ( stack_size, move || {
65
- * ret_ref = Some ( callback ( ) ) ;
66
- } ) ;
70
+
71
+ // This wrapper around `callback` achieves two things:
72
+ // * It converts the `impl FnOnce` to a `dyn FnMut`.
73
+ // `dyn` because we want it to not be generic, and
74
+ // `FnMut` because we can't pass a `dyn FnOnce` around without boxing it.
75
+ // * It eliminates the generic return value, by writing it to the stack of this function.
76
+ // Otherwise the closure would have to return an unsized value, which isn't possible.
77
+ let dyn_callback: & mut dyn FnMut ( ) = & mut || {
78
+ let taken_callback = opt_callback. take ( ) . unwrap ( ) ;
79
+ * ret_ref = Some ( taken_callback ( ) ) ;
80
+ } ;
81
+
82
+ _grow ( stack_size, dyn_callback) ;
67
83
ret. unwrap ( )
68
84
}
69
85
@@ -201,7 +217,7 @@ psm_stack_manipulation! {
201
217
}
202
218
}
203
219
204
- fn _grow< F : FnOnce ( ) > ( stack_size: usize , callback: F ) {
220
+ fn _grow( stack_size: usize , callback: & mut dyn FnMut ( ) ) {
205
221
// Calculate a number of pages we want to allocate for the new stack.
206
222
// For maximum portability we want to produce a stack that is aligned to a page and has
207
223
// a size that’s a multiple of page size. Furthermore we want to allocate two extras pages
@@ -249,7 +265,7 @@ psm_stack_manipulation! {
249
265
250
266
no {
251
267
#[ cfg( not( windows) ) ]
252
- fn _grow< F : FnOnce ( ) > ( stack_size: usize , callback: F ) {
268
+ fn _grow( stack_size: usize , callback: & mut dyn FnMut ( ) ) {
253
269
drop( stack_size) ;
254
270
callback( ) ;
255
271
}
@@ -298,7 +314,7 @@ cfg_if! {
298
314
return ;
299
315
}
300
316
301
- fn _grow< F : FnOnce ( ) > ( stack_size: usize , callback: F ) {
317
+ fn _grow( stack_size: usize , callback: & mut dyn FnMut ( ) ) {
302
318
// Fibers (or stackful coroutines) is the only official way to create new stacks on the
303
319
// same thread on Windows. So in order to extend the stack we create fiber and switch
304
320
// to it so we can use it's stack. After running `callback` within our fiber, we switch
@@ -329,8 +345,8 @@ cfg_if! {
329
345
330
346
let fiber = CreateFiber (
331
347
stack_size as SIZE_T ,
332
- Some ( fiber_proc:: <F >) ,
333
- & mut data as * mut FiberInfo <F > as * mut _,
348
+ Some ( fiber_proc:: <& mut dyn FnMut ( ) >) ,
349
+ & mut data as * mut FiberInfo <& mut dyn FnMut ( ) > as * mut _,
334
350
) ;
335
351
if fiber. is_null( ) {
336
352
panic!( "unable to allocate fiber: {}" , io:: Error :: last_os_error( ) ) ;
0 commit comments