@@ -7,7 +7,7 @@ use crate::sys::weak::dlsym;
7
7
use crate :: sys:: weak:: weak;
8
8
use crate :: sys:: { os, stack_overflow} ;
9
9
use crate :: time:: { Duration , Instant } ;
10
- use crate :: { cmp, io, ptr} ;
10
+ use crate :: { cmp, fs , io, ptr} ;
11
11
#[ cfg( not( any(
12
12
target_os = "l4re" ,
13
13
target_os = "vxworks" ,
@@ -405,6 +405,42 @@ fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_W
405
405
result
406
406
}
407
407
408
+ #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
409
+ fn count_user_threads ( ) -> Result < usize , io:: Error > {
410
+ let current_uid = unsafe { libc:: getuid ( ) } ;
411
+ let mut thread_count = 0 ;
412
+
413
+ for entry in fs:: read_dir ( "/proc" ) ? {
414
+ let entry = entry?;
415
+ let pid = entry. file_name ( ) . to_string_lossy ( ) . to_string ( ) ;
416
+
417
+ if let Ok ( _pid_num) = pid. parse :: < u32 > ( ) {
418
+ let status_path = format ! ( "/proc/{}/status" , pid) ;
419
+
420
+ if let Ok ( status) = fs:: read_to_string ( status_path) {
421
+ let mut uid: Option < libc:: uid_t > = None ;
422
+ let mut threads: Option < usize > = None ;
423
+
424
+ for line in status. lines ( ) {
425
+ if line. starts_with ( "Uid:" ) {
426
+ uid = line. split_whitespace ( ) . nth ( 1 ) . and_then ( |s| s. parse ( ) . ok ( ) ) ;
427
+ } else if line. starts_with ( "Threads:" ) {
428
+ threads = line. split_whitespace ( ) . nth ( 1 ) . and_then ( |s| s. parse ( ) . ok ( ) ) ;
429
+ }
430
+ }
431
+
432
+ if let ( Some ( uid) , Some ( t) ) = ( uid, threads) {
433
+ if uid == current_uid {
434
+ thread_count += t;
435
+ }
436
+ }
437
+ }
438
+ }
439
+ }
440
+
441
+ Ok ( thread_count)
442
+ }
443
+
408
444
pub fn available_parallelism ( ) -> io:: Result < NonZero < usize > > {
409
445
cfg_if:: cfg_if! {
410
446
if #[ cfg( any(
@@ -421,6 +457,10 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> {
421
457
#[ allow( unused_mut) ]
422
458
let mut quota = usize :: MAX ;
423
459
460
+ #[ allow( unused_assignments) ]
461
+ #[ allow( unused_mut) ]
462
+ let mut ulimit = libc:: rlim_t:: MAX ;
463
+
424
464
#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
425
465
{
426
466
quota = cgroups:: quota( ) . max( 1 ) ;
@@ -439,14 +479,30 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> {
439
479
}
440
480
}
441
481
}
482
+
483
+ let mut r: libc:: rlimit = unsafe { mem:: zeroed( ) } ;
484
+ unsafe {
485
+ if libc:: getrlimit( libc:: RLIMIT_NPROC , & mut r) == 0 {
486
+ match r. rlim_cur {
487
+ libc:: RLIM_INFINITY => ulimit = libc:: rlim_t:: MAX ,
488
+ soft_limit => {
489
+ ulimit = match count_user_threads( ) {
490
+ Ok ( t) => if soft_limit > t as libc:: rlim_t { soft_limit - t as libc:: rlim_t} else { 1 } ,
491
+ _ => 1
492
+ }
493
+ }
494
+ }
495
+ }
496
+ }
442
497
}
498
+
443
499
match unsafe { libc:: sysconf( libc:: _SC_NPROCESSORS_ONLN) } {
444
500
-1 => Err ( io:: Error :: last_os_error( ) ) ,
445
501
0 => Err ( io:: Error :: UNKNOWN_THREAD_COUNT ) ,
446
502
cpus => {
447
503
let count = cpus as usize ;
448
504
// Cover the unusual situation where we were able to get the quota but not the affinity mask
449
- let count = count. min( quota) ;
505
+ let count = count. min( quota. min ( ulimit . try_into ( ) . unwrap_or ( usize :: MAX ) ) ) ;
450
506
Ok ( unsafe { NonZero :: new_unchecked( count) } )
451
507
}
452
508
}
0 commit comments