﻿id,summary,reporter,owner,description,type,status,priority,milestone,component,version,resolution,keywords,cc,os,architecture,failure,difficulty,testcase,blockedby,blocking,related 2298,renameFile is not atomic on Windows,duncan,simonmar,"The Haskell 98 spec says about `renameFile` Computation `renameFile old new` changes the name of an existing file system object from old to new. If the new object already exists, it is atomically replaced by the old object. The `renameFile` function has been tricky on Windows historically because Windows 9x did not directly support overwriting an existing file. Windows NT and later support [http://msdn.microsoft.com/en-us/library/aa365240(VS.85).aspx `MoveFileEx`] which has a flag `MOVEFILE_REPLACE_EXISTING`. The current `__hscore_renameFile` implementation for Windows uses a complex range of tests and workarounds to allow overwriting of existing files. Of course this cannot be atomic. For Windows NT it does use `MoveFileEx()` but then if that fails it goes and tries the old tricks anyway! So abandoning all promises of atomicity it goes and tries to delete the target file and then a final attempt to rename over the now-deleted target file. As far as I can see this is bonkers. It should use `MoveFileEx()` exactly once and if that fails then the whole thing fails. That way we preserve any atomicity guarantees that `MoveFileEx()` might provide. Note that the MSDN documentation doesn't actually say if the rename is atomic, even in the case of two files in the same directory. With a `renameFile` that ''does'' meet the H98 requirement we can write an `writeFileAtomic` function that either succeeds and replaces the target file or fails without altering the target file in any way. Additionally, other threads or processes will not see any intermediate states of the target file (though they would see the temp file created in the same directory). This is how text editors etc implement reliable file writing. Glib has a function like this [http://library.gnome.org/devel/glib/stable/glib-File-Utilities.html#g-file-set-contents g-file-set-contents]. {{{ writeFileAtomic :: FilePath -> String -> IO () writeFileAtomic targetFile content = Exception.bracketOnError (openTempFile targetDir template) (\(tmpFile, tmpHandle) -> hClose tmpHandle >> removeFile tmpFile) (\(tmpFile, tmpHandle) -> hPutStr tmpHandle content >> hClose tmpHandle >> renameFile tmpFile targetFile) where template = targetName <.> ""tmp"" (targetDir,targetName) = splitFileName targetFile }}} Indeed it's not impossible to imagine using this or something similar for the actual H98 `writeFile` implementation. ",bug,closed,normal,6.10.1,libraries/base,6.8.2,fixed,,,Windows,x86,,Unknown,,,,