Chaste Commit::675f9facbe008c5eacb9006feaeb6423206579ea
ArchiveOpener.cpp
1/*
2
3Copyright (c) 2005-2025, 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 "OutputFileHandler.hpp"
49#include "ProcessSpecificArchive.hpp"
50
56template <class InputArchive>
57class ArchiveOpener<InputArchive, std::ifstream>
58{
59private:
60 friend class TestArchivingHelperClasses;
61
62public:
70 const FileFinder& rDirectory,
71 const std::string& rFileNameBase,
72 unsigned procId)
73 : mpCommonStream(nullptr),
74 mpPrivateStream(nullptr),
75 mpCommonArchive(nullptr),
76 mpPrivateArchive(nullptr)
77 {
78 // Figure out where things live
80 std::string private_path = ArchiveLocationInfo::GetProcessUniqueFilePath(rFileNameBase, procId);
81 std::stringstream common_path;
82 common_path << ArchiveLocationInfo::GetArchiveDirectory() << rFileNameBase;
83
84 // Try to open the main archive for replicated data
85 mpCommonStream = new std::ifstream(common_path.str().c_str(), std::ios::binary);
86 if (!mpCommonStream->is_open())
87 {
88 delete mpCommonStream;
89 EXCEPTION("Cannot load main archive file: " + common_path.str());
90 }
91
92 try
93 {
94 mpCommonArchive = new InputArchive(*mpCommonStream);
95 }
96 catch (boost::archive::archive_exception& boost_exception)
97 {
98 if (boost_exception.code == boost::archive::archive_exception::unsupported_version)
99 {
100 // This is forward compatibility issue. We can't open the archive because it's been written by a more recent Boost.
101 delete mpCommonArchive;
102 delete mpCommonStream;
103 EXCEPTION("Could not open Boost archive '" + common_path.str() + "' because it was written by a more recent Boost. Check process-specific archives too");
104 }
105 else
106 {
107 // We don't understand the exception, so we shouldn't continue
108 throw boost_exception; // LCOV_EXCL_LINE
109 }
110 }
111
112 // Try to open the secondary archive for distributed data
113 mpPrivateStream = new std::ifstream(private_path.c_str(), std::ios::binary);
114 if (!mpPrivateStream->is_open())
115 {
116 delete mpPrivateStream;
117 delete mpCommonArchive;
118 delete mpCommonStream;
119 EXCEPTION("Cannot load secondary archive file: " + private_path);
120 }
121 mpPrivateArchive = new InputArchive(*mpPrivateStream);
123 }
124
128 InputArchive* GetCommonArchive()
129 {
130 assert(mpCommonArchive != NULL);
131 return mpCommonArchive;
132 }
133
135 {
137 delete mpPrivateArchive;
138 delete mpPrivateStream;
139 delete mpCommonArchive;
140 delete mpCommonStream;
141 }
142
143private:
145 std::ifstream* mpCommonStream;
146
148 std::ifstream* mpPrivateStream;
149
151 InputArchive* mpCommonArchive;
152
154 InputArchive* mpPrivateArchive;
155};
156
162template <class OutputArchive>
163class ArchiveOpener<OutputArchive, std::ofstream>
164{
165private:
166 friend class TestArchivingHelperClasses;
167
168public:
176 const FileFinder& rDirectory,
177 const std::string& rFileNameBase,
178 unsigned procId)
179 : mpCommonStream(nullptr),
180 mpPrivateStream(nullptr),
181 mpCommonArchive(nullptr),
182 mpPrivateArchive(nullptr)
183 {
184 // Check for user error
185 if (procId != PetscTools::GetMyRank())
186 {
187 EXCEPTION("Specifying the secondary archive file ID doesn't make sense when writing.");
188 }
189
190 // Figure out where things live
193 {
194 // Ensure the directory exists
196 }
197 std::string private_path = ArchiveLocationInfo::GetProcessUniqueFilePath(rFileNameBase);
198 std::stringstream common_path;
199 common_path << ArchiveLocationInfo::GetArchiveDirectory() << rFileNameBase;
200
201 // Create master archive for replicated data
203 {
204 mpCommonStream = new std::ofstream(common_path.str().c_str(), std::ios::binary | std::ios::trunc);
205 if (!mpCommonStream->is_open())
206 {
207 delete mpCommonStream;
208 EXCEPTION("Failed to open main archive file for writing: " + common_path.str());
209 }
210 }
211 else
212 {
213 // Non-master processes need to go through the serialization methods, but not write any data
214#ifdef _MSC_VER
215 mpCommonStream = new std::ofstream("NUL", std::ios::binary | std::ios::trunc);
216#else
217 mpCommonStream = new std::ofstream("/dev/null", std::ios::binary | std::ios::trunc);
218#endif
219 // LCOV_EXCL_START
220 if (!mpCommonStream->is_open())
221 {
222 delete mpCommonStream;
223 EXCEPTION("Failed to open dummy archive file '/dev/null' for writing");
224 }
225 // LCOV_EXCL_STOP
226 }
227 mpCommonArchive = new OutputArchive(*mpCommonStream);
228
229 // Create secondary archive for distributed data
230 mpPrivateStream = new std::ofstream(private_path.c_str(), std::ios::binary | std::ios::trunc);
231 if (!mpPrivateStream->is_open())
232 {
233 delete mpPrivateStream;
234 delete mpCommonArchive;
235 delete mpCommonStream;
236 EXCEPTION("Failed to open secondary archive file for writing: " + private_path);
237 }
238 mpPrivateArchive = new OutputArchive(*mpPrivateStream);
240 }
241
245 OutputArchive* GetCommonArchive()
246 {
247 assert(mpCommonArchive != NULL);
248 return mpCommonArchive;
249 }
250
252 {
254 delete mpPrivateArchive;
255 delete mpPrivateStream;
256 delete mpCommonArchive;
257 delete mpCommonStream;
258
259 /* In a parallel setting, make sure all processes have finished writing before
260 * continuing, to avoid nasty race conditions.
261 * For example, many tests will write an archive then immediately read it back
262 * in, which could easily break without this.
263 */
264 PetscTools::Barrier("~ArchiveOpener");
265 }
266
267private:
269 std::ofstream* mpCommonStream;
270
272 std::ofstream* mpPrivateStream;
273
275 OutputArchive* mpCommonArchive;
276
278 OutputArchive* mpPrivateArchive;
279};
280
281// 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 bool AmMaster()
static void Barrier(const std::string callerId="")
static unsigned GetMyRank()
static void Set(Archive *pArchive)