SST 15.0
Structural Simulation Toolkit
sharedSet.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_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(SST::Core::Serialization::serializer& ser) override
198 {
199 SST::Shared::SharedObject::serialize_order(ser);
200 SST_SER(published);
201 switch ( ser.mode() ) {
202 case SST::Core::Serialization::serializer::SIZER:
203 case SST::Core::Serialization::serializer::PACK:
204 {
205 std::string name = data->getName();
206 SST_SER(name);
207 break;
208 }
209 case SST::Core::Serialization::serializer::UNPACK:
210 {
211 std::string name;
212 SST_SER(name);
213 data = manager.getSharedObjectData<Data>(name);
214 break;
215 }
216 case SST::Core::Serialization::serializer::MAP:
217 // Add your code here
218 break;
219 };
220 }
221 ImplementSerializable(SST::Shared::SharedSet<valT>)
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::set<valT> set;
235 ChangeSet* change_set;
236
237 verify_type verify;
238
239 Data() :
241 change_set(nullptr),
242 verify(VERIFY_UNINITIALIZED)
243 {}
244 explicit Data(const std::string& name) :
245 SharedObjectData(name),
246 change_set(nullptr),
247 verify(VERIFY_UNINITIALIZED)
248 {
249 if ( Private::getNumRanks().rank > 1 ) {
250 change_set = new ChangeSet(name);
251 }
252 }
253
254 ~Data()
255 {
256 if ( change_set ) delete change_set;
257 }
258
259 void setVerify(verify_type v_type)
260 {
261 if ( v_type != verify && verify != VERIFY_UNINITIALIZED ) {
262 Private::getSimulationOutput().fatal(
263 CALL_INFO, 1, "ERROR: Type different verify_types specified for SharedSet %s\n", name.c_str());
264 }
265 verify = v_type;
266 if ( change_set ) change_set->setVerify(v_type);
267 }
268
269 size_t getSize() const
270 {
271 std::lock_guard<std::mutex> lock(mtx);
272 return set.size();
273 }
274
275 void update_write(const valT& value)
276 {
277 // Don't need to mutex because this is only ever called
278 // from one thread at a time, with barrier before and
279 // after, or from write(), which does mutex.
280 auto success = set.insert(value);
281 if ( !success.second ) {
282 // Wrote to a value that already existed
283 if ( verify != NO_VERIFY && !(value == *(success.first)) ) {
284 Private::getSimulationOutput().fatal(CALL_INFO, 1,
285 "ERROR: wrote two non-equal values to same set item in SharedSet %s\n", name.c_str());
286 }
287 }
288 }
289
290 void write(const valT& value)
291 {
292 // std::lock_guard<std::mutex> lock(mtx);
293 check_lock_for_write("SharedSet");
294 update_write(value);
295 if ( change_set ) change_set->addChange(value);
296 }
297
298 // Inline the read since it may be called often during run().
299 // This read is not protected from data races in the case
300 // where the set may be simultaneously written by another
301 // thread. If there is a danger of simultaneous access
302 // during init, use the mutex_find function until after the
303 // init phase.
304 inline const_iterator find(const valT& value) { return set.find(value); }
305
306 // Mutexed read for use if you are resizing the array as you go
307 inline const_iterator mutex_find(const valT& value)
308 {
309 std::lock_guard<std::mutex> lock(mtx);
310 auto ret = set.find(value);
311 return ret;
312 }
313
314 // Functions inherited from SharedObjectData
315 virtual SharedObjectChangeSet* getChangeSet() override { return change_set; }
316 virtual void resetChangeSet() override { change_set->clear(); }
317
318 void serialize_order(SST::Core::Serialization::serializer& ser) override
319 {
320 SharedObjectData::serialize_order(ser);
321 SST_SER(set);
322 }
323
324 ImplementSerializable(SST::Shared::SharedSet<valT>::Data);
325
326 private:
327 class ChangeSet : public SharedObjectChangeSet
328 {
329
330 std::set<valT> changes;
331 verify_type verify;
332
333 void serialize_order(SST::Core::Serialization::serializer& ser) override
334 {
335 SharedObjectChangeSet::serialize_order(ser);
336 SST_SER(changes);
337 SST_SER(verify);
338 }
339
340 ImplementSerializable(SST::Shared::SharedSet<valT>::Data::ChangeSet);
341
342 public:
343 // For serialization
344 ChangeSet() :
345 SharedObjectChangeSet(),
346 verify(VERIFY_UNINITIALIZED)
347 {}
348 explicit ChangeSet(const std::string& name) :
349 SharedObjectChangeSet(name),
350 verify(VERIFY_UNINITIALIZED)
351 {}
352
353 void addChange(const valT& value) { changes.insert(value); }
354
355 void setVerify(verify_type v_type) { verify = v_type; }
356
357 void applyChanges(SharedObjectDataManager* manager) override
358 {
359 auto data = manager->getSharedObjectData<Data>(getName());
360 data->setVerify(verify);
361 for ( auto x : changes ) {
362 data->update_write(x);
363 }
364 }
365
366 void clear() override { changes.clear(); }
367 };
368 };
369};
370
371} // namespace SST::Shared
372
373#endif // SST_CORE_SHARED_SHAREDSET_H
This class is basically a wrapper for objects to declare the order in which their members should be s...
Definition serializer.h:45
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
Definition sharedSet.h:228
virtual void resetChangeSet() override
Resets the changeset for this data on this rank.
Definition sharedSet.h:316
virtual SharedObjectChangeSet * getChangeSet() override
Gets the changeset for this data on this rank.
Definition sharedSet.h:315
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