@@ -29,12 +29,23 @@ cfg_langinfo! {
2929 use std:: ffi:: CStr ;
3030 use std:: sync:: OnceLock ;
3131 use nix:: libc;
32+
33+ #[ cfg( test) ]
34+ use std:: sync:: Mutex ;
3235}
3336
3437cfg_langinfo ! {
3538 /// Cached locale date/time format string
3639 static DEFAULT_FORMAT_CACHE : OnceLock <& ' static str > = OnceLock :: new( ) ;
3740
41+ /// Mutex to serialize setlocale() calls during tests.
42+ ///
43+ /// setlocale() is process-global, so parallel tests that call it can
44+ /// interfere with each other. This mutex ensures only one test accesses
45+ /// locale functions at a time.
46+ #[ cfg( test) ]
47+ static LOCALE_MUTEX : Mutex <( ) > = Mutex :: new( ( ) ) ;
48+
3849 /// Returns the default date format string for the current locale.
3950 ///
4051 /// The format respects locale preferences for time display (12-hour vs 24-hour),
@@ -55,6 +66,11 @@ cfg_langinfo! {
5566
5667 /// Retrieves the date/time format string from the system locale
5768 fn get_locale_format_string( ) -> Option <String > {
69+ // In tests, acquire mutex to prevent race conditions with setlocale()
70+ // which is process-global and not thread-safe
71+ #[ cfg( test) ]
72+ let _lock = LOCALE_MUTEX . lock( ) . unwrap( ) ;
73+
5874 unsafe {
5975 // Set locale from environment variables
6076 libc:: setlocale( libc:: LC_TIME , c"" . as_ptr( ) ) ;
@@ -158,11 +174,24 @@ mod tests {
158174
159175 #[ test]
160176 fn test_c_locale_format( ) {
161- // Save original locale
177+ // Acquire mutex to prevent interference with other tests
178+ let _lock = LOCALE_MUTEX . lock( ) . unwrap( ) ;
179+
180+ // Save original locale (both environment and process locale)
162181 let original_lc_all = std:: env:: var( "LC_ALL" ) . ok( ) ;
163182 let original_lc_time = std:: env:: var( "LC_TIME" ) . ok( ) ;
164183 let original_lang = std:: env:: var( "LANG" ) . ok( ) ;
165184
185+ // Save current process locale
186+ let original_process_locale = unsafe {
187+ let ptr = libc:: setlocale( libc:: LC_TIME , std:: ptr:: null( ) ) ;
188+ if ptr. is_null( ) {
189+ None
190+ } else {
191+ CStr :: from_ptr( ptr) . to_str( ) . ok( ) . map( |s| s. to_string( ) )
192+ }
193+ } ;
194+
166195 unsafe {
167196 // Set C locale
168197 std:: env:: set_var( "LC_ALL" , "C" ) ;
@@ -177,7 +206,7 @@ mod tests {
177206 if d_t_fmt_ptr. is_null( ) {
178207 None
179208 } else {
180- std :: ffi :: CStr :: from_ptr( d_t_fmt_ptr) . to_str( ) . ok( )
209+ CStr :: from_ptr( d_t_fmt_ptr) . to_str( ) . ok( )
181210 }
182211 } ;
183212
@@ -190,7 +219,7 @@ mod tests {
190219 assert!( uses_24_hour, "C locale should use 24-hour format, got: {locale_format}" ) ;
191220 }
192221
193- // Restore original locale
222+ // Restore original environment variables
194223 unsafe {
195224 if let Some ( val) = original_lc_all {
196225 std:: env:: set_var( "LC_ALL" , val) ;
@@ -208,6 +237,17 @@ mod tests {
208237 std:: env:: remove_var( "LANG" ) ;
209238 }
210239 }
240+
241+ // Restore original process locale
242+ unsafe {
243+ if let Some ( locale) = original_process_locale {
244+ let c_locale = std:: ffi:: CString :: new( locale) . unwrap( ) ;
245+ libc:: setlocale( libc:: LC_TIME , c_locale. as_ptr( ) ) ;
246+ } else {
247+ // Restore from environment
248+ libc:: setlocale( libc:: LC_TIME , c"" . as_ptr( ) ) ;
249+ }
250+ }
211251 }
212252
213253 #[ test]
0 commit comments