Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
ThroughTmpFileWriter.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_IO_FileSystem_ThroughTmpFileWriter_h_
5#define _Stroika_Foundation_IO_FileSystem_ThroughTmpFileWriter_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <filesystem>
10
13
14/**
15 * \file
16 *
17 * \note Code-Status: <a href="Code-Status.md#Release">Release</a>
18 */
19
21
22 using Characters::String;
23
24 /**
25 * \brief utility to atomically update/write a file.
26 *
27 * Specify the name of a file to write, and an optional file suffix for a tempfile, and
28 * support writing through the tempfile, and then atomicly renaming the file when done.
29 *
30 * Even in case of failure, assure the tmpfile is removed.
31 *
32 * There is no need to first remove 'realFileName' - as this will not fail because it already exists (unless
33 * we don't have permission to remove it).
34 *
35 * The point of this is to allow writing a file in such a way that the entire file write is
36 * atomic. We don't want to partially update a file and upon failure, leave it corrupted (partly updated).
37 *
38 * Using this class, you create a tempfile, write to it, and the Commit () the change. NOTE,
39 * it is REQUIRED you call Commit () after all writing to tmpfile is done (and closed),
40 * otherwise the changes are abandoned.
41 *
42 * \par Example Usage
43 * \code
44 * ThroughTmpFileWriter tmpFile{targetFileName};
45 * IO::FileSystem::FileOutputStream::Ptr outStream = IO::FileSystem::FileOutputStream::New (tmpFile.GetFilePath ());
46 * YourCodeToWriteDataToStream (your_data, out);
47 * out.clear(); // close like this so we can throw exception - cannot throw if we count on DTOR
48 * tmpFile.Commit (); // any exceptions cause the tmp file to be automatically cleaned up
49 * \endcode
50 *
51 * \par Example Usage
52 * \code
53 * ThroughTmpFileWriter tmpFile{targetFileName};
54 * {
55 * IO::FileSystem::FileOutputStream::Ptr outStream = IO::FileSystem::FileOutputStream::New (tmpFile.GetFilePath ());
56 * YourCodeToWriteDataToStream (your_data, outStream);
57 * // use scope (braces) to force close before commit (or call out.clear())
58 * }
59 * tmpFile.Commit (); // any exceptions cause the tmp file to be automatically cleaned up
60 * \endcode
61 *
62 * \note - despite the similaries, this does not use AppTempFile module, because that writes in a specific place,
63 * and for 'rename' to work, it must be in the same filesystem (disk/os dependent), so write in same folder (is what this does).
64 *
65 * \note to assure no conflicts in multithreading/multiprocessing scenarios, the file is actually created by this function (empty), but
66 * not kept open.
67 *
68 * \todo for qStroika_Foundation_Common_Platform_Windows/fRetryOnSharingViolationFor issue/feature, maybe check ACCESS
69 * on eventual target file on CTOR, so any real issues with access caught immediately, before we write through
70 * tmp file
71 */
73 public:
74 ThroughTmpFileWriter (const filesystem::path& realFileName, const String& tmpSuffix = ".tmp"sv);
77
78 public:
79 nonvirtual ThroughTmpFileWriter& operator= (const ThroughTmpFileWriter&) = delete;
80
81 public:
82 /**
83 */
84 nonvirtual filesystem::path GetTmpFilePath () const;
85
86 public:
87 /**
88 */
89 nonvirtual filesystem::path GetRealFilePath () const;
90
91 public:
92 /**
93 * Before commit this returns the tmpfile name. After commit returns the eventual file name.
94 */
95 nonvirtual filesystem::path GetFilePath () const;
96
97 public:
98 /**
99 * tmpfile must have been closed the time we call Commit, and it atomicly renames the file
100 * to the target name. This CAN fail (in which case cleanup is handled automatically)
101 */
102 nonvirtual void Commit ();
103
104#if qStroika_Foundation_Common_Platform_Windows
105 public:
106 /**
107 * Sadly windows has issues with antivirus scanners. They often OPEN a file (frequently when just created like
108 * this scenario) and still have it open when we are ready to rename it. Because of flaws in windows filesystem,
109 * this then fails (due to rename failure). OFTEN the best approach is to just wait and retry.
110 *
111 * Do that by default, but allow for different behaviors. If fRetryOnSharingViolationFor == 0, this won't be
112 * retried.
113 *
114 * \note ERROR_SHARING_VIOLATION (32) and ERROR_ACCESS_DENIED (5) retried.
115 */
116 optional<Time::DurationSeconds> fRetryOnSharingViolationFor;
117 static constexpr auto kRetryOnSharingViolationFor_Default = 1s;
118 static constexpr auto kRetryOnSharingViolationFor_Disable = 0s;
119#endif
120
121 private:
122 filesystem::path fRealFilePath_;
123 filesystem::path fTmpFilePath_;
124 };
125
126}
127
128/*
129 ********************************************************************************
130 ***************************** Implementation Details ***************************
131 ********************************************************************************
132 */
133#include "ThroughTmpFileWriter.inl"
134
135#endif /*_Stroika_Foundation_IO_FileSystem_ThroughTmpFileWriter_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201