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