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