//+------------------------------------------------------------------+ //| Warrior_EA | //| AnimateDread | //| | //| Crash-safe binary file writes. | //+------------------------------------------------------------------+ #ifndef WARRIOR_ATOMIC_FILE_MQH #define WARRIOR_ATOMIC_FILE_MQH //+------------------------------------------------------------------+ //| Open a temp file to stage an atomic binary write. | //+------------------------------------------------------------------+ int AtomicWriteBegin(const string finalName, const int commonFlag, string &tmpName) { tmpName = finalName + ".savetmp"; ResetLastError(); return FileOpen(tmpName, commonFlag | FILE_BIN | FILE_WRITE | FILE_SHARE_READ | FILE_SHARE_WRITE); } //+------------------------------------------------------------------+ //| Close the staged write and either publish it or discard it. | //+------------------------------------------------------------------+ bool AtomicWriteEnd(const int handle, const string finalName, const string tmpName, const int commonFlag, const bool ok, const string context) { if(handle != INVALID_HANDLE) { FileFlush(handle); FileClose(handle); } //--- A failed/partial write must NEVER touch the real file - discard the temp, keep the last good copy. if(!ok) { FileDelete(tmpName, commonFlag); Print(context, ": write to ", tmpName, " failed - kept the existing ", finalName, " intact (no atomic swap performed)"); return false; } //--- Atomic swap: FILE_REWRITE lets FileMove replace an existing destination in one operation. ResetLastError(); if(!FileMove(tmpName, commonFlag, finalName, commonFlag | FILE_REWRITE)) { Print(context, ": atomic rename ", tmpName, " -> ", finalName, " failed (error ", IntegerToString(GetLastError()), ") - the temp holds the new content and the existing ", finalName, " is unchanged"); ResetLastError(); return false; } return true; } //+------------------------------------------------------------------+ #endif // WARRIOR_ATOMIC_FILE_MQH