SST 12.1.0
Structural Simulation Toolkit
sharedMap.h
1// Copyright 2009-2022 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-2022, 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
20namespace SST {
21namespace 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 */
27template <typename keyT, typename valT>
28class 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
35public:
36 SharedMap() : SharedObject(), published(false), data(nullptr) {}
37
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
223private:
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::getSimulation()->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
This class is basically a wrapper for objects to declare the order in which their members should be s...
Definition: serializer.h:35
SharedMap class.
Definition: sharedMap.h:29
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
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
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
size_t count(const keyT &k) const
Counts elements with a specific key.
Definition: sharedMap.h:102
bool isFullyPublished()
Check whether all instances of this SharedMap have called publish().
Definition: sharedMap.h:169
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
const_reverse_iterator rbegin() const
Get const_reverse_iterator to beginning of underlying map.
Definition: sharedMap.h:126
int initialize(const std::string &obj_name, verify_type v_type=FE_VERIFY)
Initialize the SharedMap.
Definition: sharedMap.h:65
const_reverse_iterator rend() const
Get const_reverse_iterator to end of underlying map.
Definition: sharedMap.h:131
const_iterator begin() const
Get const_iterator to beginning of underlying map.
Definition: sharedMap.h:116
bool empty() const
Tests if the map is empty.
Definition: sharedMap.h:94
void publish()
Indicate that the calling element has written all the data it plans to write.
Definition: sharedMap.h:155
const valT & mutex_read(const keyT &key) const
Read data from the map.
Definition: sharedMap.h:221
size_t size() const
Get the size of the map.
Definition: sharedMap.h:87
const_iterator end() const
Get const_iterator to end of underlying map.
Definition: sharedMap.h:121
const std::string & getName()
Get the name of the shared data the changeset should be applied to.
Definition: sharedObject.h:71
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
void lock()
Called by the core when writing to shared regions is no longer allowed.
Definition: sharedObject.h:185
Definition: sharedObject.h:260
verify_type
Enum of verify types.
Definition: sharedObject.h:266