Permalink
Browse files

Factor out "write-temp-file-then-replace" code into AtomicFileOverwri…

…ter class.

git-svn-id: https://scantailor.svn.sourceforge.net/svnroot/scantailor/trunk@133 80b70061-164d-0410-b7b7-e274ec932871
  • Loading branch information...
1 parent a627fc9 commit bc170df16514e9908d911a181a32aeaa8efa0f8c jart committed Aug 31, 2008
Showing with 169 additions and 24 deletions.
  1. +81 −0 AtomicFileOverwriter.cpp
  2. +75 −0 AtomicFileOverwriter.h
  3. +1 −0 CMakeLists.txt
  4. +12 −24 ThumbnailPixmapCache.cpp
@@ -0,0 +1,81 @@
+/*
+ Scan Tailor - Interactive post-processing tool for scanned pages.
+ Copyright (C) 2007-2008 Joseph Artsimovich <joseph_a@mail.ru>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "AtomicFileOverwriter.h"
+#include "Utils.h"
+#include <QString>
+#include <QFile>
+#include <QTemporaryFile>
+
+AtomicFileOverwriter::AtomicFileOverwriter()
+{
+}
+
+AtomicFileOverwriter::~AtomicFileOverwriter()
+{
+ abort();
+}
+
+QIODevice*
+AtomicFileOverwriter::startWriting(QString const& file_path)
+{
+ abort();
+
+ m_ptrTempFile.reset(new QTemporaryFile(file_path));
+ m_ptrTempFile->setAutoRemove(false);
+ if (!m_ptrTempFile->open()) {
+ m_ptrTempFile->reset();
+ }
+
+ return m_ptrTempFile.get();
+}
+
+bool
+AtomicFileOverwriter::commit()
+{
+ if (!m_ptrTempFile.get()) {
+ return false;
+ }
+
+ QString const temp_file_path(m_ptrTempFile->fileName());
+ QString const target_path(m_ptrTempFile->fileTemplate());
+
+ // Yes, we have to destroy this object here, because:
+ // 1. Under Windows, open files can't be renamed or deleted.
+ // 2. QTemporaryFile::close() doesn't really close it.
+ m_ptrTempFile.reset();
+
+ if (!Utils::renameFile(temp_file_path, target_path)) {
+ QFile::remove(temp_file_path);
+ return false;
+ }
+
+ return true;
+}
+
+void
+AtomicFileOverwriter::abort()
+{
+ if (!m_ptrTempFile.get()) {
+ return;
+ }
+
+ QString const temp_file_path(m_ptrTempFile->fileName());
+ m_ptrTempFile.reset(); // See comments in commit()
+ QFile::remove(temp_file_path);
+}
@@ -0,0 +1,75 @@
+/*
+ Scan Tailor - Interactive post-processing tool for scanned pages.
+ Copyright (C) 2007-2008 Joseph Artsimovich <joseph_a@mail.ru>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ATOMICFILEOVERWRITER_H_
+#define ATOMICFILEOVERWRITER_H_
+
+#include "NonCopyable.h"
+#include <memory>
+
+class QString;
+class QIODevice;
+class QTemporaryFile;
+
+/**
+ * \brief Overwrites files by writing to a temporary file and then replacing
+ * the target file with it.
+ *
+ * Because renaming across volumes doesn't work, we create a temporary file
+ * in the same directory as the target file.
+ */
+class AtomicFileOverwriter
+{
+ DECLARE_NON_COPYABLE(AtomicFileOverwriter)
+public:
+ AtomicFileOverwriter();
+
+ /**
+ * \brief Destroys the object and calls abort() if necessary.
+ */
+ ~AtomicFileOverwriter();
+
+ /**
+ * \brief Start writing to a temporary file.
+ *
+ * \returns A temporary file as QIODevice, or null of temporary file
+ * could not be opened. In latter case, calling abort()
+ * is not necessary.
+ *
+ * If a file is already being written, it calles abort() and then
+ * proceeds as usual.
+ */
+ QIODevice* startWriting(QString const& file_path);
+
+ /**
+ * \brief Replaces the target file with the temporary one.
+ *
+ * If replacing failed, false is returned and the temporary file
+ * is removed.
+ */
+ bool commit();
+
+ /**
+ * \brief Removes the temporary file without touching the target one.
+ */
+ void abort();
+private:
+ std::auto_ptr<QTemporaryFile> m_ptrTempFile;
+};
+
+#endif
View
@@ -216,6 +216,7 @@ SET(
ProjectWriter.cpp ProjectWriter.h
XmlMarshaller.cpp XmlMarshaller.h
XmlUnmarshaller.cpp XmlUnmarshaller.h
+ AtomicFileOverwriter.cpp AtomicFileOverwriter.h
Utils.cpp Utils.h
AutoManualMode.h
AbstractCommand.h
@@ -19,7 +19,7 @@
#include "ThumbnailPixmapCache.h"
#include "ImageId.h"
#include "ImageLoader.h"
-#include "Utils.h"
+#include "AtomicFileOverwriter.h"
#include "imageproc/Scale.h"
#include <QCoreApplication>
#include <QCryptographicHash>
@@ -29,8 +29,6 @@
#include <QFileInfo>
#include <QDir>
#include <QFile>
-#include <QTemporaryFile>
-#include <QAbstractFileEngine>
#include <QString>
#include <QChar>
#include <QImage>
@@ -453,16 +451,10 @@ ThumbnailPixmapCache::Impl::ensureThumbnailExists(
QImage const thumbnail(makeThumbnail(image, max_thumb_size));
- // To avoid concurrently writing to the same file, we write
- // to a temporary file and then replace the original.
- QTemporaryFile temp_file(thumb_file_path);
- temp_file.setAutoRemove(true);
- if (temp_file.open()) {
- if (thumbnail.save(&temp_file, "PNG")) {
- if (Utils::renameFile(temp_file.fileName(), thumb_file_path)) {
- temp_file.setAutoRemove(false);
- }
- }
+ AtomicFileOverwriter overwriter;
+ QIODevice* iodev = overwriter.startWriting(thumb_file_path);
+ if (thumbnail.save(iodev, "PNG")) {
+ overwriter.commit();
}
}
@@ -487,17 +479,13 @@ ThumbnailPixmapCache::Impl::recreateThumbnail(
QImage const thumbnail(makeThumbnail(image, max_thumb_size));
bool thumb_written = false;
- // To avoid concurrently writing to the same file, we write
- // to a temporary file and then replace the original.
- QTemporaryFile temp_file(thumb_file_path);
- temp_file.setAutoRemove(true);
- if (temp_file.open()) {
- if (thumbnail.save(&temp_file, "PNG")) {
- if (Utils::renameFile(temp_file.fileName(), thumb_file_path)) {
- thumb_written = true;
- temp_file.setAutoRemove(false);
- }
- }
+ // Note that we may be called from multiple threads at the same time.
+ AtomicFileOverwriter overwriter;
+ QIODevice* iodev = overwriter.startWriting(thumb_file_path);
+ if (thumbnail.save(iodev, "PNG")) {
+ thumb_written = overwriter.commit();
+ } else {
+ overwriter.abort();
}
if (!thumb_written) {

0 comments on commit bc170df

Please sign in to comment.