@@ -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
}
@@ -276,6 +280,20 @@ impl Drop for Thread {
276
280
}
277
281
}
278
282
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
+
279
297
pub fn available_parallelism ( ) -> io:: Result < NonZeroUsize > {
280
298
cfg_if:: cfg_if! {
281
299
if #[ cfg( any(
@@ -902,3 +920,28 @@ fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
902
920
fn min_stack_size ( _: * const libc:: pthread_attr_t ) -> usize {
903
921
2048 // just a guess
904
922
}
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