ide/settings: Fixed write locking on Unix
[sdk] / ide / src / IDESettings.ec
index b83f2a5..8a19e0d 100644 (file)
@@ -629,11 +629,11 @@ static void getConfigFilePath(char * path, Class _class, char * dir, const char
 static SettingsIOResult writeConfigFile(const char * path, Class dataType, void * data)
 {
    SettingsIOResult result = error;
-   SafeFile sf;
-   sf = safeWriteFileOpen(path, write);
-   if(sf.file)
+   SafeFile sf = SafeFile::open(path, write);
+   if(sf)
    {
       WriteECONObject(sf.file, dataType, data, 0);
+      sf.sync();
       delete sf;
       result = success;
    }
@@ -648,7 +648,7 @@ static SettingsIOResult readConfigFile(const char * path, Class dataType, void *
    SafeFile sf;
    if(!FileExists(path))
       result = fileNotFound;
-   else if((sf = safeWriteFileOpen(path, read)))
+   else if((sf = SafeFile::open(path, read)))
    {
       JSONResult jsonResult;
       {
@@ -678,69 +678,113 @@ class SafeFile
    char path[MAX_LOCATION];
    char tmp[MAX_LOCATION];
 
-   ~SafeFile()
+   SafeFile ::open(const char * path, FileOpenMode mode)
    {
-      delete file;
-      // how to make this atomic ?
-      if(mode == write)
+      SafeFile result = null;
+      if(mode == write || mode == read)
       {
+         SafeFile sf { mode = mode };
          int c;
-         for(c = 0; c < 10; c++)
+         bool locked = false;
+         FileLock lockType = mode == write ? exclusive : shared;
+
+         strcpy(sf.path, path);
+         strcpy(sf.tmp, path);
+         strcat(sf.tmp, ".tmp");
+         if(mode == write && FileExists(sf.tmp).isFile)
+            DeleteFile(sf.tmp);
+
+         if(mode == write)
          {
-            if(FileExists(path).isFile)
-               DeleteFile(path);
-            else
+            sf.file = FileOpen(sf.tmp, readWrite);
+            if(!sf.file)
             {
-               MoveFile(tmp, path);
-               if(FileExists(path).isFile)
-                  break;
+               sf.file = FileOpen(sf.tmp, writeRead);
+               if(sf.file)
+               {
+                  delete sf.file;
+                  sf.file = FileOpen(sf.tmp, readWrite);
+               }
             }
          }
+         else
+            sf.file = FileOpen(path, mode);
+         if(sf.file)
+         {
+            for(c = 0; c < 10 && !(locked = sf.file.Lock(lockType, 0, 0, false)); c++) Sleep(0.01);
+            if(locked)
+               result = sf;
+            else if(mode == write)
+               PrintLn($"warning: SafeFile::open: unable to obtain exclusive lock on temporary file for writing: ", sf.tmp);
+            else
+               PrintLn($"warning: SafeFile::open: unable to obtain shared lock on file for reading: ", path);
+         }
+         else if(mode == write)
+            PrintLn($"warning: SafeFile::open: unable to open temporary file for writing: ", sf.tmp);
+         else
+            PrintLn($"warning: SafeFile::open: unable to open file for reading: ", path);
+
+         if(!result)
+            delete sf;
       }
+      else
+         PrintLn($"warning: SafeFile::open: does not yet support FileOpenMode::", mode);
+      return result;
    }
-}
 
-SafeFile safeWriteFileOpen(const char * path, FileOpenMode mode)
-{
-   SafeFile sf { mode = mode };
-   int c;
-   bool locked = false;
-   strcpy(sf.path, path);
-   strcpy(sf.tmp, path);
-   strcat(sf.tmp, ".tmp");
-   if(mode == write)
-   {
-      if(FileExists(sf.tmp).isFile)
-         DeleteFile(sf.tmp);
-      sf.file = FileOpen(sf.tmp, write);
-      if(sf.file)
+   void sync()
+   {
+      if(file && mode == write)
       {
-         for(c = 0; c < 10 && !(locked = sf.file.Lock(exclusive, 0, 0, false)); c++);
-         if(!locked)
+         int c;
+         File f = FileOpen(path, readWrite);
+         if(!f)
          {
-            delete sf.file;
-            PrintLn($"warning: safeWriteFileOpen: unable to obtain exclusive lock on temporary file for writing: ", sf.tmp);
+            f = FileOpen(path, writeRead);
+            if(f)
+            {
+               delete f;
+               f = FileOpen(path, readWrite);
+            }
+         }
+         if(f)
+         {
+            bool locked = true;
+            for(c = 0; c < 10 && !(locked = f.Lock(exclusive, 0,0, false)); c++) Sleep(0.01);
+
+            if(locked)
+            {
+               f.Unlock(0,0, false);
+               delete f;
+               file.Unlock(0,0, false);
+               delete file;
+
+               for(c = 0; c < 10; c++)
+               {
+                  if(MoveFileEx(tmp, path, { true, true }))
+                     break;
+                  else
+                     Sleep(0.01);
+               }
+            }
+            else
+            {
+               delete f;
+               PrintLn($"warning: SafeFile::sync: failed to lock file", mode);
+            }
          }
       }
-      else
-         PrintLn($"warning: safeWriteFileOpen: unable to open temporary file for writing: ", sf.tmp);
    }
-   else if(mode == read)
+
+
+   ~SafeFile()
    {
-      sf.file = FileOpen(path, read);
-      if(sf.file)
+      if(file)
       {
-         for(c = 0; c < 10 && !(locked = sf.file.Lock(shared, 0, 0, false)); c++) Sleep(0.01);
-         if(!locked)
-         {
-            delete sf.file;
-            PrintLn($"warning: safeWriteFileOpen: unable to obtain shared lock on file for reading: ", path);
-         }
+         file.Unlock(0,0, false);
+         delete file;
       }
    }
-   else
-      PrintLn($"warning: safeWriteFileOpen: does not yet support FileOpenMode::", mode);
-   return sf;
 }
 
 class RecentFilesData