@@ -361,15 +361,40 @@ cfg_if! {
361361 }
362362 }
363363
364+ // Tests extract language/braille zips in parallel. During extraction, individual YAML
365+ // files can briefly exist in a partially-written state (created/truncated before the
366+ // full contents are flushed). Guard both extraction and YAML reads with the same lock
367+ // so readers never observe truncated YAML.
368+ use std:: sync:: Mutex ;
369+ static RULES_ZIP_EXTRACT_LOCK : Mutex <( ) > = Mutex :: new( ( ) ) ;
370+
371+ fn should_lock_rules_yaml_path( path: & Path ) -> bool {
372+ let s = path. to_string_lossy( ) ;
373+ let is_yaml = s. ends_with( ".yaml" ) || s. ends_with( ".yml" ) ;
374+ if !is_yaml {
375+ return false ;
376+ }
377+ return s. contains( "Rules/Languages/" ) || s. contains( "Rules\\ Languages\\ " )
378+ || s. contains( "Rules/Braille/" ) || s. contains( "Rules\\ Braille\\ " ) ;
379+ }
380+
364381 pub fn read_to_string_shim( path: & Path ) -> Result <String > {
365382 let path = match path. canonicalize( ) {
366383 Ok ( path) => path,
367384 Err ( _) => path. to_path_buf( ) ,
368385 } ;
369386 debug!( "Reading file '{}'" , path. display( ) ) ;
370- match std:: fs:: read_to_string( & path) {
371- Ok ( str ) => return Ok ( str ) ,
372- Err ( e) => bail!( "Read error while trying to read {}: {}" , path. display( ) , e) ,
387+ if should_lock_rules_yaml_path( & path) {
388+ let _guard = RULES_ZIP_EXTRACT_LOCK . lock( ) . unwrap_or_else( |poisoned| poisoned. into_inner( ) ) ;
389+ match std:: fs:: read_to_string( & path) {
390+ Ok ( str ) => return Ok ( str ) ,
391+ Err ( e) => bail!( "Read error while trying to read {}: {}" , path. display( ) , e) ,
392+ }
393+ } else {
394+ match std:: fs:: read_to_string( & path) {
395+ Ok ( str ) => return Ok ( str ) ,
396+ Err ( e) => bail!( "Read error while trying to read {}: {}" , path. display( ) , e) ,
397+ }
373398 }
374399 }
375400
@@ -386,6 +411,7 @@ cfg_if! {
386411 }
387412 } ,
388413 Ok ( contents) => {
414+ let _guard = RULES_ZIP_EXTRACT_LOCK . lock( ) . unwrap_or_else( |poisoned| poisoned. into_inner( ) ) ;
389415 let archive = std:: io:: Cursor :: new( contents) ;
390416 let mut zip_archive = zip:: ZipArchive :: new( archive) . unwrap( ) ;
391417 zip_archive. extract( dir) . expect( "Zip extraction failed" ) ;
0 commit comments