@@ -32,6 +32,8 @@ extern crate windows_sys;
32
32
#[ macro_use]
33
33
extern crate psm;
34
34
35
+ mod backends;
36
+
35
37
use std:: cell:: Cell ;
36
38
37
39
/// Grows the call stack if necessary.
@@ -116,7 +118,7 @@ psm_stack_information!(
116
118
117
119
thread_local ! {
118
120
static STACK_LIMIT : Cell <Option <usize >> = Cell :: new( unsafe {
119
- guess_os_stack_limit( )
121
+ backends :: guess_os_stack_limit( )
120
122
} )
121
123
}
122
124
@@ -163,10 +165,12 @@ psm_stack_manipulation! {
163
165
-1 , // Some implementations assert fd = -1 if MAP_ANON is specified
164
166
0
165
167
) ;
166
- if new_stack == libc:: MAP_FAILED {
167
- let error = std:: io:: Error :: last_os_error( ) ;
168
- panic!( "allocating stack failed with: {}" , error)
169
- }
168
+ assert_ne!(
169
+ new_stack,
170
+ libc:: MAP_FAILED ,
171
+ "mmap failed to allocate stack: {}" ,
172
+ std:: io:: Error :: last_os_error( )
173
+ ) ;
170
174
let guard = StackRestoreGuard {
171
175
new_stack,
172
176
stack_bytes,
@@ -191,11 +195,12 @@ psm_stack_manipulation! {
191
195
} else {
192
196
-1
193
197
} ;
194
- if result == -1 {
195
- let error = std:: io:: Error :: last_os_error( ) ;
196
- drop( guard) ;
197
- panic!( "setting stack permissions failed with: {}" , error)
198
- }
198
+ assert_ne!(
199
+ result,
200
+ -1 ,
201
+ "mprotect/mmap failed: {}" ,
202
+ std:: io:: Error :: last_os_error( )
203
+ ) ;
199
204
guard
200
205
}
201
206
}
@@ -271,191 +276,7 @@ psm_stack_manipulation! {
271
276
let _ = stack_size;
272
277
callback( ) ;
273
278
}
274
- }
275
- }
276
-
277
- cfg_if ! {
278
- if #[ cfg( miri) ] {
279
- // Miri doesn't have a stack limit
280
- #[ inline( always) ]
281
- unsafe fn guess_os_stack_limit( ) -> Option <usize > {
282
- None
283
- }
284
- } else if #[ cfg( windows) ] {
285
- use std:: ptr;
286
- use std:: io;
287
- use libc:: c_void;
288
- use windows_sys:: Win32 :: System :: Threading :: { SwitchToFiber , IsThreadAFiber , ConvertThreadToFiber ,
289
- CreateFiber , DeleteFiber , ConvertFiberToThread , SetThreadStackGuarantee
290
- } ;
291
- use windows_sys:: Win32 :: Foundation :: BOOL ;
292
- use windows_sys:: Win32 :: System :: Memory :: VirtualQuery ;
293
-
294
- // Make sure the libstacker.a (implemented in C) is linked.
295
- // See https://github.com/rust-lang/rust/issues/65610
296
- #[ link( name="stacker" ) ]
297
- extern {
298
- fn __stacker_get_current_fiber( ) -> * mut c_void;
299
- }
300
-
301
- struct FiberInfo <F > {
302
- callback: std:: mem:: MaybeUninit <F >,
303
- panic: Option <Box <dyn std:: any:: Any + Send + ' static >>,
304
- parent_fiber: * mut c_void,
305
- }
306
-
307
- unsafe extern "system" fn fiber_proc<F : FnOnce ( ) >( data: * mut c_void) {
308
- // This function is the entry point to our inner fiber, and as argument we get an
309
- // instance of `FiberInfo`. We will set-up the "runtime" for the callback and execute
310
- // it.
311
- let data = & mut * ( data as * mut FiberInfo <F >) ;
312
- let old_stack_limit = get_stack_limit( ) ;
313
- set_stack_limit( guess_os_stack_limit( ) ) ;
314
- let callback = data. callback. as_ptr( ) ;
315
- data. panic = std:: panic:: catch_unwind( std:: panic:: AssertUnwindSafe ( callback. read( ) ) ) . err( ) ;
316
-
317
- // Restore to the previous Fiber
318
- set_stack_limit( old_stack_limit) ;
319
- SwitchToFiber ( data. parent_fiber) ;
320
- }
321
-
322
- fn _grow( stack_size: usize , callback: & mut dyn FnMut ( ) ) {
323
- // Fibers (or stackful coroutines) is the only official way to create new stacks on the
324
- // same thread on Windows. So in order to extend the stack we create fiber and switch
325
- // to it so we can use it's stack. After running `callback` within our fiber, we switch
326
- // back to the current stack and destroy the fiber and its associated stack.
327
- unsafe {
328
- let was_fiber = IsThreadAFiber ( ) == 1 as BOOL ;
329
- let mut data = FiberInfo {
330
- callback: std:: mem:: MaybeUninit :: new( callback) ,
331
- panic: None ,
332
- parent_fiber: {
333
- if was_fiber {
334
- // Get a handle to the current fiber. We need to use a C implementation
335
- // for this as GetCurrentFiber is an header only function.
336
- __stacker_get_current_fiber( )
337
- } else {
338
- // Convert the current thread to a fiber, so we are able to switch back
339
- // to the current stack. Threads coverted to fibers still act like
340
- // regular threads, but they have associated fiber data. We later
341
- // convert it back to a regular thread and free the fiber data.
342
- ConvertThreadToFiber ( ptr:: null_mut( ) )
343
- }
344
- } ,
345
- } ;
346
-
347
- if data. parent_fiber. is_null( ) {
348
- panic!( "unable to convert thread to fiber: {}" , io:: Error :: last_os_error( ) ) ;
349
- }
350
-
351
- let fiber = CreateFiber (
352
- stack_size as usize ,
353
- Some ( fiber_proc:: <& mut dyn FnMut ( ) >) ,
354
- & mut data as * mut FiberInfo <& mut dyn FnMut ( ) > as * mut _,
355
- ) ;
356
- if fiber. is_null( ) {
357
- panic!( "unable to allocate fiber: {}" , io:: Error :: last_os_error( ) ) ;
358
- }
359
-
360
- // Switch to the fiber we created. This changes stacks and starts executing
361
- // fiber_proc on it. fiber_proc will run `callback` and then switch back to run the
362
- // next statement.
363
- SwitchToFiber ( fiber) ;
364
- DeleteFiber ( fiber) ;
365
-
366
- // Clean-up.
367
- if !was_fiber && ConvertFiberToThread ( ) == 0 {
368
- // FIXME: Perhaps should not panic here?
369
- panic!( "unable to convert back to thread: {}" , io:: Error :: last_os_error( ) ) ;
370
- }
371
-
372
- if let Some ( p) = data. panic {
373
- std:: panic:: resume_unwind( p) ;
374
- }
375
- }
376
- }
377
-
378
- #[ inline( always) ]
379
- fn get_thread_stack_guarantee( ) -> usize {
380
- let min_guarantee = if cfg!( target_pointer_width = "32" ) {
381
- 0x1000
382
- } else {
383
- 0x2000
384
- } ;
385
- let mut stack_guarantee = 0 ;
386
- unsafe {
387
- // Read the current thread stack guarantee
388
- // This is the stack reserved for stack overflow
389
- // exception handling.
390
- // This doesn't return the true value so we need
391
- // some further logic to calculate the real stack
392
- // guarantee. This logic is what is used on x86-32 and
393
- // x86-64 Windows 10. Other versions and platforms may differ
394
- SetThreadStackGuarantee ( & mut stack_guarantee)
395
- } ;
396
- std:: cmp:: max( stack_guarantee, min_guarantee) as usize + 0x1000
397
- }
398
-
399
- #[ inline( always) ]
400
- unsafe fn guess_os_stack_limit( ) -> Option <usize > {
401
- // Query the allocation which contains our stack pointer in order
402
- // to discover the size of the stack
403
- //
404
- // FIXME: we could read stack base from the TIB, specifically the 3rd element of it.
405
- type QueryT = windows_sys:: Win32 :: System :: Memory :: MEMORY_BASIC_INFORMATION ;
406
- let mut mi = std:: mem:: MaybeUninit :: <QueryT >:: uninit( ) ;
407
- VirtualQuery (
408
- psm:: stack_pointer( ) as * const _,
409
- mi. as_mut_ptr( ) ,
410
- std:: mem:: size_of:: <QueryT >( ) as usize ,
411
- ) ;
412
- Some ( mi. assume_init( ) . AllocationBase as usize + get_thread_stack_guarantee( ) + 0x1000 )
413
- }
414
- } else if #[ cfg( any( target_os = "linux" , target_os="solaris" , target_os = "netbsd" ) ) ] {
415
- unsafe fn guess_os_stack_limit( ) -> Option <usize > {
416
- let mut attr = std:: mem:: MaybeUninit :: <libc:: pthread_attr_t>:: uninit( ) ;
417
- assert_eq!( libc:: pthread_attr_init( attr. as_mut_ptr( ) ) , 0 ) ;
418
- assert_eq!( libc:: pthread_getattr_np( libc:: pthread_self( ) ,
419
- attr. as_mut_ptr( ) ) , 0 ) ;
420
- let mut stackaddr = std:: ptr:: null_mut( ) ;
421
- let mut stacksize = 0 ;
422
- assert_eq!( libc:: pthread_attr_getstack(
423
- attr. as_ptr( ) , & mut stackaddr, & mut stacksize
424
- ) , 0 ) ;
425
- assert_eq!( libc:: pthread_attr_destroy( attr. as_mut_ptr( ) ) , 0 ) ;
426
- Some ( stackaddr as usize )
427
- }
428
- } else if #[ cfg( any( target_os = "freebsd" , target_os = "dragonfly" , target_os = "illumos" ) ) ] {
429
- unsafe fn guess_os_stack_limit( ) -> Option <usize > {
430
- let mut attr = std:: mem:: MaybeUninit :: <libc:: pthread_attr_t>:: uninit( ) ;
431
- assert_eq!( libc:: pthread_attr_init( attr. as_mut_ptr( ) ) , 0 ) ;
432
- assert_eq!( libc:: pthread_attr_get_np( libc:: pthread_self( ) , attr. as_mut_ptr( ) ) , 0 ) ;
433
- let mut stackaddr = std:: ptr:: null_mut( ) ;
434
- let mut stacksize = 0 ;
435
- assert_eq!( libc:: pthread_attr_getstack(
436
- attr. as_ptr( ) , & mut stackaddr, & mut stacksize
437
- ) , 0 ) ;
438
- assert_eq!( libc:: pthread_attr_destroy( attr. as_mut_ptr( ) ) , 0 ) ;
439
- Some ( stackaddr as usize )
440
- }
441
- } else if #[ cfg( target_os = "openbsd" ) ] {
442
- unsafe fn guess_os_stack_limit( ) -> Option <usize > {
443
- let mut stackinfo = std:: mem:: MaybeUninit :: <libc:: stack_t>:: uninit( ) ;
444
- assert_eq!( libc:: pthread_stackseg_np( libc:: pthread_self( ) , stackinfo. as_mut_ptr( ) ) , 0 ) ;
445
- Some ( stackinfo. assume_init( ) . ss_sp as usize - stackinfo. assume_init( ) . ss_size)
446
- }
447
- } else if #[ cfg( target_os = "macos" ) ] {
448
- unsafe fn guess_os_stack_limit( ) -> Option <usize > {
449
- Some ( libc:: pthread_get_stackaddr_np( libc:: pthread_self( ) ) as usize -
450
- libc:: pthread_get_stacksize_np( libc:: pthread_self( ) ) as usize )
451
- }
452
- } else {
453
- // fallback for other platforms is to always increase the stack if we're on
454
- // the root stack. After we increased the stack once, we know the new stack
455
- // size and don't need this pessimization anymore
456
- #[ inline( always) ]
457
- unsafe fn guess_os_stack_limit( ) -> Option <usize > {
458
- None
459
- }
279
+ #[ cfg( windows) ]
280
+ use backends:: windows:: _grow;
460
281
}
461
282
}
0 commit comments