NEURON
soa_identifier.hpp
Go to the documentation of this file.
1 #pragma once
2 #include "backtrace_utils.h"
3 #include "memory_usage.hpp"
5 
6 #include <cassert>
7 #include <cstddef>
8 #include <iostream>
9 #include <limits>
10 #include <memory>
11 #include <ostream>
12 #include <vector>
13 
14 #include "utils/logger.hpp"
15 
16 #include "hocdec.h"
17 
18 namespace neuron::container {
19 /**
20  * @brief A non-owning permutation-stable identifier for a entry in a container.
21  * @tparam Storage The type of the referred-to container. This might be a type
22  * derived from @ref neuron::container::soa<...>, or a plain
23  * container like std::vector<T> in the case of @ref
24  * neuron::container::data_handle<T>.
25  *
26  * A (non-owning) handle to that row combines an instance of that class with an
27  * interface that is specific to Storage. non_owning_identifier<Storage> wraps
28  * non_owning_identifier_without_container so as to provide the same interface
29  * as owning_identifier<Storage>.
30  */
31 template <typename Storage>
35  , m_storage{storage} {}
36 
37  /**
38  * @brief Return a reference to the container in which this entry lives.
39  */
40  Storage& underlying_storage() {
42  return *m_storage;
43  }
44 
45  /**
46  * @brief Return a const reference to the container in which this entry lives.
47  */
48  Storage const& underlying_storage() const {
50  return *m_storage;
51  }
52 
53  using storage_type = Storage;
54 
55  private:
56  Storage* m_storage;
57 };
58 
59 namespace detail {
61 }
62 
63 /**
64  * @brief An owning permutation-stable identifier for a entry in a container.
65  * @tparam Storage The type of the referred-to container, which is expected to
66  * be a type derived from @ref neuron::container::soa<...>
67  *
68  * This identifier has owning semantics, meaning that when it is destroyed the
69  * corresponding entry in the container is deleted.
70  */
71 template <typename Storage>
73  /**
74  * @brief Create a non-null owning identifier by creating a new entry.
75  */
76  owning_identifier(Storage& storage)
77  : owning_identifier(storage.acquire_owning_identifier()) {}
78 
81  : m_ptr(std::move(other.m_ptr))
82  , m_data_ptr(other.m_data_ptr) {
83  other.m_data_ptr = nullptr;
84  }
85 
88  destroy();
89 
90  m_ptr = std::move(other.m_ptr);
91 
92  m_data_ptr = other.m_data_ptr;
93  other.m_data_ptr = nullptr;
94 
95  return *this;
96  }
97 
99  destroy();
100  }
101 
102  /**
103  * @brief Return a reference to the container in which this entry lives.
104  */
105  Storage& underlying_storage() {
106  return *m_data_ptr;
107  }
108 
109  /**
110  * @brief Return a const reference to the container in which this entry lives.
111  */
112  Storage const& underlying_storage() const {
113  return *m_data_ptr;
114  }
115 
116  [[nodiscard]] operator non_owning_identifier<Storage>() const {
117  return {m_data_ptr, m_ptr};
118  }
119 
120  [[nodiscard]] operator non_owning_identifier_without_container() const {
121  return static_cast<non_owning_identifier<Storage>>(*this);
122  }
123 
124  /**
125  * @brief What is the current row?
126  *
127  * The returned value is invalidated by any deletions from the underlying
128  * container, and by any permutations of the underlying container.
129  */
130  [[nodiscard]] std::size_t current_row() const {
131  assert(m_ptr);
132  return m_ptr.current_row();
133  }
134 
135  friend std::ostream& operator<<(std::ostream& os, owning_identifier const& oi) {
136  return os << "owning " << non_owning_identifier<Storage>{oi};
137  }
138 
139  using storage_type = Storage;
140 
141  private:
142  owning_identifier() = default;
143 
144  void destroy() {
145  if (!m_ptr) {
146  // Nothing to be done.
147  return;
148  }
149 
151  auto& data_container = *m_data_ptr;
152 
153  // We should still be a valid reference at this point.
154  assert(m_ptr);
155  assert(m_ptr.current_row() < data_container.size());
156 
157  // Prove that the bookkeeping works.
158  assert(data_container.at(m_ptr.current_row()) == m_ptr);
159 
160  bool terminate = false;
161  // Delete the corresponding row from `data_container`
162  try {
163  data_container.erase(m_ptr.current_row());
164  } catch (std::exception const& e) {
165  // Cannot throw from unique_ptr release/reset/destructor, this
166  // is the best we can do. Most likely what has happened is
167  // something like:
168  // auto const read_only_token = node_data.issue_frozen_token();
169  // list_of_nodes.pop_back();
170  // which tries to delete a row from a container in read-only mode.
171  Fprintf(stderr,
172  fmt::format(
173  "neuron::container::owning_identifier<{}> destructor could not delete from "
174  "the underlying storage: {} [{}]. This is not recoverable, aborting.\n",
175  cxx_demangle(typeid(Storage).name()),
176  e.what(),
177  cxx_demangle(typeid(e).name()))
178  .c_str());
179  terminate = true;
180  }
181  if (terminate) {
182  std::terminate();
183  }
184  // We don't know how many people know the pointer `p`, so write a sentinel
185  // value to it and transfer ownership "elsewhere".
187 
188  // This is to provide compatibility with NEURON's old nrn_notify_when_double_freed and
189  // nrn_notify_when_void_freed methods.
191  }
192 
193 
195  Storage* m_data_ptr{};
196 
197  template <typename, typename...>
198  friend struct soa;
199  void set_current_row(std::size_t new_row) {
200  assert(m_ptr);
201  m_ptr.set_current_row(new_row);
202  }
203  /**
204  * @brief Create a non-null owning identifier that owns the given row.
205  *
206  * This is used inside
207  * neuron::container::soa<...>::acquire_owning_identifier() and should not
208  * be used without great care.
209  */
210  owning_identifier(Storage& storage, std::size_t row)
211  : m_ptr(row)
212  , m_data_ptr(&storage) {}
213 };
214 } // namespace neuron::container
int cxx_demangle(const char *symbol, char **funcname, size_t *funcname_sz)
#define id
Definition: md1redef.h:41
#define assert(ex)
Definition: hocassrt.h:24
const char * name
Definition: init.cpp:16
void move(Item *q1, Item *q2, Item *q3)
Definition: list.cpp:200
void notify_handle_dying(non_owning_identifier_without_container p)
Respond to the news that data_handles relying on p are now dead.
Definition: ivoc.cpp:114
constexpr std::size_t invalid_row
static unsigned row
Definition: nonlin.cpp:78
A non-owning permutation-stable identifier for an entry in a container.
A non-owning permutation-stable identifier for a entry in a container.
non_owning_identifier(Storage *storage, non_owning_identifier_without_container id)
Storage const & underlying_storage() const
Return a const reference to the container in which this entry lives.
Storage & underlying_storage()
Return a reference to the container in which this entry lives.
An owning permutation-stable identifier for a entry in a container.
std::size_t current_row() const
What is the current row?
owning_identifier(const owning_identifier &)=delete
owning_identifier & operator=(owning_identifier &&other)
owning_identifier & operator=(const owning_identifier &)=delete
friend std::ostream & operator<<(std::ostream &os, owning_identifier const &oi)
non_owning_identifier_without_container m_ptr
Storage const & underlying_storage() const
Return a const reference to the container in which this entry lives.
Storage & underlying_storage()
Return a reference to the container in which this entry lives.
owning_identifier(Storage &storage, std::size_t row)
Create a non-null owning identifier that owns the given row.
owning_identifier(owning_identifier &&other) noexcept
void set_current_row(std::size_t new_row)
owning_identifier(Storage &storage)
Create a non-null owning identifier by creating a new entry.
int Fprintf(FILE *stream, const char *fmt, Args... args)
Definition: logger.hpp:8