@@ -132,8 +132,11 @@ impl Thread {
132
132
133
133
#[ cfg( target_os = "linux" ) ]
134
134
pub fn set_name ( name : & CStr ) {
135
+ const TASK_COMM_LEN : usize = 16 ;
136
+
135
137
unsafe {
136
138
// Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20.
139
+ let name = truncate_cstr ( name, TASK_COMM_LEN ) ;
137
140
libc:: pthread_setname_np ( libc:: pthread_self ( ) , name. as_ptr ( ) ) ;
138
141
}
139
142
}
@@ -148,6 +151,7 @@ impl Thread {
148
151
#[ cfg( any( target_os = "macos" , target_os = "ios" , target_os = "watchos" ) ) ]
149
152
pub fn set_name ( name : & CStr ) {
150
153
unsafe {
154
+ let name = truncate_cstr ( name, libc:: MAXTHREADNAMESIZE ) ;
151
155
libc:: pthread_setname_np ( name. as_ptr ( ) ) ;
152
156
}
153
157
}
@@ -277,6 +281,20 @@ impl Drop for Thread {
277
281
}
278
282
}
279
283
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
+
280
298
pub fn available_parallelism ( ) -> io:: Result < NonZeroUsize > {
281
299
cfg_if:: cfg_if! {
282
300
if #[ cfg( any(
@@ -893,3 +911,28 @@ fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
893
911
fn min_stack_size ( _: * const libc:: pthread_attr_t ) -> usize {
894
912
2048 // just a guess
895
913
}
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