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/IterationEncoding.hpp"
27 #include "openPMD/config.hpp"
28 
29 #if openPMD_HAVE_MPI
30 #include <mpi.h>
31 #endif
32 
33 #include <future>
34 #include <memory>
35 #include <queue>
36 #include <stdexcept>
37 #include <string>
38 #include <type_traits>
39 
40 namespace openPMD
41 {
46 // do not write `enum class FlushLevel : unsigned char` here since NVHPC
47 // does not compile it correctly
48 enum class FlushLevel
49 {
55  UserFlush,
76 };
77 
78 namespace internal
79 {
85  struct FlushParams
86  {
88  std::string backendConfig = "{}";
89 
90  explicit FlushParams()
91  {}
92  FlushParams(FlushLevel flushLevel_in) : flushLevel(flushLevel_in)
93  {}
94  FlushParams(FlushLevel flushLevel_in, std::string backendConfig_in)
95  : flushLevel(flushLevel_in)
96  , backendConfig{std::move(backendConfig_in)}
97  {}
98  };
99 
100  /*
101  * To be used for reading
102  */
103  FlushParams const defaultFlushParams{};
104 
105  struct ParsedFlushParams;
106 
118  enum class SeriesStatus : unsigned char
119  {
120  Default,
123  Parsing
127  };
128 
129  // @todo put this somewhere else
130  template <typename Functor, typename... Args>
131  auto withRWAccess(SeriesStatus &status, Functor &&functor, Args &&...args)
132  -> decltype(std::forward<Functor>(functor)(std::forward<Args>(args)...))
133  {
134  using Res = decltype(std::forward<Functor>(functor)(
135  std::forward<Args>(args)...));
136  if constexpr (std::is_void_v<Res>)
137  {
138  auto oldStatus = status;
140  try
141  {
142  std::forward<decltype(functor)>(functor)();
143  }
144  catch (...)
145  {
146  status = oldStatus;
147  throw;
148  }
149  status = oldStatus;
150  return;
151  }
152  else
153  {
154  auto oldStatus = status;
156  Res res;
157  try
158  {
159  res = std::forward<decltype(functor)>(functor)();
160  }
161  catch (...)
162  {
163  status = oldStatus;
164  throw;
165  }
166  status = oldStatus;
167  return res;
168  }
169  }
170 } // namespace internal
171 
172 namespace detail
173 {
174  class ADIOS2File;
175 }
176 
186 {
187  friend class Series;
188  friend class ADIOS2IOHandlerImpl;
189  friend class detail::ADIOS2File;
190 
191 private:
192  IterationEncoding m_encoding = IterationEncoding::groupBased;
193 
194  void setIterationEncoding(IterationEncoding encoding)
195  {
196  /*
197  * In file-based iteration encoding, the APPEND mode is handled entirely
198  * by the frontend, the backend should just treat it as CREATE mode.
199  * Similar for READ_LINEAR which should be treated as READ_RANDOM_ACCESS
200  * in the backend.
201  */
202  if (encoding == IterationEncoding::fileBased)
203  {
204  switch (m_backendAccess)
205  {
206 
207  case Access::READ_LINEAR:
208  // do we really want to have those as const members..?
209  *const_cast<Access *>(&m_backendAccess) =
211  break;
212  case Access::APPEND:
213  *const_cast<Access *>(&m_backendAccess) = Access::CREATE;
214  break;
216  case Access::READ_WRITE:
217  case Access::CREATE:
218  break;
219  }
220  }
221 
222  m_encoding = encoding;
223  }
224 
225 public:
226 #if openPMD_HAVE_MPI
227  AbstractIOHandler(std::string path, Access at, MPI_Comm)
228  : directory{std::move(path)}, m_backendAccess{at}, m_frontendAccess{at}
229  {}
230 #endif
231  AbstractIOHandler(std::string path, Access at)
232  : directory{std::move(path)}, m_backendAccess{at}, m_frontendAccess{at}
233  {}
234  virtual ~AbstractIOHandler() = default;
235 
236  AbstractIOHandler(AbstractIOHandler const &) = default;
237  AbstractIOHandler(AbstractIOHandler &&) = default;
238 
239  AbstractIOHandler &operator=(AbstractIOHandler const &) = default;
240  AbstractIOHandler &operator=(AbstractIOHandler &&) = default;
241 
247  virtual void enqueue(IOTask const &iotask)
248  {
249  m_work.push(iotask);
250  }
251 
257  std::future<void> flush(internal::FlushParams const &);
258 
264  virtual std::future<void> flush(internal::ParsedFlushParams &) = 0;
265 
267  virtual std::string backendName() const = 0;
268 
269  std::string directory;
270  /*
271  * Originally, the reason for distinguishing these two was that during
272  * parsing in reading access modes, the access type would be temporarily
273  * const_cast'ed to an access type that would support modifying
274  * the openPMD object model. Then, it would be const_cast'ed back to
275  * READ_ONLY, to disable further modifications.
276  * Due to this approach's tendency to cause subtle bugs, and due to its
277  * difficult debugging properties, this was replaced by the SeriesStatus
278  * enum, defined in this file.
279  * The distinction of backendAccess and frontendAccess stays relevant, since
280  * the frontend can use it in order to pretend to the backend that another
281  * access type is being used. This is used by the file-based append mode,
282  * which is entirely implemented by the frontend, which internally uses
283  * the backend in CREATE mode.
284  */
285  Access m_backendAccess;
286  Access m_frontendAccess;
288  std::queue<IOTask> m_work;
296  bool m_lastFlushSuccessful = false;
297 }; // AbstractIOHandler
298 
299 } // namespace openPMD
Definition: ADIOS2IOHandler.hpp:94
Interface for communicating between logical and physically persistent data.
Definition: AbstractIOHandler.hpp:186
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:296
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:247
Self-contained description of a single IO operation.
Definition: IOTask.hpp:725
Implementation for the root level of the openPMD hierarchy.
Definition: Series.hpp:251
Definition: ADIOS2File.hpp:137
SeriesStatus
Some parts of the openPMD object model are read-only when accessing a Series in Access::READ_ONLY mod...
Definition: AbstractIOHandler.hpp:119
@ 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
@ READ_RANDOM_ACCESS
more explicit alias for READ_ONLY
@ READ_WRITE
Open existing Series as writable.
FlushLevel
Determine what items should be flushed upon Series::flush()
Definition: AbstractIOHandler.hpp:49
@ 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:86
Definition: FlushParametersInternal.hpp:32