SST  15.1.0
StructuralSimulationToolkit
tunneldef.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_INTERPROCESS_TUNNEL_DEF_H
13 #define SST_CORE_INTERPROCESS_TUNNEL_DEF_H
14 
15 /*
16  * This may be compiled into both SST and an Intel Pin3 tool
17  * Therefore it has the following restrictions:
18  * - Nothing that uses runtime type info (e.g., dynamic cast)
19  * - No c++11 (auto, nullptr, etc.)
20  * - Includes must all be PinCRT-enabled but not require PinCRT
21  */
22 
23 #include "sst/core/interprocess/circularBuffer.h"
24 
25 #include <cstddef>
26 #include <cstdint>
27 #include <inttypes.h>
28 #include <unistd.h>
29 #include <utility>
30 #include <vector>
31 
32 namespace SST::Core::Interprocess {
33 
34 extern uint32_t globalMMAPIPCCount;
35 
36 /* Internal bookkeeping */
38 {
39  volatile uint32_t expectedChildren;
40  size_t shmSegSize;
41  size_t numBuffers;
42  size_t offsets[0]; // offset[0] points to user region, offset[1]... points to circular buffers
43 };
44 
45 /**
46  * This class defines a shared-memory region between a master process and
47  * one or more child processes
48  * Region has three data structures:
49  * - internal bookkeeping (InternalSharedData),
50  * - user defined shared data (ShareDataType)
51  * - multiple circular-buffer queues with entries of type MsgType
52  *
53  * @tparam ShareDataType Type to put in the shared data region
54  * @tparam MsgType Type of messages being sent in the circular buffers
55  */
56 template <typename ShareDataType, typename MsgType>
57 class TunnelDef
58 {
59 
61 
62 public:
63  /** Create a new tunnel
64  *
65  * @param numBuffers Number of buffers for which we should tunnel
66  * @param bufferSize How large each buffer should be
67  * @param expectedChildren Number of child processes that will connect to this tunnel
68  */
69  TunnelDef(size_t numBuffers, size_t bufferSize, uint32_t expectedChildren) :
70  master(true),
71  shmPtr(NULL)
72  {
73  // Locally buffer info
74  numBuffs = numBuffers;
75  buffSize = bufferSize;
76  children = expectedChildren;
77  shmSize = calculateShmemSize(numBuffers, bufferSize);
78  }
79 
80  /** Access an existing tunnel
81  * Child creates the TunnelDef, reads the shmSize, and then resizes its map accordingly
82  * @param sPtr Location of shared memory region
83  */
84  explicit TunnelDef(void* sPtr) :
85  master(false),
86  shmPtr(sPtr)
87  {
88  isd = (InternalSharedData*)shmPtr;
89  shmSize = isd->shmSegSize;
90  }
91 
92  /** Finish setting up a tunnel once the manager knows the correct size of the tunnel
93  * and has mmap'd a large enough region for it
94  * @param sPtr Location of shared memory region
95  * returns number of children left to attach
96  */
97  uint32_t initialize(void* sPtr)
98  {
99  shmPtr = sPtr;
100 
101  if ( master ) {
102  nextAllocPtr = (uint8_t*)shmPtr;
103 
104  // Reserve space for InternalSharedData
105  // Including an offset array entry for each buffer & sharedData structure
106  std::pair<size_t, InternalSharedData*> aResult =
107  reserveSpace<InternalSharedData>((1 + numBuffs) * sizeof(size_t));
108  isd = aResult.second;
109  isd->expectedChildren = children;
110  isd->shmSegSize = shmSize;
111  isd->numBuffers = numBuffs;
112 
113  // Reserve space for ShareDataType structure
114  std::pair<size_t, ShareDataType*> bResult = reserveSpace<ShareDataType>(0);
115  isd->offsets[0] = bResult.first;
116  sharedData = bResult.second;
117 
118  // Reserve space for circular buffers
119  const size_t cbSize = sizeof(MsgType) * buffSize;
120  for ( size_t c = 0; c < isd->numBuffers; c++ ) {
121  CircBuff_t* cPtr = NULL;
122 
123  std::pair<size_t, CircBuff_t*> cResult = reserveSpace<CircBuff_t>(cbSize);
124  isd->offsets[1 + c] = cResult.first;
125  cPtr = cResult.second;
126  if ( !cPtr->setBufferSize(buffSize) ) exit(1); // function prints error message
127  circBuffs.push_back(cPtr);
128  }
129  return isd->expectedChildren;
130  }
131  else {
132  isd = (InternalSharedData*)shmPtr;
133  shmSize = isd->shmSegSize;
134 
135  sharedData = (ShareDataType*)((uint8_t*)shmPtr + isd->offsets[0]);
136 
137  for ( size_t c = 0; c < isd->numBuffers; c++ ) {
138  circBuffs.push_back((CircBuff_t*)((uint8_t*)shmPtr + isd->offsets[c + 1]));
139  }
140  numBuffs = isd->numBuffers;
141 
142  auto t = isd->expectedChildren - 1;
143  isd->expectedChildren = t;
144  return t;
145  }
146  }
147 
148  /** Destructor */
149  virtual ~TunnelDef() { shutdown(); }
150 
151  /** Clean up a region */
152  void shutdown()
153  {
154  if ( master ) {
155  for ( size_t i = 0; i < circBuffs.size(); i++ ) {
156  circBuffs[i]->~CircBuff_t();
157  }
158  }
159 
160  if ( shmPtr ) {
161  shmPtr = NULL;
162  isd = NULL;
163  sharedData = NULL;
164  shmSize = 0;
165  }
166  }
167 
168  /** return size of tunnel */
169  size_t getTunnelSize() { return shmSize; }
170 
171  /** return a pointer to the ShareDataType region */
172  ShareDataType* getSharedData() { return sharedData; }
173 
174  /** Write data to buffer, blocks until space is available
175  * @param buffer which buffer index to write to
176  * @param command message to write to buffer
177  */
178  void writeMessage(size_t buffer, const MsgType& command) { circBuffs[buffer]->write(command); }
179 
180  /** Read data from buffer, blocks until message received
181  * @param buffer which buffer to read from
182  * return the message
183  */
184  MsgType readMessage(size_t buffer) { return circBuffs[buffer]->read(); }
185 
186  /** Read data from buffer, non-blocking
187  * @param buffer which buffer to read from
188  * @param result pointer to return read message at
189  * return whether a message was read
190  */
191  bool readMessageNB(size_t buffer, MsgType* result) { return circBuffs[buffer]->readNB(result); }
192 
193  /** Empty the messages in a buffer
194  * @param buffer which buffer to empty
195  */
196  void clearBuffer(size_t buffer) { circBuffs[buffer]->clearBuffer(); }
197 
198  /** return whether this is a master-side tunnel or a child*/
199  bool isMaster() { return master; }
200 
201 private:
202  /** Allocate space for a data structure in the shared region
203  * @tparam T data structure type to allocate space for
204  * @param extraSpace how many extra bytes to reserve with this allocation
205  * return offset from shmPtr where structure was allocated and pointer to structure
206  */
207  template <typename T>
208  std::pair<size_t, T*> reserveSpace(size_t extraSpace = 0)
209  {
210  size_t space = sizeof(T) + extraSpace;
211  if ( (size_t)((nextAllocPtr + space) - (uint8_t*)shmPtr) > shmSize ) return std::make_pair<size_t, T*>(0, NULL);
212  T* ptr = (T*)nextAllocPtr;
213  nextAllocPtr += space;
214  new (ptr) T(); // Call constructor if need be
215  return std::make_pair((uint8_t*)ptr - (uint8_t*)shmPtr, ptr);
216  }
217 
218  /** Calculate the size of the tunnel */
219  static size_t calculateShmemSize(size_t numBuffers, size_t bufferSize)
220  {
221  long pagesize = sysconf(_SC_PAGESIZE);
222  /* Count how many pages are needed, at minimum */
223  size_t isd = 1 + ((sizeof(InternalSharedData) + (1 + numBuffers) * sizeof(size_t)) / pagesize);
224  size_t buffer = 1 + ((sizeof(CircularBuffer<MsgType>) + bufferSize * sizeof(MsgType)) / pagesize);
225  size_t shdata = 1 + ((sizeof(ShareDataType) + sizeof(InternalSharedData)) / pagesize);
226 
227  /* Alloc 2 extra pages just in case */
228  return (2 + isd + shdata + numBuffers * buffer) * pagesize;
229  }
230 
231 protected:
232  /** Pointer to the Shared Data Region */
233  ShareDataType* sharedData;
234 
235  /** Return the number of buffers */
236  size_t getNumBuffers() { return numBuffs; }
237 
238 private:
239  bool master;
240  void* shmPtr;
241 
242  uint8_t* nextAllocPtr;
243  size_t shmSize;
244 
245  // Local data
246  size_t numBuffs;
247  size_t buffSize;
248  uint32_t children;
249 
250  // Shared objects
251  InternalSharedData* isd;
252  std::vector<CircBuff_t*> circBuffs;
253 };
254 
255 } // namespace SST::Core::Interprocess
256 
257 #endif // SST_CORE_INTERPROCESS_TUNNEL_DEF_H
ShareDataType * getSharedData()
return a pointer to the ShareDataType region
Definition: tunneldef.h:172
void clearBuffer(size_t buffer)
Empty the messages in a buffer.
Definition: tunneldef.h:196
size_t getTunnelSize()
return size of tunnel
Definition: tunneldef.h:169
ShareDataType * sharedData
Pointer to the Shared Data Region.
Definition: tunneldef.h:233
TunnelDef(void *sPtr)
Access an existing tunnel Child creates the TunnelDef, reads the shmSize, and then resizes its map ac...
Definition: tunneldef.h:84
size_t getNumBuffers()
Return the number of buffers.
Definition: tunneldef.h:236
uint32_t initialize(void *sPtr)
Finish setting up a tunnel once the manager knows the correct size of the tunnel and has mmap&#39;d a lar...
Definition: tunneldef.h:97
Definition: circularBuffer.h:20
Definition: circularBuffer.h:23
void shutdown()
Clean up a region.
Definition: tunneldef.h:152
This class defines a shared-memory region between a master process and one or more child processes Re...
Definition: tunneldef.h:57
virtual ~TunnelDef()
Destructor.
Definition: tunneldef.h:149
bool readMessageNB(size_t buffer, MsgType *result)
Read data from buffer, non-blocking.
Definition: tunneldef.h:191
void writeMessage(size_t buffer, const MsgType &command)
Write data to buffer, blocks until space is available.
Definition: tunneldef.h:178
MsgType readMessage(size_t buffer)
Read data from buffer, blocks until message received.
Definition: tunneldef.h:184
TunnelDef(size_t numBuffers, size_t bufferSize, uint32_t expectedChildren)
Create a new tunnel.
Definition: tunneldef.h:69
bool isMaster()
return whether this is a master-side tunnel or a child
Definition: tunneldef.h:199