@@ -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 }
@@ -277,6 +281,20 @@ impl Drop for Thread {
277281 }
278282}
279283
284+ #[ cfg( any( target_os = "linux" , target_os = "macos" , target_os = "ios" , target_os = "watchos" ) ) ]
285+ fn truncate_cstr ( cstr : & CStr , max_with_nul : usize ) -> crate :: borrow:: Cow < ' _ , CStr > {
286+ use crate :: { borrow:: Cow , ffi:: CString } ;
287+
288+ if cstr. to_bytes_with_nul ( ) . len ( ) > max_with_nul {
289+ let bytes = cstr. to_bytes ( ) [ ..max_with_nul - 1 ] . to_vec ( ) ;
290+ // SAFETY: the non-nul bytes came straight from a CStr.
291+ // (CString will add the terminating nul.)
292+ Cow :: Owned ( unsafe { CString :: from_vec_unchecked ( bytes) } )
293+ } else {
294+ Cow :: Borrowed ( cstr)
295+ }
296+ }
297+
280298pub fn available_parallelism ( ) -> io:: Result < NonZeroUsize > {
281299 cfg_if:: cfg_if! {
282300 if #[ cfg( any(
@@ -893,3 +911,28 @@ fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
893911fn min_stack_size ( _: * const libc:: pthread_attr_t ) -> usize {
894912 2048 // just a guess
895913}
914+
915+ #[ test]
916+ #[ cfg( any( target_os = "linux" , target_os = "macos" , target_os = "ios" , target_os = "watchos" ) ) ]
917+ fn test_named_thread_truncation ( ) {
918+ use crate :: thread:: { self , Builder } ;
919+
920+ let long_name = crate :: iter:: once ( "test_named_thread_truncation" )
921+ . chain ( crate :: iter:: repeat ( " yada" ) . take ( 100 ) )
922+ . collect :: < String > ( ) ;
923+
924+ let result = Builder :: new ( ) . name ( long_name. clone ( ) ) . spawn ( move || {
925+ // Rust remembers the full thread name itself.
926+ assert_eq ! ( thread:: current( ) . name( ) , Some ( long_name. as_str( ) ) ) ;
927+
928+ // But the kernel is limited -- make sure we successfully set a truncation.
929+ let mut buf = vec ! [ 0u8 ; long_name. len( ) + 1 ] ;
930+ unsafe {
931+ libc:: pthread_getname_np ( libc:: pthread_self ( ) , buf. as_mut_ptr ( ) . cast ( ) , buf. len ( ) ) ;
932+ }
933+ let cstr = CStr :: from_bytes_until_nul ( & buf) . unwrap ( ) ;
934+ assert ! ( cstr. to_bytes( ) . len( ) > 0 ) ;
935+ assert ! ( long_name. as_bytes( ) . starts_with( cstr. to_bytes( ) ) ) ;
936+ } ) ;
937+ result. unwrap ( ) . join ( ) . unwrap ( ) ;
938+ }
0 commit comments