SST  15.1.0
StructuralSimulationToolkit
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 
23 namespace 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  */
29 template <typename valT>
30 class 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 
37 public:
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  bool initialized = (data != nullptr);
202  SST_SER(initialized);
203 
204  if ( !initialized ) return;
205 
206  switch ( ser.mode() ) {
207  case SST::Core::Serialization::serializer::SIZER:
208  case SST::Core::Serialization::serializer::PACK:
209  {
210  std::string name = data->getName();
211  SST_SER(name);
212  break;
213  }
214  case SST::Core::Serialization::serializer::UNPACK:
215  {
216  std::string name;
217  SST_SER(name);
218  data = manager.getSharedObjectData<Data>(name);
219  break;
220  }
221  case SST::Core::Serialization::serializer::MAP:
222  // Add your code here
223  break;
224  };
225  }
226  ImplementSerializable(SST::Shared::SharedSet<valT>)
227 
228 private:
229  bool published;
230  Data* data = nullptr;
231 
232  class Data : public SharedObjectData
233  {
234 
235  // Forward declaration. Defined below
236  class ChangeSet;
237 
238  public:
239  std::set<valT> set;
240  ChangeSet* change_set;
241 
242  verify_type verify;
243 
244  Data() :
246  change_set(nullptr),
247  verify(VERIFY_UNINITIALIZED)
248  {}
249  explicit Data(const std::string& name) :
250  SharedObjectData(name),
251  change_set(nullptr),
252  verify(VERIFY_UNINITIALIZED)
253  {
254  if ( Private::getNumRanks().rank > 1 ) {
255  change_set = new ChangeSet(name);
256  }
257  }
258 
259  ~Data()
260  {
261  if ( change_set ) delete change_set;
262  }
263 
264  void setVerify(verify_type v_type)
265  {
266  if ( v_type != verify && verify != VERIFY_UNINITIALIZED ) {
267  Private::getSimulationOutput().fatal(
268  CALL_INFO, 1, "ERROR: Type different verify_types specified for SharedSet %s\n", name.c_str());
269  }
270  verify = v_type;
271  if ( change_set ) change_set->setVerify(v_type);
272  }
273 
274  size_t getSize() const
275  {
276  std::lock_guard<std::mutex> lock(mtx);
277  return set.size();
278  }
279 
280  void update_write(const valT& value)
281  {
282  // Don't need to mutex because this is only ever called
283  // from one thread at a time, with barrier before and
284  // after, or from write(), which does mutex.
285  auto success = set.insert(value);
286  if ( !success.second ) {
287  // Wrote to a value that already existed
288  if ( verify != NO_VERIFY && !(value == *(success.first)) ) {
289  Private::getSimulationOutput().fatal(CALL_INFO, 1,
290  "ERROR: wrote two non-equal values to same set item in SharedSet %s\n", name.c_str());
291  }
292  }
293  }
294 
295  void write(const valT& value)
296  {
297  std::lock_guard<std::mutex> lock(mtx);
298  check_lock_for_write("SharedSet");
299  update_write(value);
300  if ( change_set ) change_set->addChange(value);
301  }
302 
303  // Inline the read since it may be called often during run().
304  // This read is not protected from data races in the case
305  // where the set may be simultaneously written by another
306  // thread. If there is a danger of simultaneous access
307  // during init, use the mutex_find function until after the
308  // init phase.
309  inline const_iterator find(const valT& value) { return set.find(value); }
310 
311  // Mutexed read for use if you are resizing the array as you go
312  inline const_iterator mutex_find(const valT& value)
313  {
314  std::lock_guard<std::mutex> lock(mtx);
315  auto ret = set.find(value);
316  return ret;
317  }
318 
319  // Functions inherited from SharedObjectData
320  virtual SharedObjectChangeSet* getChangeSet() override { return change_set; }
321  virtual void resetChangeSet() override { change_set->clear(); }
322 
323  void serialize_order(SST::Core::Serialization::serializer& ser) override
324  {
325  SharedObjectData::serialize_order(ser);
326  SST_SER(set);
327  }
328 
329  ImplementSerializable(SST::Shared::SharedSet<valT>::Data);
330 
331  private:
332  class ChangeSet : public SharedObjectChangeSet
333  {
334 
335  std::set<valT> changes;
336  verify_type verify;
337 
338  void serialize_order(SST::Core::Serialization::serializer& ser) override
339  {
340  SharedObjectChangeSet::serialize_order(ser);
341  SST_SER(changes);
342  SST_SER(verify);
343  }
344 
345  ImplementSerializable(SST::Shared::SharedSet<valT>::Data::ChangeSet);
346 
347  public:
348  // For serialization
349  ChangeSet() :
350  SharedObjectChangeSet(),
351  verify(VERIFY_UNINITIALIZED)
352  {}
353  explicit ChangeSet(const std::string& name) :
354  SharedObjectChangeSet(name),
355  verify(VERIFY_UNINITIALIZED)
356  {}
357 
358  void addChange(const valT& value) { changes.insert(value); }
359 
360  void setVerify(verify_type v_type) { verify = v_type; }
361 
362  void applyChanges(SharedObjectDataManager* manager) override
363  {
364  auto data = manager->getSharedObjectData<Data>(getName());
365  data->setVerify(verify);
366  for ( auto x : changes ) {
367  data->update_write(x);
368  }
369  }
370 
371  void clear() override { changes.clear(); }
372  };
373  };
374 };
375 
376 } // namespace SST::Shared
377 
378 #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:42
Definition: sharedObject.h:276
SharedSet class.
Definition: sharedSet.h:30
void lock()
Called by the core when writing to shared regions is no longer allowed.
Definition: sharedObject.h:192
virtual SharedObjectChangeSet * getChangeSet() override
Gets the changeset for this data on this rank.
Definition: sharedSet.h:320
bool isFullyPublished()
Checks to see if all instances of this SharedObject have called publish().
Definition: sharedObject.h:115
bool empty() const
Tests if the set is empty.
Definition: sharedSet.h:100
const_reverse_iterator rend() const
Get const_reverse_iterator to end of underlying set.
Definition: sharedSet.h:128
const_iterator begin() const
Get const_iterator to beginning of underlying set.
Definition: sharedSet.h:113
virtual void resetChangeSet() override
Resets the changeset for this data on this rank.
Definition: sharedSet.h:321
verify_type
Enum of verify types.
Definition: sharedObject.h:283
Definition: sharedSet.h:232
Definition: sharedArray.h:24
bool isFullyPublished()
Check whether all instances of this SharedSet have called publish().
Definition: sharedSet.h:149
const_iterator end() const
Get const_iterator to end of underlying set.
Definition: sharedSet.h:118
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
void publish()
Indicate that the calling element has written all the data it plans to write.
Definition: sharedSet.h:135
const std::string & getName()
Get the name of the SharedObject for this data.
Definition: sharedObject.h:105
This is the base class for holding data on changes made to the shared data on each rank...
Definition: sharedObject.h:48
int initialize(const std::string &obj_name, verify_type v_type=FE_VERIFY)
Initialize the SharedSet.
Definition: sharedSet.h:71
void insert(const valT &value)
Insert data to the set.
Definition: sharedSet.h:157
Base class for holding SharedObject data.
Definition: sharedObject.h:93
const_reverse_iterator rbegin() const
Get const_reverse_iterator to beginning of underlying set.
Definition: sharedSet.h:123
size_t size() const
Get the size of the set.
Definition: sharedSet.h:93
size_t count(const valT &k) const
Counts elements with a specific value.
Definition: sharedSet.h:108
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