SST 16.0.0
Structural Simulation Toolkit
sharedSet.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_SHAREDSET_H
13#define SST_CORE_SHARED_SHAREDSET_H
14
15#include "sst/core/shared/sharedObject.h"
16#include "sst/core/sst_types.h"
17
18#include <cstddef>
19#include <mutex>
20#include <set>
21#include <string>
22
23namespace SST::Shared {
24
25/**
26 SharedSet class. The class is templated to allow for an array
27 of any non-pointer type. The type must be serializable.
28 */
29template <typename valT>
30class SharedSet : public SharedObject
31{
32 static_assert(!std::is_pointer_v<valT>, "Cannot use a pointer type as value with SharedSet");
33
34 // Forward declaration. Defined below
35 class Data;
36
37public:
38 SharedSet() :
39 SharedObject(),
40 published(false),
41 data(nullptr)
42 {}
43
44 ~SharedSet()
45 {
46 // data does not need to be deleted since the
47 // SharedObjectManager owns the pointer
48 }
49
50 /**
51 Initialize the SharedSet.
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 set 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 SharedSet %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::set<valT>::const_iterator;
86 using const_reverse_iterator = typename std::set<valT>::const_reverse_iterator;
87
88 /**
89 Get the size of the set.
90
91 @return size of the set
92 */
93 inline size_t size() const { return data->getSize(); }
94
95 /**
96 Tests if the set is empty.
97
98 @return true if set is empty, false otherwise
99 */
100 inline bool empty() const { return data->set.empty(); }
101
102 /**
103 Counts elements with a specific value. Becuase this is not a
104 multiset, it will either return 1 or 0.
105
106 @return Count of elements with specified value
107 */
108 size_t count(const valT& k) const { return data->set.count(k); }
109
110 /**
111 Get const_iterator to beginning of underlying set
112 */
113 const_iterator begin() const { return data->set.cbegin(); }
114
115 /**
116 Get const_iterator to end of underlying set
117 */
118 const_iterator end() const { return data->set.cend(); }
119
120 /**
121 Get const_reverse_iterator to beginning of underlying set
122 */
123 const_reverse_iterator rbegin() const { return data->set.crbegin(); }
124
125 /**
126 Get const_reverse_iterator to end of underlying set
127 */
128 const_reverse_iterator rend() const { return data->set.crend(); }
129
130 /**
131 Indicate that the calling element has written all the data it
132 plans to write. Writing to the set through this instance
133 after publish() is called will create an error.
134 */
135 void publish()
136 {
137 if ( published ) return;
138 published = true;
139 incPublishCount(data);
140 }
141
142 /**
143 Check whether all instances of this SharedSet have called
144 publish(). NOTE: Is is possible that this could return true
145 one round, but false the next if a new instance of the
146 SharedSet was initialized but not published after the last
147 call.
148 */
149 bool isFullyPublished() { return data->isFullyPublished(); }
150
151 /**
152 Insert data to the set. This function is thread-safe, as a
153 mutex is used to ensure only one insert at a time.
154
155 @param val value of the insert
156 */
157 inline void insert(const valT& value)
158 {
159 if ( published ) {
160 Private::getSimulationOutput().fatal(
161 CALL_INFO, 1, "ERROR: insert into SharedSet %s after publish() was called\n", data->getName().c_str());
162 }
163 return data->write(value);
164 }
165
166 /**
167 Searches the SharedSet for an element equivalent to value and
168 returns a const iterator to it if found, otherwise it returns
169 an iterator to SharedSet::end.
170
171 @param value value to search for
172
173 NOTE: This function does not use a mutex, so it is possible to
174 get invalid results if another thread is simulateously writing
175 to the set. However, after the init() phase of simulation is
176 complete (in setup() and beyond), this is always a safe
177 operation. If reading during init() and you can't guarantee
178 that all elements have already written all elements to the
179 SharedSet, use mutex_find() to guarantee thread safety.
180
181 @return read-only iterator to data referenced by value
182 */
183 inline const_iterator find(const valT& value) const { return data->find(value); }
184
185 /**
186 Searches the SharedSet for an element equivalent to value and
187 returns a const iterator to it if found, otherwise it returns
188 an iterator to SharedSet::end. This version of find is always thread safe (@see
189 find()).
190
191 @param value value to search for
192
193 @return read-only iterator to data referenced by value
194 */
195 inline const_iterator mutex_find(const valT& value) const { return data->mutex_find(value); }
196
197 void serialize_order(serializer& ser) override
198 {
199 SharedObject::serialize_order(ser);
200
201 const auto mode = ser.mode();
202 initialized = data != nullptr;
203
204 if ( mode == serializer::MAP )
205 ser.mapper().map_hierarchy_start(ser.getMapName(), new ObjectMapContainer<SharedSet>(this));
206
207 SST_SER(published, SerOption::map_read_only);
208 SST_SER(initialized, SerOption::map_read_only);
209
210 switch ( mode ) {
211 case serializer::SIZER:
212 case serializer::PACK:
213 {
214 if ( !initialized ) return;
215 std::string name = data->getName();
216 SST_SER(name);
217 break;
218 }
219 case serializer::UNPACK:
220 {
221 if ( !initialized ) return;
222 std::string name;
223 SST_SER(name);
224 data = manager.getSharedObjectData<Data>(name);
225 break;
226 }
227 case serializer::MAP:
228 if ( initialized ) SST_SER_NAME(data->set, "data");
229 ser.mapper().map_hierarchy_end();
230 break;
231 }
232 }
233
234 ImplementSerializable(SharedSet<valT>)
235
236private:
237 bool published = false;
238 Data* data = nullptr;
239 bool initialized = false;
240
241 class Data : public SharedObjectData
242 {
243
244 // Forward declaration. Defined below
245 class ChangeSet;
246
247 public:
248 std::set<valT> set;
249 ChangeSet* change_set;
250
251 verify_type verify;
252
253 Data() :
255 change_set(nullptr),
256 verify(VERIFY_UNINITIALIZED)
257 {}
258 explicit Data(const std::string& name) :
259 SharedObjectData(name),
260 change_set(nullptr),
261 verify(VERIFY_UNINITIALIZED)
262 {
263 if ( Private::getNumRanks().rank > 1 ) {
264 change_set = new ChangeSet(name);
265 }
266 }
267
268 ~Data()
269 {
270 if ( change_set ) delete change_set;
271 }
272
273 void setVerify(verify_type v_type)
274 {
275 if ( v_type != verify && verify != VERIFY_UNINITIALIZED ) {
276 Private::getSimulationOutput().fatal(
277 CALL_INFO, 1, "ERROR: Type different verify_types specified for SharedSet %s\n", name.c_str());
278 }
279 verify = v_type;
280 if ( change_set ) change_set->setVerify(v_type);
281 }
282
283 size_t getSize() const
284 {
285 std::lock_guard<std::mutex> lock(mtx);
286 return set.size();
287 }
288
289 void update_write(const valT& value)
290 {
291 // Don't need to mutex because this is only ever called
292 // from one thread at a time, with barrier before and
293 // after, or from write(), which does mutex.
294 auto success = set.insert(value);
295 if ( !success.second ) {
296 // Wrote to a value that already existed
297 if ( verify != NO_VERIFY && !(value == *(success.first)) ) {
298 Private::getSimulationOutput().fatal(CALL_INFO, 1,
299 "ERROR: wrote two non-equal values to same set item in SharedSet %s\n", name.c_str());
300 }
301 }
302 }
303
304 void write(const valT& value)
305 {
306 std::lock_guard<std::mutex> lock(mtx);
307 check_lock_for_write("SharedSet");
308 update_write(value);
309 if ( change_set ) change_set->addChange(value);
310 }
311
312 // Inline the read since it may be called often during run().
313 // This read is not protected from data races in the case
314 // where the set may be simultaneously written by another
315 // thread. If there is a danger of simultaneous access
316 // during init, use the mutex_find function until after the
317 // init phase.
318 inline const_iterator find(const valT& value) { return set.find(value); }
319
320 // Mutexed read for use if you are resizing the array as you go
321 inline const_iterator mutex_find(const valT& value)
322 {
323 std::lock_guard<std::mutex> lock(mtx);
324 auto ret = set.find(value);
325 return ret;
326 }
327
328 // Functions inherited from SharedObjectData
329 virtual SharedObjectChangeSet* getChangeSet() override { return change_set; }
330 virtual void resetChangeSet() override { change_set->clear(); }
331
332 void serialize_order(serializer& ser) override
333 {
334 SharedObjectData::serialize_order(ser);
335 SST_SER(set);
336 }
337
338 ImplementSerializable(SharedSet<valT>::Data);
339
340 private:
341 class ChangeSet : public SharedObjectChangeSet
342 {
343
344 std::set<valT> changes;
345 verify_type verify;
346
347 void serialize_order(serializer& ser) override
348 {
349 SharedObjectChangeSet::serialize_order(ser);
350 SST_SER(changes);
351 SST_SER(verify);
352 }
353
354 ImplementSerializable(SharedSet<valT>::Data::ChangeSet);
355
356 public:
357 // For serialization
358 ChangeSet() :
359 SharedObjectChangeSet(),
360 verify(VERIFY_UNINITIALIZED)
361 {}
362 explicit ChangeSet(const std::string& name) :
363 SharedObjectChangeSet(name),
364 verify(VERIFY_UNINITIALIZED)
365 {}
366
367 void addChange(const valT& value) { changes.insert(value); }
368
369 void setVerify(verify_type v_type) { verify = v_type; }
370
371 void applyChanges(SharedObjectDataManager* manager) override
372 {
373 auto data = manager->getSharedObjectData<Data>(getName());
374 data->setVerify(verify);
375 for ( auto x : changes ) {
376 data->update_write(x);
377 }
378 }
379
380 void clear() override { changes.clear(); }
381 };
382 };
383};
384
385} // namespace SST::Shared
386
387#endif // SST_CORE_SHARED_SHAREDSET_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
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
Definition sharedSet.h:242
virtual void resetChangeSet() override
Resets the changeset for this data on this rank.
Definition sharedSet.h:330
virtual SharedObjectChangeSet * getChangeSet() override
Gets the changeset for this data on this rank.
Definition sharedSet.h:329
const_iterator mutex_find(const valT &value) const
Searches the SharedSet for an element equivalent to value and returns a const iterator to it if found...
Definition sharedSet.h:195
const_reverse_iterator rbegin() const
Get const_reverse_iterator to beginning of underlying set.
Definition sharedSet.h:123
const_iterator end() const
Get const_iterator to end of underlying set.
Definition sharedSet.h:118
int initialize(const std::string &obj_name, verify_type v_type=FE_VERIFY)
Initialize the SharedSet.
Definition sharedSet.h:71
const_iterator find(const valT &value) const
Searches the SharedSet for an element equivalent to value and returns a const iterator to it if found...
Definition sharedSet.h:183
bool isFullyPublished()
Check whether all instances of this SharedSet have called publish().
Definition sharedSet.h:149
const_reverse_iterator rend() const
Get const_reverse_iterator to end of underlying set.
Definition sharedSet.h:128
void publish()
Indicate that the calling element has written all the data it plans to write.
Definition sharedSet.h:135
void insert(const valT &value)
Insert data to the set.
Definition sharedSet.h:157
bool empty() const
Tests if the set is empty.
Definition sharedSet.h:100
const_iterator begin() const
Get const_iterator to beginning of underlying set.
Definition sharedSet.h:113
size_t count(const valT &k) const
Counts elements with a specific value.
Definition sharedSet.h:108
size_t size() const
Get the size of the set.
Definition sharedSet.h:93