SST  13.0.0
StructuralSimulationToolkit
sharedMap.h
1 // Copyright 2009-2023 NTESS. Under the terms
2 // of Contract DE-NA0003525 with NTESS, the U.S.
3 // Government retains certain rights in this software.
4 //
5 // Copyright (c) 2009-2023, NTESS
6 // All rights reserved.
7 //
8 // This file is part of the SST software package. For license
9 // information, see the LICENSE file in the top level directory of the
10 // distribution.
11 
12 #ifndef SST_CORE_SHARED_SHAREDMAP_H
13 #define SST_CORE_SHARED_SHAREDMAP_H
14 
15 #include "sst/core/shared/sharedObject.h"
16 #include "sst/core/sst_types.h"
17 
18 #include <map>
19 
20 namespace SST {
21 namespace Shared {
22 
23 /**
24  SharedMap class. The class is templated to allow for Map of any
25  non-pointer type as value. The type must be serializable.
26  */
27 template <typename keyT, typename valT>
28 class SharedMap : public SharedObject
29 {
30  static_assert(!std::is_pointer<valT>::value, "Cannot use a pointer type as value with SharedMap");
31 
32  // Forward declaration. Defined below
33  class Data;
34 
35 public:
36  SharedMap() : SharedObject(), published(false), data(nullptr) {}
37 
38  ~SharedMap()
39  {
40  // data does not need to be deleted since the
41  // SharedObjectManager owns the pointer
42  }
43 
44  /**
45  Initialize the SharedMap.
46 
47  @param obj_name Name of the object. This name is how the
48  object is uniquely identified across ranks.
49 
50  @param verify_mode Specifies how multiply written data should
51  be verified. Since the underlying map knows if the data has
52  already been written, FE_VERIFY and INIT_VERIFY simply use this
53  built-in mechanism to know when an item has previously been
54  written. When these modes are enabled, multiply written data
55  must match what was written before. When NO_VERIFY is passed,
56  no verification will occur. This is mostly useful when you can
57  guarantee that multiple elements won't write the same value and
58  you want to do in-place modifications as you initialize.
59  VERIFY_UNINITIALIZED is a reserved value and should not be
60  passed.
61 
62  @return returns the number of instances that have intialized
63  themselve before this instance on this MPI rank.
64  */
65  int initialize(const std::string& obj_name, verify_type v_type = FE_VERIFY)
66  {
67  if ( data ) {
68  Private::getSimulationOutput().fatal(
69  CALL_INFO, 1, "ERROR: called initialize() of SharedMap %s more than once\n", obj_name.c_str());
70  }
71 
72  data = manager.getSharedObjectData<Data>(obj_name);
73  int ret = incShareCount(data);
74  data->setVerify(v_type);
75  return ret;
76  }
77 
78  /*** Typedefs and functions to mimic parts of the vector API ***/
79  typedef typename std::map<keyT, valT>::const_iterator const_iterator;
80  typedef typename std::map<keyT, valT>::const_reverse_iterator const_reverse_iterator;
81 
82  /**
83  Get the size of the map.
84 
85  @return size of the map
86  */
87  inline size_t size() const { return data->getSize(); }
88 
89  /**
90  Tests if the map is empty.
91 
92  @return true if map is empty, false otherwise
93  */
94  inline bool empty() const { return data->map.empty(); }
95 
96  /**
97  Counts elements with a specific key. Becuase this is not a
98  multimap, it will either return 1 or 0.
99 
100  @return Count of elements with specified key
101  */
102  size_t count(const keyT& k) const { return data->map.count(k); }
103 
104  /**
105  Searches the container for an element with a key equivalent to
106  k and returns an iterator to it if found, otherwise it returns
107  an iterator to SharedMap::end().
108 
109  @param key key to search for
110  */
111  const_iterator find(const keyT& key) const { return data->map.find(key); }
112 
113  /**
114  Get const_iterator to beginning of underlying map
115  */
116  const_iterator begin() const { return data->map.cbegin(); }
117 
118  /**
119  Get const_iterator to end of underlying map
120  */
121  const_iterator end() const { return data->map.cend(); }
122 
123  /**
124  Get const_reverse_iterator to beginning of underlying map
125  */
126  const_reverse_iterator rbegin() const { return data->map.crbegin(); }
127 
128  /**
129  Get const_reverse_iterator to end of underlying map
130  */
131  const_reverse_iterator rend() const { return data->map.crend(); }
132 
133  /**
134  Returns an iterator pointing to the first element in the
135  container whose key is not considered to go before k (i.e.,
136  either it is equivalent or goes after).
137 
138  @param key key to compare to
139  */
140  inline const_iterator lower_bound(const keyT& key) const { return data->map.lower_bound(key); }
141 
142  /**
143  Returns an iterator pointing to the first element in the
144  container whose key is considered to go after k.
145 
146  @param key key to compare to
147  */
148  inline const_iterator upper_bound(const keyT& key) const { return data->map.lower_bound(key); }
149 
150  /**
151  Indicate that the calling element has written all the data it
152  plans to write. Writing to the map through this instance
153  after publish() is called will create an error.
154  */
155  void publish()
156  {
157  if ( published ) return;
158  published = true;
159  incPublishCount(data);
160  }
161 
162  /**
163  Check whether all instances of this SharedMap have called
164  publish(). NOTE: Is is possible that this could return true
165  one round, but false the next if a new instance of the
166  SharedMap was initialized but not published after the last
167  call.
168  */
169  bool isFullyPublished() { return data->isFullyPublished(); }
170 
171  /**
172  Write data to the map. This function is thread-safe, as a
173  mutex is used to ensure only one write at a time.
174 
175  @param key key of the write
176 
177  @param value value to be written
178  */
179  inline void write(const keyT& key, const valT& value)
180  {
181  if ( published ) {
182  Private::getSimulationOutput().fatal(
183  CALL_INFO, 1, "ERROR: write to SharedMap %s after publish() was called\n", data->getName().c_str());
184  }
185  return data->write(key, value);
186  }
187 
188  /**
189  Read data from the map. This returns a const reference, so is
190  read only. If the key is not in the map, an out_of_range
191  exception will be thrown.
192 
193  NOTE: This function does not use a mutex, so it is possible to
194  get invalid results if another thread is simulateously writing
195  to the map. However, after the init() phase of simulation is
196  complete (in setup() and beyond), this is always a safe
197  operation. If reading during init() and you can't guarantee
198  that all elements have already written all elements to the
199  SharedMap, use mutex_read() to guarantee thread safety.
200 
201  @param key key to read
202 
203  @return const reference to data referenced by key
204 
205  @exception std::out_of_range key is not found in map
206  */
207  inline const valT& operator[](const keyT& key) const { return data->read(key); }
208 
209  /**
210  Read data from the map. This returns a const reference, so
211  is read only. This version of read is always thread safe (@see
212  operator[]). If the key is not in the map, an out_of_range
213  exception will be thrown.
214 
215  @param key key to read
216 
217  @return const reference to data at index
218 
219  @exception std::out_of_range key is not found in map
220  */
221  inline const valT& mutex_read(const keyT& key) const { return data->mutex_read(key); }
222 
223 private:
224  bool published;
225  Data* data;
226 
227  class Data : public SharedObjectData
228  {
229 
230  // Forward declaration. Defined below
231  class ChangeSet;
232 
233  public:
234  std::map<keyT, valT> map;
235  ChangeSet* change_set;
236  verify_type verify;
237 
238  Data(const std::string& name) : SharedObjectData(name), change_set(nullptr), verify(VERIFY_UNINITIALIZED)
239  {
240  if ( Private::getNumRanks().rank > 1 ) { change_set = new ChangeSet(name); }
241  }
242 
243  ~Data() { delete change_set; }
244 
245  void setVerify(verify_type v_type)
246  {
247  if ( v_type != verify && verify != VERIFY_UNINITIALIZED ) {
248  Private::getSimulationOutput().fatal(
249  CALL_INFO, 1, "ERROR: Two different verify_types specified for SharedMap %s\n", name.c_str());
250  }
251  verify = v_type;
252  if ( change_set ) change_set->setVerify(v_type);
253  }
254 
255  size_t getSize() const { return map.size(); }
256 
257  void update_write(const keyT& key, const valT& value)
258  {
259  // Don't need to mutex because this is only ever called
260  // from one thread at a time, with barrier before and
261  // after, or from write(), which does mutex.
262  auto success = map.insert(std::make_pair(key, value));
263  if ( !success.second ) {
264  // Wrote to a key that already existed
265  if ( verify != NO_VERIFY && value != success.first->second ) {
266  Private::getSimulationOutput().fatal(
267  CALL_INFO, 1, "ERROR: wrote two different values to same key in SharedMap %s\n", name.c_str());
268  }
269  }
270  }
271 
272  void write(const keyT& key, const valT& value)
273  {
274  std::lock_guard<std::mutex> lock(mtx);
275  check_lock_for_write("SharedMap");
276  update_write(key, value);
277  if ( change_set ) change_set->addChange(key, value);
278  }
279 
280  // Inline the read since it may be called often during run().
281  // This read is not protected from data races in the case
282  // where the map may be simulataeously written by another
283  // thread. If there is a danger of simultaneous access
284  // during init, use the mutex_read function until after the
285  // init phase.
286  inline const valT& read(const keyT& key) { return map.at(key); }
287 
288  // Mutexed read for use if you are resizing the array as you go
289  inline const valT& mutex_read(const keyT& key)
290  {
291  std::lock_guard<std::mutex> lock(mtx);
292  auto ret = map.at(key);
293  return ret;
294  }
295 
296  // Functions inherited from SharedObjectData
297  virtual SharedObjectChangeSet* getChangeSet() override { return change_set; }
298  virtual void resetChangeSet() override { change_set->clear(); }
299 
300  private:
301  class ChangeSet : public SharedObjectChangeSet
302  {
303 
304  std::map<keyT, valT> changes;
305  verify_type verify;
306 
307  void serialize_order(SST::Core::Serialization::serializer& ser) override
308  {
309  SharedObjectChangeSet::serialize_order(ser);
310  ser& changes;
311  ser& verify;
312  }
313 
315 
316  public:
317  // For serialization
318  ChangeSet() : SharedObjectChangeSet(), verify(VERIFY_UNINITIALIZED) {}
319  ChangeSet(const std::string& name) : SharedObjectChangeSet(name), verify(VERIFY_UNINITIALIZED) {}
320 
321  void addChange(const keyT& key, const valT& value) { changes[key] = value; }
322 
323  void setVerify(verify_type v_type) { verify = v_type; }
324 
325  void applyChanges(SharedObjectDataManager* manager) override
326  {
327  auto data = manager->getSharedObjectData<Data>(getName());
328  data->setVerify(verify);
329  for ( auto x : changes ) {
330  data->update_write(x.first, x.second);
331  }
332  }
333 
334  void clear() override { changes.clear(); }
335  };
336  };
337 };
338 
339 } // namespace Shared
340 } // namespace SST
341 
342 #endif // SST_CORE_SHARED_SHAREDMAP_H
const_iterator lower_bound(const keyT &key) const
Returns an iterator pointing to the first element in the container whose key is not considered to go ...
Definition: sharedMap.h:140
const valT & mutex_read(const keyT &key) const
Read data from the map.
Definition: sharedMap.h:221
This class is basically a wrapper for objects to declare the order in which their members should be s...
Definition: serializer.h:34
const_iterator find(const keyT &key) const
Searches the container for an element with a key equivalent to k and returns an iterator to it if fou...
Definition: sharedMap.h:111
Definition: sharedObject.h:260
const_iterator begin() const
Get const_iterator to beginning of underlying map.
Definition: sharedMap.h:116
void lock()
Called by the core when writing to shared regions is no longer allowed.
Definition: sharedObject.h:186
const std::string & getName()
Get the name of the shared data the changeset should be applied to.
Definition: sharedObject.h:72
Definition: action.cc:18
bool isFullyPublished()
Check whether all instances of this SharedMap have called publish().
Definition: sharedMap.h:169
const_iterator end() const
Get const_iterator to end of underlying map.
Definition: sharedMap.h:121
int initialize(const std::string &obj_name, verify_type v_type=FE_VERIFY)
Initialize the SharedMap.
Definition: sharedMap.h:65
SharedMap class.
Definition: sharedMap.h:28
verify_type
Enum of verify types.
Definition: sharedObject.h:267
size_t count(const keyT &k) const
Counts elements with a specific key.
Definition: sharedMap.h:102
size_t size() const
Get the size of the map.
Definition: sharedMap.h:87
const_reverse_iterator rend() const
Get const_reverse_iterator to end of underlying map.
Definition: sharedMap.h:131
void write(const keyT &key, const valT &value)
Write data to the map.
Definition: sharedMap.h:179
const valT & operator[](const keyT &key) const
Read data from the map.
Definition: sharedMap.h:207
const_reverse_iterator rbegin() const
Get const_reverse_iterator to beginning of underlying map.
Definition: sharedMap.h:126
const_iterator upper_bound(const keyT &key) const
Returns an iterator pointing to the first element in the container whose key is considered to go afte...
Definition: sharedMap.h:148
void publish()
Indicate that the calling element has written all the data it plans to write.
Definition: sharedMap.h:155
bool empty() const
Tests if the map is empty.
Definition: sharedMap.h:94
virtual void applyChanges(SharedObjectDataManager *UNUSED(manager))=0
Apply the changes to the name shared data.
Base class for holding SharedObject data.
Definition: sharedObject.h:87