SST 16.0.0
Structural Simulation Toolkit
tunneldef.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_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
32namespace SST::Core::Interprocess {
33
34extern 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 */
56template <typename ShareDataType, typename MsgType>
58{
59
61
62public:
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
201private:
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
231protected:
232 /** Pointer to the Shared Data Region */
233 ShareDataType* sharedData;
234
235 /** Return the number of buffers */
236 size_t getNumBuffers() { return numBuffs; }
237
238private:
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
252 std::vector<CircBuff_t*> circBuffs;
253};
254
255} // namespace SST::Core::Interprocess
256
257#endif // SST_CORE_INTERPROCESS_TUNNEL_DEF_H
Definition circularBuffer.h:24
virtual ~TunnelDef()
Destructor.
Definition tunneldef.h:149
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
bool isMaster()
return whether this is a master-side tunnel or a child
Definition tunneldef.h:199
TunnelDef(void *sPtr)
Access an existing tunnel Child creates the TunnelDef, reads the shmSize, and then resizes its map ac...
Definition tunneldef.h:84
TunnelDef(size_t numBuffers, size_t bufferSize, uint32_t expectedChildren)
Create a new tunnel.
Definition tunneldef.h:69
bool readMessageNB(size_t buffer, MsgType *result)
Read data from buffer, non-blocking.
Definition tunneldef.h:191
void shutdown()
Clean up a region.
Definition tunneldef.h:152
void clearBuffer(size_t buffer)
Empty the messages in a buffer.
Definition tunneldef.h:196
ShareDataType * getSharedData()
return a pointer to the ShareDataType region
Definition tunneldef.h:172
size_t getTunnelSize()
return size of tunnel
Definition tunneldef.h:169
int * sharedData
Definition tunneldef.h:233
uint32_t initialize(void *sPtr)
Finish setting up a tunnel once the manager knows the correct size of the tunnel and has mmap'd a lar...
Definition tunneldef.h:97
size_t getNumBuffers()
Return the number of buffers.
Definition tunneldef.h:236