openPMD-api
AbstractIOHandler.hpp
1 /* Copyright 2017-2021 Fabian Koller, Axel Huebl
2  *
3  * This file is part of openPMD-api.
4  *
5  * openPMD-api is free software: you can redistribute it and/or modify
6  * it under the terms of of either the GNU General Public License or
7  * the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * openPMD-api is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License and the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * and the GNU Lesser General Public License along with openPMD-api.
19  * If not, see <http://www.gnu.org/licenses/>.
20  */
21 #pragma once
22 
23 #include "openPMD/IO/Access.hpp"
24 #include "openPMD/IO/Format.hpp"
25 #include "openPMD/IO/IOTask.hpp"
26 #include "openPMD/config.hpp"
27 
28 #if openPMD_HAVE_MPI
29 #include <mpi.h>
30 #endif
31 
32 #include <future>
33 #include <memory>
34 #include <queue>
35 #include <stdexcept>
36 #include <string>
37 #include <type_traits>
38 
39 namespace openPMD
40 {
45 // do not write `enum class FlushLevel : unsigned char` here since NVHPC
46 // does not compile it correctly
47 enum class FlushLevel
48 {
54  UserFlush,
75 };
76 
77 namespace internal
78 {
84  struct FlushParams
85  {
87  std::string backendConfig = "{}";
88 
89  explicit FlushParams()
90  {}
91  FlushParams(FlushLevel flushLevel_in) : flushLevel(flushLevel_in)
92  {}
93  FlushParams(FlushLevel flushLevel_in, std::string backendConfig_in)
94  : flushLevel(flushLevel_in)
95  , backendConfig{std::move(backendConfig_in)}
96  {}
97  };
98 
99  /*
100  * To be used for reading
101  */
102  FlushParams const defaultFlushParams{};
103 
104  struct ParsedFlushParams;
105 
117  enum class SeriesStatus : unsigned char
118  {
119  Default,
122  Parsing
126  };
127 
128  // @todo put this somewhere else
129  template <typename Functor, typename... Args>
130  auto withRWAccess(SeriesStatus &status, Functor &&functor, Args &&...args)
131  -> decltype(std::forward<Functor>(functor)(std::forward<Args>(args)...))
132  {
133  using Res = decltype(std::forward<Functor>(functor)(
134  std::forward<Args>(args)...));
135  if constexpr (std::is_void_v<Res>)
136  {
137  auto oldStatus = status;
139  try
140  {
141  std::forward<decltype(functor)>(functor)();
142  }
143  catch (...)
144  {
145  status = oldStatus;
146  throw;
147  }
148  status = oldStatus;
149  return;
150  }
151  else
152  {
153  auto oldStatus = status;
155  Res res;
156  try
157  {
158  res = std::forward<decltype(functor)>(functor)();
159  }
160  catch (...)
161  {
162  status = oldStatus;
163  throw;
164  }
165  status = oldStatus;
166  return res;
167  }
168  }
169 } // namespace internal
170 
180 {
181  friend class Series;
182 
183 private:
184  void setIterationEncoding(IterationEncoding encoding)
185  {
186  /*
187  * In file-based iteration encoding, the APPEND mode is handled entirely
188  * by the frontend, the backend should just treat it as CREATE mode
189  */
190  if (encoding == IterationEncoding::fileBased &&
191  m_backendAccess == Access::APPEND)
192  {
193  // do we really want to have those as const members..?
194  *const_cast<Access *>(&m_backendAccess) = Access::CREATE;
195  }
196  }
197 
198 public:
199 #if openPMD_HAVE_MPI
200  AbstractIOHandler(std::string path, Access at, MPI_Comm)
201  : directory{std::move(path)}, m_backendAccess{at}, m_frontendAccess{at}
202  {}
203 #endif
204  AbstractIOHandler(std::string path, Access at)
205  : directory{std::move(path)}, m_backendAccess{at}, m_frontendAccess{at}
206  {}
207  virtual ~AbstractIOHandler() = default;
208 
214  virtual void enqueue(IOTask const &iotask)
215  {
216  m_work.push(iotask);
217  }
218 
224  std::future<void> flush(internal::FlushParams const &);
225 
231  virtual std::future<void> flush(internal::ParsedFlushParams &) = 0;
232 
234  virtual std::string backendName() const = 0;
235 
236  std::string const directory;
237  /*
238  * Originally, the reason for distinguishing these two was that during
239  * parsing in reading access modes, the access type would be temporarily
240  * const_cast'ed to an access type that would support modifying
241  * the openPMD object model. Then, it would be const_cast'ed back to
242  * READ_ONLY, to disable further modifications.
243  * Due to this approach's tendency to cause subtle bugs, and due to its
244  * difficult debugging properties, this was replaced by the SeriesStatus
245  * enum, defined in this file.
246  * The distinction of backendAccess and frontendAccess stays relevant, since
247  * the frontend can use it in order to pretend to the backend that another
248  * access type is being used. This is used by the file-based append mode,
249  * which is entirely implemented by the frontend, which internally uses
250  * the backend in CREATE mode.
251  */
252  Access const m_backendAccess;
253  Access const m_frontendAccess;
255  std::queue<IOTask> m_work;
263  bool m_lastFlushSuccessful = false;
264 }; // AbstractIOHandler
265 
266 } // namespace openPMD
Interface for communicating between logical and physically persistent data.
Definition: AbstractIOHandler.hpp:180
virtual std::future< void > flush(internal::ParsedFlushParams &)=0
Process operations in queue according to FIFO.
std::future< void > flush(internal::FlushParams const &)
Process operations in queue according to FIFO.
Definition: AbstractIOHandler.cpp:28
bool m_lastFlushSuccessful
This is to avoid that the destructor tries flushing again if an error happened.
Definition: AbstractIOHandler.hpp:263
virtual std::string backendName() const =0
The currently used backend.
virtual void enqueue(IOTask const &iotask)
Add provided task to queue according to FIFO.
Definition: AbstractIOHandler.hpp:214
Self-contained description of a single IO operation.
Definition: IOTask.hpp:697
Implementation for the root level of the openPMD hierarchy.
Definition: Series.hpp:206
SeriesStatus
Some parts of the openPMD object model are read-only when accessing a Series in Access::READ_ONLY mod...
Definition: AbstractIOHandler.hpp:118
@ Default
Mutability of objects in the openPMD object model is determined by the open mode (Access enum),...
@ Parsing
All objects in the openPMD object model are temporarily mutable to allow inserting newly-parsed data.
Public definitions of openPMD-api.
Definition: Date.cpp:29
Access
File access mode to use during IO.
Definition: Access.hpp:30
@ CREATE
create new series and truncate existing (files)
@ APPEND
write new iterations to an existing series without reading
FlushLevel
Determine what items should be flushed upon Series::flush()
Definition: AbstractIOHandler.hpp:48
@ SkeletonOnly
Restricted mode, ensures to set up the openPMD hierarchy (as far as defined so far) in the backend.
@ CreateOrOpenFiles
Only creates/opens files, nothing more.
@ InternalFlush
Default mode, used when flushes are triggered internally, e.g.
@ UserFlush
Flush operation that was triggered by user code.
IterationEncoding
Encoding scheme of an Iterations Series'.
Definition: IterationEncoding.hpp:33
Parameters recursively passed through the openPMD hierarchy when flushing.
Definition: AbstractIOHandler.hpp:85
Definition: FlushParametersInternal.hpp:32