Race condition during configuration file writing

EDIT: I just found that’s actually #5733 at work!

I use aseprite heavily in cli mode (for thumbnailing, for example); occasionally I see the configuration/preference reset when launching it in graphic mode.

This is under Linux; it probably doesn’t happen on Windows due to the different way file open works.

What’s happening is that, since aseprite.ini is rewritten at each program exit (even in CLI/batch mode), when there are two instances running at the same file the configuration file can be opened for writing by both, thus creating an invalid configuration. Windows automatically locks the file so it shouldn’t happen. (the fix for #5377 avoids writing in batch)

I solved the issue using a temporary file and an atomic rename in cfg.cpp

  void save()
  {
    std::string tmpname(m_filename + "." + std::to_string(getpid()));
    SI_Error err = SI_OK;
    {
      base::FileHandle file(base::open_file(tmpname, "wb"));
      if (file) {
        err = m_ini.SaveFile(file.get());
      }
    }
    if (rename(tmpname.c_str(), m_filename.c_str()) < 0) {
      err = SI_FILE;
    }
    if (err != SI_OK) {
      LOG(ERROR, "CFG: Error %d saving configuration into %s\n", (int)err, m_filename.c_str());
    }
  }

It’s almost portable, except for the getpid syscall (but I guess there’s an equivalent Windows one, anything semirandom is ok). The inner block is to ensure the file is closed (due to RAII) before the rename.