Chaste Commit::fa89f2b838c1edb21a1eaec92ee3a2eacc9255dd
ArchiveOpener.cpp
1/*
2
3Copyright (c) 2005-2026, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, are permitted provided that the following conditions are met:
14 * Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16 * Redistributions in binary form must reproduce the above copyright notice,
17 this list of conditions and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
19 * Neither the name of the University of Oxford nor the names of its
20 contributors may be used to endorse or promote products derived from this
21 software without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36// Must be included before any other serialization headers
37#include <boost/archive/binary_iarchive.hpp>
38#include <boost/archive/binary_oarchive.hpp>
39#include <boost/archive/text_iarchive.hpp>
40#include <boost/archive/text_oarchive.hpp>
41
42#include <fstream>
43#include <sstream>
44
45#include "ArchiveLocationInfo.hpp"
46#include "ArchiveOpener.hpp"
47#include "Exception.hpp"
48#include "FilesystemPermissions.hpp"
49#include "OutputFileHandler.hpp"
50#include "ProcessSpecificArchive.hpp"
51
57template <class InputArchive>
58class ArchiveOpener<InputArchive, std::ifstream>
59{
60private:
61 friend class TestArchivingHelperClasses;
62
63public:
71 const FileFinder& rDirectory,
72 const std::string& rFileNameBase,
73 unsigned procId)
74 : mpCommonStream(nullptr),
75 mpPrivateStream(nullptr),
76 mpCommonArchive(nullptr),
77 mpPrivateArchive(nullptr)
78 {
79 // Figure out where things live
81 std::string private_path = ArchiveLocationInfo::GetProcessUniqueFilePath(rFileNameBase, procId);
82 std::stringstream common_path;
83 common_path << ArchiveLocationInfo::GetArchiveDirectory() << rFileNameBase;
84
85 // Try to open the main archive for replicated data
86 mpCommonStream = new std::ifstream(common_path.str().c_str(), std::ios::binary);
87 if (!mpCommonStream->is_open())
88 {
89 delete mpCommonStream;
90 EXCEPTION("Cannot load main archive file: " + common_path.str());
91 }
92
93 try
94 {
95 mpCommonArchive = new InputArchive(*mpCommonStream);
96 }
97 catch (boost::archive::archive_exception& boost_exception)
98 {
99 if (boost_exception.code == boost::archive::archive_exception::unsupported_version)
100 {
101 // This is forward compatibility issue. We can't open the archive because it's been written by a more recent Boost.
102 delete mpCommonArchive;
103 delete mpCommonStream;
104 EXCEPTION("Could not open Boost archive '" + common_path.str() + "' because it was written by a more recent Boost. Check process-specific archives too");
105 }
106 else
107 {
108 // We don't understand the exception, so we shouldn't continue
109 throw boost_exception; // LCOV_EXCL_LINE
110 }
111 }
112
113 // Try to open the secondary archive for distributed data
114 mpPrivateStream = new std::ifstream(private_path.c_str(), std::ios::binary);
115 if (!mpPrivateStream->is_open())
116 {
117 delete mpPrivateStream;
118 delete mpCommonArchive;
119 delete mpCommonStream;
120 EXCEPTION("Cannot load secondary archive file: " + private_path);
121 }
122 mpPrivateArchive = new InputArchive(*mpPrivateStream);
124 }
125
129 InputArchive* GetCommonArchive()
130 {
131 assert(mpCommonArchive != NULL);
132 return mpCommonArchive;
133 }
134
136 {
138 delete mpPrivateArchive;
139 delete mpPrivateStream;
140 delete mpCommonArchive;
141 delete mpCommonStream;
142 }
143
144private:
146 std::ifstream* mpCommonStream;
147
149 std::ifstream* mpPrivateStream;
150
152 InputArchive* mpCommonArchive;
153
155 InputArchive* mpPrivateArchive;
156};
157
163template <class OutputArchive>
164class ArchiveOpener<OutputArchive, std::ofstream>
165{
166private:
167 friend class TestArchivingHelperClasses;
168
169public:
177 const FileFinder& rDirectory,
178 const std::string& rFileNameBase,
179 unsigned procId)
180 : mpCommonStream(nullptr),
181 mpPrivateStream(nullptr),
182 mpCommonArchive(nullptr),
183 mpPrivateArchive(nullptr)
184 {
185 // Check for user error
186 if (procId != PetscTools::GetMyRank())
187 {
188 EXCEPTION("Specifying the secondary archive file ID doesn't make sense when writing.");
189 }
190
191 // Figure out where things live
194 {
195 // Ensure the directory exists
197 }
198 std::string private_path = ArchiveLocationInfo::GetProcessUniqueFilePath(rFileNameBase);
199 std::stringstream common_path;
200 common_path << ArchiveLocationInfo::GetArchiveDirectory() << rFileNameBase;
201
202 // Create master archive for replicated data
204 {
205 mpCommonStream = new std::ofstream(common_path.str().c_str(), std::ios::binary | std::ios::trunc);
206 if (!mpCommonStream->is_open())
207 {
208 delete mpCommonStream;
209 EXCEPTION("Failed to open main archive file for writing: " + common_path.str());
210 }
212 }
213 else
214 {
215 // Non-master processes need to go through the serialization methods, but not write any data
216 mpCommonStream = new std::ofstream("/dev/null", std::ios::binary | std::ios::trunc);
217 // LCOV_EXCL_START
218 if (!mpCommonStream->is_open())
219 {
220 delete mpCommonStream;
221 EXCEPTION("Failed to open dummy archive file '/dev/null' for writing");
222 }
223 // LCOV_EXCL_STOP
224 }
225 mpCommonArchive = new OutputArchive(*mpCommonStream);
226
227 // Create secondary archive for distributed data
228 mpPrivateStream = new std::ofstream(private_path.c_str(), std::ios::binary | std::ios::trunc);
229 if (!mpPrivateStream->is_open())
230 {
231 delete mpPrivateStream;
232 delete mpCommonArchive;
233 delete mpCommonStream;
234 EXCEPTION("Failed to open secondary archive file for writing: " + private_path);
235 }
237 mpPrivateArchive = new OutputArchive(*mpPrivateStream);
239 }
240
244 OutputArchive* GetCommonArchive()
245 {
246 assert(mpCommonArchive != NULL);
247 return mpCommonArchive;
248 }
249
251 {
253 delete mpPrivateArchive;
254 delete mpPrivateStream;
255 delete mpCommonArchive;
256 delete mpCommonStream;
257
258 /* In a parallel setting, make sure all processes have finished writing before
259 * continuing, to avoid nasty race conditions.
260 * For example, many tests will write an archive then immediately read it back
261 * in, which could easily break without this.
262 */
263 PetscTools::Barrier("~ArchiveOpener");
264 }
265
266private:
268 std::ofstream* mpCommonStream;
269
271 std::ofstream* mpPrivateStream;
272
274 OutputArchive* mpCommonArchive;
275
277 OutputArchive* mpPrivateArchive;
278};
279
280// Explicit instantiation
#define EXCEPTION(message)
static std::string GetProcessUniqueFilePath(const std::string &rFileName, unsigned procId=PetscTools::GetMyRank())
static bool GetIsDirRelativeToChasteTestOutput()
static std::string GetArchiveDirectory()
static void SetArchiveDirectory(const FileFinder &rDirectory)
static std::string GetArchiveRelativePath()
ArchiveOpener(const FileFinder &rDirectory, const std::string &rFileNameBase, unsigned procId)
ArchiveOpener(const FileFinder &rDirectory, const std::string &rFileNameBase, unsigned procId)
std::ofstream * mpCommonStream
Archive * mpCommonArchive
std::ofstream * mpPrivateStream
Archive * mpPrivateArchive
static void SetFilePermissions(const fs::path &rPath)
static bool AmMaster()
static void Barrier(const std::string callerId="")
static unsigned GetMyRank()
static void Set(Archive *pArchive)