@@ -132,8 +132,11 @@ impl Thread {
132132
133133 #[ cfg( target_os = "linux" ) ]
134134 pub fn set_name ( name : & CStr ) {
135+ const TASK_COMM_LEN : usize = 16 ;
136+
135137 unsafe {
136138 // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20.
139+ let name = truncate_cstr ( name, TASK_COMM_LEN ) ;
137140 libc:: pthread_setname_np ( libc:: pthread_self ( ) , name. as_ptr ( ) ) ;
138141 }
139142 }
@@ -148,6 +151,7 @@ impl Thread {
148151 #[ cfg( any( target_os = "macos" , target_os = "ios" , target_os = "watchos" ) ) ]
149152 pub fn set_name ( name : & CStr ) {
150153 unsafe {
154+ let name = truncate_cstr ( name, libc:: MAXTHREADNAMESIZE ) ;
151155 libc:: pthread_setname_np ( name. as_ptr ( ) ) ;
152156 }
153157 }
@@ -276,6 +280,20 @@ impl Drop for Thread {
276280 }
277281}
278282
283+ #[ cfg( any( target_os = "linux" , target_os = "macos" , target_os = "ios" , target_os = "watchos" ) ) ]
284+ fn truncate_cstr ( cstr : & CStr , max_with_nul : usize ) -> crate :: borrow:: Cow < ' _ , CStr > {
285+ use crate :: { borrow:: Cow , ffi:: CString } ;
286+
287+ if cstr. to_bytes_with_nul ( ) . len ( ) > max_with_nul {
288+ let bytes = cstr. to_bytes ( ) [ ..max_with_nul - 1 ] . to_vec ( ) ;
289+ // SAFETY: the non-nul bytes came straight from a CStr.
290+ // (CString will add the terminating nul.)
291+ Cow :: Owned ( unsafe { CString :: from_vec_unchecked ( bytes) } )
292+ } else {
293+ Cow :: Borrowed ( cstr)
294+ }
295+ }
296+
279297pub fn available_parallelism ( ) -> io:: Result < NonZeroUsize > {
280298 cfg_if:: cfg_if! {
281299 if #[ cfg( any(
@@ -902,3 +920,28 @@ fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
902920fn min_stack_size ( _: * const libc:: pthread_attr_t ) -> usize {
903921 2048 // just a guess
904922}
923+
924+ #[ test]
925+ #[ cfg( any( target_os = "linux" , target_os = "macos" , target_os = "ios" , target_os = "watchos" ) ) ]
926+ fn test_named_thread_truncation ( ) {
927+ use crate :: thread:: { self , Builder } ;
928+
929+ let long_name = crate :: iter:: once ( "test_named_thread_truncation" )
930+ . chain ( crate :: iter:: repeat ( " yada" ) . take ( 100 ) )
931+ . collect :: < String > ( ) ;
932+
933+ let result = Builder :: new ( ) . name ( long_name. clone ( ) ) . spawn ( move || {
934+ // Rust remembers the full thread name itself.
935+ assert_eq ! ( thread:: current( ) . name( ) , Some ( long_name. as_str( ) ) ) ;
936+
937+ // But the kernel is limited -- make sure we successfully set a truncation.
938+ let mut buf = vec ! [ 0u8 ; long_name. len( ) + 1 ] ;
939+ unsafe {
940+ libc:: pthread_getname_np ( libc:: pthread_self ( ) , buf. as_mut_ptr ( ) . cast ( ) , buf. len ( ) ) ;
941+ }
942+ let cstr = CStr :: from_bytes_until_nul ( & buf) . unwrap ( ) ;
943+ assert ! ( cstr. to_bytes( ) . len( ) > 0 ) ;
944+ assert ! ( long_name. as_bytes( ) . starts_with( cstr. to_bytes( ) ) ) ;
945+ } ) ;
946+ result. unwrap ( ) . join ( ) . unwrap ( ) ;
947+ }
0 commit comments