SST  14.0.0
StructuralSimulationToolkit
sharedMap.h
1 // Copyright 2009-2024 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-2024, 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  void serialize_order(SST::Core::Serialization::serializer& ser) override
224  {
225  SST::Shared::SharedObject::serialize_order(ser);
226  ser& published;
227  switch ( ser.mode() ) {
228  case SST::Core::Serialization::serializer::SIZER:
229  case SST::Core::Serialization::serializer::PACK:
230  {
231  std::string name = data->getName();
232  ser& name;
233  break;
234  }
235  case SST::Core::Serialization::serializer::UNPACK:
236  {
237  std::string name;
238  ser& name;
239  data = manager.getSharedObjectData<Data>(name);
240  break;
241  }
242  };
243  }
244  ImplementSerializable(SST::Shared::SharedMap<keyT, valT>)
245 private:
246  bool published;
247  Data* data;
248 
249  class Data : public SharedObjectData
250  {
251 
252  // Forward declaration. Defined below
253  class ChangeSet;
254 
255  public:
256  std::map<keyT, valT> map;
257  ChangeSet* change_set;
258  verify_type verify;
259 
260  Data() : SharedObjectData(), change_set(nullptr), verify(VERIFY_UNINITIALIZED) {}
261  Data(const std::string& name) : SharedObjectData(name), change_set(nullptr), verify(VERIFY_UNINITIALIZED)
262  {
263  if ( Private::getNumRanks().rank > 1 ) { change_set = new ChangeSet(name); }
264  }
265 
266  ~Data()
267  {
268  if ( change_set ) delete change_set;
269  }
270 
271  void setVerify(verify_type v_type)
272  {
273  if ( v_type != verify && verify != VERIFY_UNINITIALIZED ) {
274  Private::getSimulationOutput().fatal(
275  CALL_INFO, 1, "ERROR: Two different verify_types specified for SharedMap %s\n", name.c_str());
276  }
277  verify = v_type;
278  if ( change_set ) change_set->setVerify(v_type);
279  }
280 
281  size_t getSize() const { return map.size(); }
282 
283  void update_write(const keyT& key, const valT& value)
284  {
285  // Don't need to mutex because this is only ever called
286  // from one thread at a time, with barrier before and
287  // after, or from write(), which does mutex.
288  auto success = map.insert(std::make_pair(key, value));
289  if ( !success.second ) {
290  // Wrote to a key that already existed
291  if ( verify != NO_VERIFY && value != success.first->second ) {
292  Private::getSimulationOutput().fatal(
293  CALL_INFO, 1, "ERROR: wrote two different values to same key in SharedMap %s\n", name.c_str());
294  }
295  }
296  }
297 
298  void write(const keyT& key, const valT& value)
299  {
300  std::lock_guard<std::mutex> lock(mtx);
301  check_lock_for_write("SharedMap");
302  update_write(key, value);
303  if ( change_set ) change_set->addChange(key, value);
304  }
305 
306  // Inline the read since it may be called often during run().
307  // This read is not protected from data races in the case
308  // where the map may be simulataeously written by another
309  // thread. If there is a danger of simultaneous access
310  // during init, use the mutex_read function until after the
311  // init phase.
312  inline const valT& read(const keyT& key) { return map.at(key); }
313 
314  // Mutexed read for use if you are resizing the array as you go
315  inline const valT& mutex_read(const keyT& key)
316  {
317  std::lock_guard<std::mutex> lock(mtx);
318  auto ret = map.at(key);
319  return ret;
320  }
321 
322  // Functions inherited from SharedObjectData
323  virtual SharedObjectChangeSet* getChangeSet() override { return change_set; }
324  virtual void resetChangeSet() override { change_set->clear(); }
325 
326  void serialize_order(SST::Core::Serialization::serializer& ser) override
327  {
328  SharedObjectData::serialize_order(ser);
329  ser& map;
330  }
331 
332  ImplementSerializable(SST::Shared::SharedMap<keyT, valT>::Data);
333 
334  private:
335  class ChangeSet : public SharedObjectChangeSet
336  {
337 
338  std::map<keyT, valT> changes;
339  verify_type verify;
340 
341  void serialize_order(SST::Core::Serialization::serializer& ser) override
342  {
343  SharedObjectChangeSet::serialize_order(ser);
344  ser& changes;
345  ser& verify;
346  }
347 
349 
350  public:
351  // For serialization
352  ChangeSet() : SharedObjectChangeSet(), verify(VERIFY_UNINITIALIZED) {}
353  ChangeSet(const std::string& name) : SharedObjectChangeSet(name), verify(VERIFY_UNINITIALIZED) {}
354 
355  void addChange(const keyT& key, const valT& value) { changes[key] = value; }
356 
357  void setVerify(verify_type v_type) { verify = v_type; }
358 
359  void applyChanges(SharedObjectDataManager* manager) override
360  {
361  auto data = manager->getSharedObjectData<Data>(getName());
362  data->setVerify(verify);
363  for ( auto x : changes ) {
364  data->update_write(x.first, x.second);
365  }
366  }
367 
368  void clear() override { changes.clear(); }
369  };
370  };
371 };
372 
373 } // namespace Shared
374 } // namespace SST
375 
376 #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:35
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:269
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
bool isFullyPublished()
Checks to see if all instances of this SharedObject have called publish().
Definition: sharedObject.h:109
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:276
virtual void resetChangeSet() override
Resets the changeset for this data on this rank.
Definition: sharedMap.h:324
Definition: sharedMap.h:249
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
virtual SharedObjectChangeSet * getChangeSet() override
Gets the changeset for this data on this rank.
Definition: sharedMap.h:323
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
const std::string & getName()
Get the name of the SharedObject for this data.
Definition: sharedObject.h:99
virtual void applyChanges(SharedObjectDataManager *UNUSED(manager))=0
Apply the changes to the name shared data.
This is the base class for holding data on changes made to the shared data on each rank...
Definition: sharedObject.h:44
Base class for holding SharedObject data.
Definition: sharedObject.h:87