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