@@ -51,6 +51,66 @@ impl ForksafeTempfile {
5151 self . persist_inner ( path. as_ref ( ) )
5252 }
5353
54+ #[ cfg( windows) ]
55+ fn persist_inner ( mut self , path : & Path ) -> Result < Option < std:: fs:: File > , ( std:: io:: Error , Self ) > {
56+ /// Maximum number of attempts for Windows file locking issues.
57+ /// Matches libgit2's default retry count.
58+ const MAX_ATTEMPTS : usize = 10 ;
59+ /// Delay between retry attempts in milliseconds.
60+ /// Matches libgit2's retry delay.
61+ const RETRY_DELAY_MS : u64 = 5 ;
62+
63+ fn should_retry ( err : & std:: io:: Error ) -> bool {
64+ use std:: io:: ErrorKind ;
65+ // Access denied (ERROR_ACCESS_DENIED = 5) or sharing violation (ERROR_SHARING_VIOLATION = 32)
66+ // are the common errors when external processes like antivirus or file watchers hold the file.
67+ matches ! ( err. kind( ) , ErrorKind :: PermissionDenied ) || err. raw_os_error ( ) == Some ( 32 )
68+ // ERROR_SHARING_VIOLATION
69+ }
70+
71+ match self . inner {
72+ TempfileOrTemppath :: Tempfile ( file) => {
73+ let mut current_file = file;
74+ for attempt in 0 ..MAX_ATTEMPTS {
75+ match current_file. persist ( path) {
76+ Ok ( file) => return Ok ( Some ( file) ) ,
77+ Err ( err) if attempt + 1 < MAX_ATTEMPTS && should_retry ( & err. error ) => {
78+ std:: thread:: sleep ( std:: time:: Duration :: from_millis ( RETRY_DELAY_MS ) ) ;
79+ current_file = err. file ;
80+ }
81+ Err ( err) => {
82+ return Err ( ( err. error , {
83+ self . inner = TempfileOrTemppath :: Tempfile ( err. file ) ;
84+ self
85+ } ) )
86+ }
87+ }
88+ }
89+ unreachable ! ( "loop always returns" )
90+ }
91+ TempfileOrTemppath :: Temppath ( temppath) => {
92+ let mut current_path = temppath;
93+ for attempt in 0 ..MAX_ATTEMPTS {
94+ match current_path. persist ( path) {
95+ Ok ( _) => return Ok ( None ) ,
96+ Err ( err) if attempt + 1 < MAX_ATTEMPTS && should_retry ( & err. error ) => {
97+ std:: thread:: sleep ( std:: time:: Duration :: from_millis ( RETRY_DELAY_MS ) ) ;
98+ current_path = err. path ;
99+ }
100+ Err ( err) => {
101+ return Err ( ( err. error , {
102+ self . inner = TempfileOrTemppath :: Temppath ( err. path ) ;
103+ self
104+ } ) )
105+ }
106+ }
107+ }
108+ unreachable ! ( "loop always returns" )
109+ }
110+ }
111+ }
112+
113+ #[ cfg( not( windows) ) ]
54114 fn persist_inner ( mut self , path : & Path ) -> Result < Option < std:: fs:: File > , ( std:: io:: Error , Self ) > {
55115 match self . inner {
56116 TempfileOrTemppath :: Tempfile ( file) => match file. persist ( path) {
0 commit comments