CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
TemporalMemory.cpp
Go to the documentation of this file.
1
4
5#include "TemporalMemory.h"
6#include "UnitTestFramework.h"
7
8namespace cmlabs {
9
11 mutex = NULL;
12 this->master = master;
13 header = NULL;
14 memorySize = 0;
15 port = 0;
16 node = 0;
17 serial = 0;
18}
19
21 if (mutex)
22 mutex->enter(5000, __FUNCTION__);
23 if (memorySize)
24 utils::CloseSharedMemorySegment((char*) header, memorySize);
25 header = NULL;
26 if (mutex)
27 mutex->leave();
28 delete(mutex);
29 mutex = NULL;
30}
31
32bool TemporalMemory::getMemoryUsage(uint64& alloc, uint64& usage) {
33 if (!mutex || !mutex->enter(5000, __FUNCTION__))
34 return false;
36
37 alloc = memorySize;
38 usage = header->usage;
39
40 mutex->leave();
41 return true;
42}
43
45 if (!mutex || !mutex->enter(5000, __FUNCTION__))
46 return false;
48 checkSlots(GetTimeNow());
49 mutex->leave();
50 return true;
51}
52
53bool TemporalMemory::create(uint32 slotCount, uint16 binCount, uint32 minBlockSize, uint32 maxBlockSize, uint64 initSize, uint64 maxSize, uint32 growSteps) {
54 serial = master->incrementDynamicShmemSerial();
55 port = master->getID();
56
57 uint32 bitFieldSize = utils::Calc32BitFieldSize(slotCount);
58
59 uint64 binDataSize = initSize - ( sizeof(TemporalMemoryStruct) + bitFieldSize + (slotCount * sizeof(SlotEntryStruct)) );
60
61 // Calculate virgin bin sizes
62 std::vector<BinHeaderStruct> binHeaders = calcBinHeaders(binCount, minBlockSize, maxBlockSize, binDataSize);
63 //if (binHeaders.size() <= 2)
64 // return false;
65
66 uint64 newInitSize = sizeof(TemporalMemoryStruct) + bitFieldSize + (slotCount * sizeof(SlotEntryStruct));
67 std::vector<BinHeaderStruct>::iterator i = binHeaders.begin(), e = binHeaders.end();
68 while (i != e) {
69 newInitSize += i->size;
70 i++;
71 }
72
73 mutex = new utils::Mutex(utils::StringFormat("PsycloneTemporalMemoryMutex_%u", port).c_str(), true);
74 if (!mutex->enter(5000, __FUNCTION__))
75 return false;
76
77 memorySize = newInitSize;
78
79 header = (TemporalMemoryStruct*) utils::CreateSharedMemorySegment(utils::StringFormat("PsycloneTemporalMemory_%u_%u", port, serial).c_str(), memorySize, true);
80 if (!header) {
81 mutex->leave();
82 return false;
83 }
84 memset((char*)header, 0, (size_t)(sizeof(TemporalMemoryStruct) + header->bitFieldSize + (slotCount * sizeof(SlotEntryStruct))));
85 header->size = memorySize;
86 header->maxSize = maxSize;
87 if (memorySize >= maxSize)
88 header->growStep = 0;
89 else if (maxSize - memorySize < 10000000) // 10MB
90 header->growStep = maxSize - memorySize;
91 else
92 header->growStep = (maxSize - memorySize) / (growSteps ? growSteps : 8);
93 header->cid = TEMPORALMEMORYID;
94 header->createdTime = GetTimeNow();
95 header->slotCount = slotCount;
96 header->slotsInUse = 0;
97 header->checkTimeInterval = 1000000; // 1 sec
98 header->binCount = binCount;
99 header->bitFieldSize = bitFieldSize;
100 memset((char*)header + sizeof(TemporalMemoryStruct), 255, bitFieldSize);
101 header->usage = sizeof(TemporalMemoryStruct) + header->bitFieldSize + (slotCount * sizeof(SlotEntryStruct)); // for now, update below
102 master->setDynamicShmemSize(memorySize);
103
104 //printf("%s", utils::PrintBitFieldString(((char*)header + sizeof(TemporalMemoryStruct)), header->slotCount, NULL).c_str()); fflush(stdout);
105 //printf("%s", utils::PrintBitFieldString((const char*)header + sizeof(TemporalMemoryStruct), header->slotCount, NULL).c_str()); fflush(stdout);
106
107 // init all slots
108 slotEntries = (SlotEntryStruct*)(((char*)header) + sizeof(TemporalMemoryStruct) + header->bitFieldSize);
109 memset((char*)slotEntries, 0, (size_t)(slotCount * sizeof(SlotEntryStruct)));
110
111 uint64 binHeaderUsage = 0;
112 // init all bins
113 BinHeaderStruct* binHeader = (BinHeaderStruct*)(((char*)header) + sizeof(TemporalMemoryStruct) + header->bitFieldSize + (slotCount * sizeof(SlotEntryStruct)));
114
115 i = binHeaders.begin(), e = binHeaders.end();
116 while (i != e) {
117 *binHeader = *i;
118 // reset bitfield
119 memset((char*)binHeader + sizeof(BinHeaderStruct), 255, binHeader->bitFieldSize);
120 // don't worry about the data, just leave it uninitialised
121 binHeaderUsage += binHeader->usage; // should be sizeof(BinHeaderStruct) + binHeader->bitFieldSize
122 i++;
123 binHeader = (BinHeaderStruct*)(((char*)binHeader) + binHeader->size);
124 }
125
126 header->usage = sizeof(TemporalMemoryStruct) + header->bitFieldSize + (slotCount * sizeof(SlotEntryStruct)) + binHeaderUsage;
127
128 mutex->leave();
129 return true;
130}
131
132std::vector<BinHeaderStruct> TemporalMemory::calcBinHeaders(uint32 binCount, uint32 minBlockSize, uint32 maxBlockSize, uint64 binDataSize) {
133 // we have binDataSize in total, split into binCount bins and set blockSizes from minBlockSize to maxBlockSize
134 uint32 n;
135 std::vector<BinHeaderStruct> binHeaders(binCount);
136
137 // initial sanity checks
138 if ((binCount < 1) || (minBlockSize > maxBlockSize) || (binDataSize < (binCount * minBlockSize)))
139 return binHeaders;
140
141 if (binCount == 1) {
142 binHeaders[0].size = binDataSize;
143 binHeaders[0].blockSize = minBlockSize;
144 binHeaders[0].blocksInUse = 0;
145 binHeaders[0].blockCount = (uint32)((binDataSize - sizeof(BinHeaderStruct)) / minBlockSize);
146 binHeaders[0].bitFieldSize = utils::Calc32BitFieldSize(binHeaders[0].blockCount);
147 return binHeaders;
148 }
149 else if (binCount == 2) {
150 binHeaders[0].size = binDataSize / 2;
151 binHeaders[0].blockSize = minBlockSize;
152 binHeaders[0].blocksInUse = 0;
153 binHeaders[0].blockCount = (uint32)((binHeaders[0].size - sizeof(BinHeaderStruct)) / minBlockSize);
154 binHeaders[0].bitFieldSize = utils::Calc32BitFieldSize(binHeaders[0].blockCount);
155
156 binHeaders[1].size = binDataSize / 2;
157 binHeaders[1].blockSize = maxBlockSize;
158 binHeaders[1].blocksInUse = 0;
159 binHeaders[1].blockCount = (uint32)((binHeaders[1].size - sizeof(BinHeaderStruct)) / maxBlockSize);
160 binHeaders[1].bitFieldSize = utils::Calc32BitFieldSize(binHeaders[1].blockCount);
161 return binHeaders;
162 }
163
164 binHeaders[0].size = (uint64)(binDataSize * 0.25);
165 uint64 memoryLeft = binDataSize - binHeaders[0].size;
166 long double allocation = 0.5 / ((double)binCount - 2);
167 for (n = 1; n < binCount-1; n++) {
168 binHeaders[n].size = (uint64)(binDataSize * allocation); // flooring value
169 memoryLeft -= binHeaders[n].size;
170 }
171 binHeaders[binCount - 1].size = memoryLeft;
172
173 // now fill in the other parameters
174 memoryLeft = binDataSize;
175 uint32 blockSize = minBlockSize;
176 uint32 blockSizeStep = (maxBlockSize - minBlockSize) / (binCount-1);
177 for (n = 0; n < binCount; n++) {
178 binHeaders[n].blockSize = blockSize;
179 binHeaders[n].blocksInUse = 0;
180 binHeaders[n].blockCount = (uint32)((binDataSize - sizeof(BinHeaderStruct)) / blockSize);
181 binHeaders[n].bitFieldSize = utils::Calc32BitFieldSize(binHeaders[n].blockCount);
182 // now reduce blockCount until it fits
183 while (sizeof(BinHeaderStruct) + binHeaders[n].bitFieldSize + (binHeaders[n].blockCount * blockSize) > binHeaders[n].size) {
184 binHeaders[n].blockCount -= 1;
185 binHeaders[n].bitFieldSize = utils::Calc32BitFieldSize(binHeaders[n].blockCount);
186 }
187 binHeaders[n].usage = sizeof(BinHeaderStruct) + binHeaders[n].bitFieldSize;
188 // recalc size to avoid padding
189 binHeaders[n].size = binHeaders[n].usage + (binHeaders[n].blockCount * blockSize);
190 memoryLeft -= binHeaders[n].size;
191 blockSize += blockSizeStep;
192 }
193
194 // use any memory padding remaining to add blocks to bin 0
195 if (memoryLeft > minBlockSize) {
196 uint32 growBlocks = (uint32)(memoryLeft / minBlockSize);
197 binHeaders[0].blockCount += growBlocks;
198 // now reduce blockCount until it fits
199 while (sizeof(BinHeaderStruct) + binHeaders[0].bitFieldSize + (binHeaders[0].blockCount * binHeaders[0].blockSize) > binHeaders[0].size) {
200 binHeaders[0].blockCount -= 1;
201 binHeaders[0].bitFieldSize = utils::Calc32BitFieldSize(binHeaders[0].blockCount);
202 }
203 }
204 return binHeaders;
205}
206
207
208
210 serial = master->getDynamicShmemSerial();
211 uint64 size = master->getDynamicShmemSize();
212 this->port = master->getID();
213
214 bool createdMutex = false;
215 if (!mutex) {
216 mutex = new utils::Mutex(utils::StringFormat("PsycloneTemporalMemoryMutex_%u", port).c_str());
217 createdMutex = true;
218 if (!mutex->enter(5000, __FUNCTION__))
219 return false;
220 }
221
222 TemporalMemoryStruct* newHeader = (TemporalMemoryStruct*) utils::OpenSharedMemorySegment(utils::StringFormat("PsycloneTemporalMemory_%u_%u", port, serial).c_str(), size);
223 if (!newHeader) {
224 if (createdMutex)
225 mutex->leave();
226 return false;
227 }
228 if (newHeader->cid != TEMPORALMEMORYID) {
229 utils::CloseSharedMemorySegment((char*)newHeader, size);
230 if (createdMutex)
231 mutex->leave();
232 return false;
233 }
234
235 memorySize = size;
236 if (header)
237 utils::CloseSharedMemorySegment((char*)header, header->size);
238 header = newHeader;
239 slotEntries = (SlotEntryStruct*) (((char*)header) + sizeof(TemporalMemoryStruct) + header->bitFieldSize);
240 if (createdMutex)
241 mutex->leave();
242 // else leave mutex locked as we are calling from within the object
243 return true;
244}
245
247 node = id;
248 return true;
249}
250
251BinHeaderStruct* TemporalMemory::getBestBin(uint32 size, uint32& bin, uint32 &blocksNeeded) {
252
253 BinHeaderStruct* binHeader = (BinHeaderStruct*)(((char*)header) + sizeof(TemporalMemoryStruct) + header->bitFieldSize + (header->slotCount * sizeof(SlotEntryStruct)));
254 for (bin = 0; bin < ((uint32)header->binCount-1); bin++) {
255 // quick-check, if msg fits ok into bin, great
256 blocksNeeded = (uint32)ceil((double)size / binHeader->blockSize);
257 //spaceWaisted = (uint32)((double)size) % binHeader->blockSize;
258 if (blocksNeeded < 5)
259 return binHeader;
260 binHeader = (BinHeaderStruct*)(((char*)binHeader) + binHeader->size);
261 }
262 blocksNeeded = (uint32)ceil((double)size / binHeader->blockSize);
263 return binHeader;
264
265 //BinSpaceFit* fit;
266 //BinSpaceFit bestFit;
267 //std::vector<BinSpaceFit> fits;
268 //std::sort(fits.begin(), fits.end(), BinSpaceFitSort);
269 //fit = &fits[header->binCount-1];
270 //if (fit->blocksNeeded)
271
272 //bestFit.spaceWaisted = MAXINT32;
273 //for (n = 0; n < header->binCount; n++) {
274 // fit = &fits[n];
275 // if (fit->spaceWaisted < 1024)
276 // return fit->bin;
277 // if (fit->spaceWaisted < bestFit.spaceWaisted) {
278 // bestFit = *fit;
279 // }
280 // if (fit->blocksNeeded)
281 // fit->bin = n;
282 // fit->blocksNeeded = (uint32)ceil((double)size / binHeader->blockSize);
283 // fit->spaceWaisted = (uint32)((double)size) % binHeader->blockSize;
284 // binHeader = (BinHeaderStruct*)(((char*)binHeader) + binHeader->size);
285 //}
286}
287
288// Insert new block of memory and return full id
290 uint64 now = GetTimeNow();
291 uint64 eol = msg->getEOL();
292 if (eol <= now) {
293 LogPrint(0, LOG_MEMORY, 0, "Could not add message to TemporalMemory, expired already");
294 return false;
295 }
296
297 if (!mutex || !mutex->enter(5000, __FUNCTION__) || !msg)
298 return false;
300
301 checkSlots(now);
302
303 uint32 msgSize = msg->getSize();
304 // Find the appropriate slot
305 uint32 slot;
306 if (!utils::GetFirstFreeBitLoc(((char*)header + sizeof(TemporalMemoryStruct)), header->bitFieldSize, slot) || (slot >= header->slotCount)) {
307 // run out of slots, resize
308 if (!resizeSlots()) {
309 LogPrint(0, LOG_MEMORY, 0, "Could not add message to TemporalMemory, slot resize error");
310 return false;
311 }
312 if (!utils::GetFirstFreeBitLoc(((char*)header + sizeof(TemporalMemoryStruct)), header->bitFieldSize, slot) || (slot >= header->slotCount)) {
313 LogPrint(0, LOG_MEMORY, 0, "Could not add message to TemporalMemory, slot post resize error");
314 return false;
315 }
316 }
317 SlotEntryStruct* slotEntry = (SlotEntryStruct*)((char*)header + sizeof(TemporalMemoryStruct) + header->bitFieldSize + (slot * sizeof(SlotEntryStruct)));
318 uint32 bin;
319 uint32 blocksNeeded;
320 BinHeaderStruct* binHeader = getBestBin(msg->getSize(), bin, blocksNeeded);
321 if (!binHeader) {
322 LogPrint(0, LOG_MEMORY, 0, "Could not add message to TemporalMemory, bin search error");
323 return false;
324 }
325
326 uint32 startBlock;
327 if (!utils::GetFirstFreeBitLocN(((char*)binHeader + sizeof(BinHeaderStruct)), binHeader->bitFieldSize, blocksNeeded, startBlock) || (startBlock + blocksNeeded >= binHeader->blockCount)) {
328
329 //printf("%s", utils::PrintBitFieldString(((char*)binHeader + sizeof(BinHeaderStruct)), binHeader->bitFieldSize, binHeader->blockCount, NULL).c_str()); fflush(stdout);
330
331 // run out of continuous space in bin, resize
332 if (!resizeBin(bin, binHeader, msg->getSize())) {
333 LogPrint(0, LOG_MEMORY, 0, "Could not add message to TemporalMemory, bin resize error");
334 return false;
335 }
336 slotEntry = (SlotEntryStruct*)((char*)header + sizeof(TemporalMemoryStruct) + header->bitFieldSize + (slot * sizeof(SlotEntryStruct)));
337 binHeader = getBestBin(msg->getSize(), bin, blocksNeeded);
338 if (!binHeader) {
339 LogPrint(0, LOG_MEMORY, 0, "Could not add message to TemporalMemory, bin post resize search error");
340 return false;
341 }
342 if (!utils::GetFirstFreeBitLocN(((char*)binHeader + sizeof(BinHeaderStruct)), binHeader->bitFieldSize, blocksNeeded, startBlock) || (startBlock + blocksNeeded >= binHeader->blockCount)) {
343 LogPrint(0, LOG_MEMORY, 0, "Could not add message to TemporalMemory, bin post resize error");
344 return false;
345 }
346 }
347
348 // Insert data
349 uint64 localOffset = startBlock * binHeader->blockSize;
350 memcpy(((char*)binHeader) + sizeof(BinHeaderStruct) + binHeader->bitFieldSize + localOffset, msg->data, msgSize);
351
352 utils::SetBitN(startBlock, blocksNeeded, BITOCCUPIED, ((char*)binHeader + sizeof(BinHeaderStruct)), binHeader->bitFieldSize);
353 slotEntry->bin = bin;
354 slotEntry->eol = eol;
355 slotEntry->offset = localOffset;
356 slotEntry->serial++;
357 slotEntry->size = msgSize;
358 slotEntry->usage = blocksNeeded * binHeader->blockSize;
359 slotEntry->startBlock = startBlock;
360 slotEntry->blockUsage = blocksNeeded;
361
362 binHeader->usage += slotEntry->usage;
363 binHeader->blocksInUse += blocksNeeded;
364 header->slotsInUse++;
365 header->usage += slotEntry->usage;
366 utils::SetBit(slot, BITOCCUPIED, ((char*)header + sizeof(TemporalMemoryStruct)), header->bitFieldSize);
367
368 SETMSGID(id, node, slotEntry->serial, slot);
369
370 if (header->usage > 1000000000)
371 int n = 0;
372
373 mutex->leave();
374 //printf("x x x x x x\n"); fflush(stdout);
375 return true;
376}
377
378// Get copy of block of memory
380 if (node != GETMSGNODE(id))
381 return NULL;
382
383 if (!mutex || !mutex->enter(5000, __FUNCTION__))
384 return NULL;
386
387// checkSlots();
388
389 char* block = getMemoryBlock(id);
390 if (!block) {
391 mutex->leave();
392 return NULL;
393 }
394
395 DataMessage* msg = new DataMessage(block, true);
396 mutex->leave();
397 return msg;
398}
399
400char* TemporalMemory::getMemoryBlock(uint64 id) {
401 // Assume node id has already been checked
402 // Assume mutex is locked
403 // Find the slot
404 uint32 slot = GETMSGSLOT(id);
405 if (slot > header->slotCount)
406 return NULL;
407
408 SlotEntryStruct* slotEntry = (SlotEntryStruct*)((char*)header + sizeof(TemporalMemoryStruct) + header->bitFieldSize + (slot * sizeof(SlotEntryStruct)));
409
410 if ((slotEntry->serial != GETMSGSERIAL(id))
411 || (slotEntry->eol < GetTimeNow())
412 || !slotEntry->size ) {
413 LogPrint(0, LOG_MEMORY, 0, "Message requested from TemporalMemory has expired");
414 return NULL;
415 }
416
417 BinHeaderStruct* binHeader = (BinHeaderStruct*)(((char*)header) + sizeof(TemporalMemoryStruct) + header->bitFieldSize + (header->slotCount * sizeof(SlotEntryStruct)));
418 for (uint32 n = 0; n < slotEntry->bin; n++)
419 binHeader = (BinHeaderStruct*)(((char*)binHeader) + binHeader->size);
420
421 return (((char*)binHeader) + sizeof(BinHeaderStruct) + binHeader->bitFieldSize + slotEntry->offset);
422}
423
424bool TemporalMemory::checkSlots(uint64 now) {
425
426 if ((header->lastCheckTime && (now > header->lastCheckTime + header->checkTimeInterval)) || !header->slotsInUse)
427 return true;
428
429 //printf("%s", utils::PrintBitFieldString(((char*)header + sizeof(TemporalMemoryStruct)), header->bitFieldSize, header->slotCount, NULL).c_str()); fflush(stdout);
430 uint32 maxSlot;
431 if (!utils::GetLastOccupiedBitLoc(((char*)header + sizeof(TemporalMemoryStruct)), header->bitFieldSize, maxSlot)) {
432 // no slots appear to be occupied, we are all good
433 LogPrint(0, LOG_MEMORY, 0, "TemporalMemory::checkSlots inconsistency, no slots found, but header claims %u slots in use...", header->slotsInUse);
434 return true;
435 // maxSlot = header->slotCount-1;
436 }
437 SlotEntryStruct* slotEntry = (SlotEntryStruct*)((char*)header + sizeof(TemporalMemoryStruct) + header->bitFieldSize);
438
439 BinHeaderStruct* binHeader;
440 for (uint32 n = 0; n <= maxSlot; n++) {
441 if (slotEntry->size && (now >= slotEntry->eol)) {
442 // reset storage
443 binHeader = (BinHeaderStruct*)(((char*)header) + sizeof(TemporalMemoryStruct) + header->bitFieldSize + (header->slotCount * sizeof(SlotEntryStruct)));
444 for (uint32 i = 0; i < slotEntry->bin; i++)
445 binHeader = (BinHeaderStruct*)(((char*)binHeader) + binHeader->size);
446 utils::SetBitN(slotEntry->startBlock, slotEntry->blockUsage, BITFREE, ((char*)binHeader + sizeof(BinHeaderStruct)), binHeader->bitFieldSize);
447 binHeader->usage -= slotEntry->usage;
448 binHeader->blocksInUse -= slotEntry->blockUsage;
449 header->usage -= slotEntry->usage;
450 header->slotsInUse--;
451
452 // reset slot
453 slotEntry->size = 0;
454 utils::SetBit(n, BITFREE, ((char*)header + sizeof(TemporalMemoryStruct)), header->bitFieldSize);
455 }
456 slotEntry += 1;
457 }
458 return true;
459}
460
461
462bool TemporalMemory::resizeSlots() {
463 // For now just double slot count
464 uint32 slotGrowth = header->slotCount * 4;
465 std::vector<BinHeaderStruct> binHeaderSizes = calcBinHeadersGrowth();
466 return resizeMemory(slotGrowth, binHeaderSizes);
467}
468
469bool TemporalMemory::resizeBin(uint32 bin, BinHeaderStruct* binHeader, uint32 msgSize) {
470 uint32 slotGrowth = 0;
471 if ((double)header->slotsInUse / header->slotCount > 0.8)
472 slotGrowth = header->slotCount * 4;
473 else if ((double)header->slotsInUse / header->slotCount > 0.5)
474 slotGrowth = header->slotCount * 2;
475 std::vector<BinHeaderStruct> binHeaderSizes = calcBinHeadersGrowth(bin, msgSize);
476 return resizeMemory(slotGrowth, binHeaderSizes);
477}
478
479std::vector<BinHeaderStruct> TemporalMemory::calcBinHeadersGrowth(uint32 bin, uint32 msgSize) {
480 // we have binDataSize in total, split into binCount bins and set blockSizes from minBlockSize to maxBlockSize
481 std::vector<BinHeaderStruct> binHeaders(header->binCount);
482
483 BinHeaderStruct* binHeader = (BinHeaderStruct*)(((char*)header) + sizeof(TemporalMemoryStruct) + header->bitFieldSize + (header->slotCount * sizeof(SlotEntryStruct)));
484 for (uint32 n = 0; n < header->binCount; n++) {
485 binHeaders[n] = *binHeader;
486 if ( ((double)binHeader->usage / binHeader->size > 0.8)
487 || (msgSize && (n == bin)) ) {
488 binHeaders[n].blockCount = clmax(binHeaders[n].blockCount * 4, binHeaders[n].blockCount + (uint32)(20.0*((double)msgSize/binHeader->blockSize)));
489 binHeaders[n].bitFieldSize = utils::Calc32BitFieldSize(binHeaders[n].blockCount);
490 binHeaders[n].size = sizeof(BinHeaderStruct) + binHeaders[n].bitFieldSize + (binHeaders[n].blockCount * binHeaders[n].blockSize);
491 // usage remains the same, of course
492 }
493 binHeader = (BinHeaderStruct*)(((char*)binHeader) + binHeader->size);
494 }
495 return binHeaders;
496}
497
498bool TemporalMemory::resizeMemory(uint32 slotGrowth, std::vector<BinHeaderStruct>& binHeaderSizes) {
499
500 uint32 slotCount = header->slotCount + slotGrowth;
501 uint32 bitFieldSize = utils::Calc32BitFieldSize(slotCount);
502 uint64 newTotalSize = sizeof(TemporalMemoryStruct) + bitFieldSize + (slotCount * sizeof(SlotEntryStruct));
503
504 for (uint32 n = 0; n < header->binCount; n++) {
505 newTotalSize += binHeaderSizes[n].size;
506 }
507
508 if (newTotalSize > header->maxSize) {
509 LogPrint(0, LOG_MEMORY, 0, "Temporal Memory hit maximum size %s when asked to grow by: %s to: %s total",
510 utils::BytifySize((double)header->maxSize).c_str(),
511 utils::BytifySize((double)newTotalSize - header->size).c_str(),
512 utils::BytifySize((double)newTotalSize).c_str()
513 );
514 return false;
515 }
516
517 serial = master->incrementDynamicShmemSerial();
518
519 TemporalMemoryStruct* newHeader = (TemporalMemoryStruct*) utils::CreateSharedMemorySegment(utils::StringFormat("PsycloneTemporalMemory_%u_%u", port, serial).c_str(), newTotalSize, true);
520 if (!newHeader) {
521 LogPrint(0, LOG_MEMORY, 0, "Temporal Memory couldn't allocate memory to grow by: %s to: %s total as requested",
522 utils::BytifySize((double)newTotalSize - header->size).c_str(),
523 utils::BytifySize((double)newTotalSize).c_str()
524 );
525 return false;
526 }
527
528 // First set the full new bitfield to unoccupied
529 memset((char*)newHeader + sizeof(TemporalMemoryStruct), 255, bitFieldSize);
530 // Copy old header and old bitFieldSize
531 memcpy((char*)newHeader, header, sizeof(TemporalMemoryStruct) + header->bitFieldSize);
532 newHeader->size = newTotalSize;
533 // First set all new entries to 0
534 memset((char*)newHeader + sizeof(TemporalMemoryStruct) + bitFieldSize, 0, (size_t)(slotCount * sizeof(SlotEntryStruct)));
535 // Copy in old entries
536 memcpy((char*)newHeader + sizeof(TemporalMemoryStruct) + bitFieldSize,
537 (char*)header + sizeof(TemporalMemoryStruct) + header->bitFieldSize, (size_t)(header->slotCount * sizeof(SlotEntryStruct)));
538
539 newHeader->slotCount = slotCount;
540 newHeader->bitFieldSize = bitFieldSize;
541
542 uint64 binGrowth = 0;
543 BinHeaderStruct* binHeader = (BinHeaderStruct*)(((char*)header) + sizeof(TemporalMemoryStruct) + header->bitFieldSize + (header->slotCount * sizeof(SlotEntryStruct)));
544 BinHeaderStruct* newBinHeader = (BinHeaderStruct*)(((char*)newHeader) + sizeof(TemporalMemoryStruct) + newHeader->bitFieldSize + (newHeader->slotCount * sizeof(SlotEntryStruct)));
545 for (uint32 n = 0; n < header->binCount; n++) {
546 if (binHeaderSizes[n].size == binHeader->size) {
547 // Just copy the full bin as is, header and all
548 memcpy((char*)newBinHeader, binHeader, (size_t)binHeader->size);
549 }
550 else {
551 binGrowth++;
552 // First set the full new bitfield to unoccupied
553 memset((char*)newBinHeader + sizeof(BinHeaderStruct), 255, binHeaderSizes[n].bitFieldSize);
554 // Copy old header and old bitFieldSize
555 memcpy((char*)newBinHeader, binHeader, sizeof(BinHeaderStruct) + binHeader->bitFieldSize);
556 newBinHeader->bitFieldSize = binHeaderSizes[n].bitFieldSize;
557 newBinHeader->blockCount = binHeaderSizes[n].blockCount;
558 newBinHeader->size = binHeaderSizes[n].size;
559 // Copy existing data
560 memcpy((char*)newBinHeader + sizeof(BinHeaderStruct) + newBinHeader->bitFieldSize,
561 (char*)binHeader + sizeof(BinHeaderStruct) + binHeader->bitFieldSize,
562 (size_t)(binHeader->size - (sizeof(BinHeaderStruct) + binHeader->bitFieldSize)));
563 }
564 binHeader = (BinHeaderStruct*)(((char*)binHeader) + binHeader->size);
565 newBinHeader = (BinHeaderStruct*)(((char*)newBinHeader) + newBinHeader->size);
566 }
567
568 if (slotGrowth && binGrowth)
569 LogPrint(0, LOG_MEMORY, 0, "Temporal Memory slots grown by: %u and %llu bins storage by %s to: %s total as requested",
570 slotGrowth,
571 binGrowth,
572 utils::BytifySize((double)newTotalSize - header->size).c_str(),
573 utils::BytifySize((double)newTotalSize).c_str()
574 );
575 else if (slotGrowth)
576 LogPrint(0, LOG_MEMORY, 0, "Temporal Memory slots grown by: %u (%s to: %s total) as requested",
577 slotGrowth,
578 utils::BytifySize((double)newTotalSize - header->size).c_str(),
579 utils::BytifySize((double)newTotalSize).c_str()
580 );
581 else
582 LogPrint(0, LOG_MEMORY, 0, "Temporal Memory %llu storage bins grown by: %s to: %s total as requested",
583 binGrowth,
584 utils::BytifySize((double)newTotalSize - header->size).c_str(),
585 utils::BytifySize((double)newTotalSize).c_str()
586 );
587
588 // Get rid of the old memory allocation
589 utils::CloseSharedMemorySegment((char*)header, memorySize);
590 header = newHeader;
591 memorySize = newTotalSize;
592 master->setDynamicShmemSize(memorySize);
593
594 return true;
595}
596
597
598//bool TemporalMemory::getSlotFromEOL(uint64 eol, uint16& slot) {
599// // Assume mutex is locked
600// uint64 now = GetTimeNow();
601// uint64 difTime = 0;
602// if (eol - now < 0)
603// return false;
604// difTime = eol - header->startTime;
605// uint16 difSlot = (uint16)(difTime / header->slotDuration);
606// // If too far in the future, choose end slot
607// if (difSlot >= header->slotCount)
608// difSlot = header->slotCount - 1;
609// // Find actual slot...
610// uint16 actualSlot = header->startSlot + difSlot;
611// if (actualSlot >= header->slotCount)
612// actualSlot -= header->slotCount;
613// slot = actualSlot;
614// return true;
615//}
616//
617//bool TemporalMemory::growSlotSize(uint64 growAmount) {
618// // Assume mutex is locked
619//
620// uint32 n;
621// //uint64 totalSize = sizeof(TemporalMemoryStruct) + (header->slotCount * sizeof(SlotHeaderStruct));
622// //for (n = 0; n<header->slotCount; n++)
623// // totalSize += slotHeaders[n].size;
624// //if (totalSize > header->size) {
625// // printf("Error!!!\n");
626// //}
627//
628// uint64 totalGrowAmount = growAmount * header->slotCount;
629// if ((header->size >= header->maxSize) || !header->growStep) {
630// LogPrint(0, LOG_MEMORY, 0, "Temporal Memory has reached maximum size: %s total, %s per slot, cannot grow by %s as requested",
631// utils::BytifySize((double)header->size).c_str(),
632// utils::BytifySize((double)header->slotSize).c_str(),
633// utils::BytifySize((double)totalGrowAmount).c_str()
634// );
635// return false;
636// }
637//
638// uint64 newTotalSize = header->size + header->growStep;
639// while (newTotalSize - header->size < totalGrowAmount)
640// newTotalSize += header->growStep;
641//
642// if (newTotalSize > header->maxSize) {
643// LogPrint(0, LOG_MEMORY, 0, "Temporal Memory cannot grow to %s as requested, current size: %s, maximum limit: %s",
644// utils::BytifySize((double)newTotalSize).c_str(),
645// utils::BytifySize((double)header->size).c_str(),
646// utils::BytifySize((double)header->maxSize).c_str()
647// );
648// return false;
649// }
650//
651// serial = master->incrementDynamicShmemSerial();
652//
653// TemporalMemoryStruct* newHeader = (TemporalMemoryStruct*) utils::CreateSharedMemorySegment(utils::StringFormat("PsycloneTemporalMemory_%u_%u", port, serial).c_str(), newTotalSize, true);
654// if (!newHeader) {
655// LogPrint(0, LOG_MEMORY, 0, "Temporal Memory couldn't allocate memory to grow by: %s to: %s total, %s per slot as requested",
656// utils::BytifySize((double)totalGrowAmount).c_str(),
657// utils::BytifySize((double)newTotalSize).c_str(),
658// utils::BytifySize((double)(newTotalSize/header->slotCount)).c_str()
659// );
660// return false;
661// }
662//
663// // Copy header and all the slot headers
664// memcpy(newHeader, header, sizeof(TemporalMemoryStruct) + (header->slotCount * sizeof(SlotHeaderStruct)));
665// newHeader->size = newTotalSize;
666// uint64 newDataSize = newTotalSize - sizeof(TemporalMemoryStruct) - (header->slotCount * sizeof(SlotHeaderStruct));
667// newHeader->slotSize = (uint64)(newDataSize / newHeader->slotCount);
668// SlotHeaderStruct* newSlotHeaders = (SlotHeaderStruct*)(((char*)newHeader) + sizeof(TemporalMemoryStruct));
669//
670// uint64 newOffset = sizeof(TemporalMemoryStruct) + (header->slotCount * sizeof(SlotHeaderStruct));
671//
672// // Now update each slot and copy the slot content across
673// for (n=0; n<header->slotCount; n++) {
674// SlotHeaderStruct* oldSlotHeader = &slotHeaders[n];
675// SlotHeaderStruct* newSlotHeader = &newSlotHeaders[n];
676// newSlotHeader->size = newHeader->slotSize;
677// newSlotHeader->offset = newOffset;
678// memcpy((char*)newHeader + newSlotHeader->offset, (char*)header + oldSlotHeader->offset, (size_t)oldSlotHeader->usage);
679// newOffset += newHeader->slotSize;
680// }
681//
682// // Get rid of the old memory allocation
683// utils::CloseSharedMemorySegment((char*)header, memorySize);
684// header = newHeader;
685// memorySize = newTotalSize;
686// slotHeaders = newSlotHeaders;
687// master->setDynamicShmemSize(memorySize);
688//
689// //totalSize = sizeof(TemporalMemoryStruct) + (header->slotCount * sizeof(SlotHeaderStruct));
690// //for (n = 0; n<header->slotCount; n++)
691// // totalSize += slotHeaders[n].size;
692// //if (totalSize > header->size) {
693// // printf("Error: !!!\n");
694// //}
695//
696// LogPrint(0, LOG_MEMORY, 0, "Temporal Memory size is now: %s total, %s per slot",
697// utils::BytifySize((double)header->size).c_str(),
698// utils::BytifySize((double)header->slotSize).c_str()
699// );
700// return true;
701//}
702//
703//bool TemporalMemory::checkSlots() {
704// // Assume mutex is locked
705// uint64 now = GetTimeNow();
706// while ( now - header->startTime > header->slotDuration) {
707// // Move the current slot one forward
708// header->startSlot++;
709// if (header->startSlot > header->slotCount)
710// header->startSlot -= header->slotCount;
711// header->startTime += header->slotDuration;
712// // printf("New startSlot: %u (%d)...\n", header->startSlot, GetTimeNow() - header->startTime);
713// // Find the slot
714// int16 delSlot = header->startSlot - 1;
715// if (delSlot < 0)
716// delSlot += header->slotCount;
717// // LogPrint(0,LOG_MEMORY,0,"Resetting slot %u", delSlot);
718// resetSlot(delSlot, header->startTime + (header->slotCount*header->slotDuration));
719// }
720// return true;
721//}
722//
723//bool TemporalMemory::resetSlot(uint16 slot, uint64 eol) {
725// SlotHeaderStruct* slotHeader = &slotHeaders[slot];
726// slotHeader->count = 0;
727// slotHeader->usage = 0;
728// slotHeader->eol = eol;
729// // we could resize the slot back to normal to save memory
730// return true;
731//}
732//
733//uint64 TemporalMemory::calcResizeAmount(uint32 msgSize) {
734// if (msgSize < 10000)
735// return 256000;
736// else if (msgSize < 100000)
737// return 1024000;
738// else
739// return msgSize*10;
740//}
741
743 std::string str = utils::StringFormat("TemporalMemory \tsize: %s \tusage: %s \tslots: %u \tinuse: %u\n",
744 utils::BytifySize((double)header->size).c_str(),
745 utils::BytifySize((double)header->usage).c_str(),
746 header->slotCount,
747 header->slotsInUse
748 );
749
750 BinHeaderStruct* binHeader = (BinHeaderStruct*)(((char*)header) + sizeof(TemporalMemoryStruct) + header->bitFieldSize + (header->slotCount * sizeof(SlotEntryStruct)));
751 for (uint32 n = 0; n < header->binCount; n++) {
752 str += utils::StringFormat(" Bin [%s]\tsize: %s \tusage: %s \tblocks: %u \tinuse: %u\n",
753 utils::BytifySize((double)binHeader->blockSize).c_str(),
754 utils::BytifySize((double)binHeader->size).c_str(),
755 utils::BytifySize((double)binHeader->usage).c_str(),
756 utils::BytifySize((double)binHeader->blockCount).c_str(),
757 utils::BytifySize((double)binHeader->blocksInUse).c_str()
758 );
759 binHeader = (BinHeaderStruct*)(((char*)binHeader) + binHeader->size);
760 }
761 LogPrint(0, LOG_MEMORY, 0, str.c_str());
762 return true;
763}
764
766 uint32 slotCount = 1000;
767 uint16 binCount = 2;
768 uint32 minBlockSize = 1024; // 1kb
769 uint32 maxBlockSize = 64*1024; // 64kb
770 uint64 initSize = 50000000; // 50MB
771 uint64 maxSize = 500000000; // 500MB
772 uint64 msgTTL = 5000000; // 5s
773 uint32 dataSize;
774
775 // Keep loop counts modest so the whole test runs in ~1-2s while still
776 // producing stable throughput metrics.
777 uint32 outerCircle = 40;
778 uint32 innerCircle = 30;
779
780 unittest::progress(0, "create master memory");
781 MasterMemory masterMemory;
782 if (!masterMemory.create(12000)) {
783 unittest::fail("Cannot create MasterMemory");
784 return false;
785 }
786
787 unittest::progress(5, "create temporal memory");
788 TemporalMemory temporalMemory(&masterMemory);
789
790 if (!temporalMemory.create(slotCount, binCount, minBlockSize, maxBlockSize, initSize, maxSize)) {
791 unittest::fail("Cannot create TemporalMemory");
792 return false;
793 }
794
795 uint64 n, m, bytes;
796 DataMessage* msg;
797 uint64 now = 0;
798 uint64 id, sysTotal, sysUsage;
799
800 if (temporalMemory.getMemoryUsage(sysTotal, sysUsage))
801 unittest::detail("[---] Temporal Memory in use:\t%10s\t[%s allocated])",
802 utils::BytifySize((double)sysUsage).c_str(), utils::BytifySize((double)sysTotal).c_str());
803
804 char* data;
805
806 std::map<uint64, uint64> sentMessages;
807
808 // Aggregate throughput / latency across all write iterations.
809 double totalWriteUS = 0.0; // total microseconds spent inserting messages
810 uint64 totalBytes = 0; // total bytes inserted
811 uint64 totalMsgs = 0; // total messages inserted
812
813 unittest::progress(10, "insert / retrieve round-trips");
814 for (n = 0; n<outerCircle; n++) {
815 now = GetTimeNow();
816 bytes = 0;
817 for (m = 0; m<innerCircle; m++) {
818 msg = new DataMessage();
819 dataSize = (uint32)utils::RandomValue(minBlockSize / 3.0, maxBlockSize);
820 data = new char[dataSize];
821 msg->setData("Data", data, dataSize);
822 delete[] data;
823 msg->setCreatedTime(now);
824 msg->setTTL(msgTTL);
825 msg->setSerial((n*1000) + m);
826 if (!temporalMemory.insertMessage(msg, id)) {
827 unittest::fail("TemporalMemory insert memory failed at [%llu][%llu]", n, m);
828 delete(msg);
829 return false;
830 }
831 bytes += msg->getSize();
832 sentMessages[(n * 1000) + m] = id;
833 delete(msg);
834 }
835 double iterUS = (double)GetTimeAge(now);
836 totalWriteUS += iterUS;
837 totalBytes += bytes;
838 totalMsgs += innerCircle;
839
840 if (temporalMemory.getMemoryUsage(sysTotal, sysUsage))
841 unittest::detail("[%llu] Temporal Memory in use:\t%10s\t[%s allocated] - %.3fms/msg - %.3fMB/sec",
842 n, utils::BytifySize((double)sysUsage).c_str(), utils::BytifySize((double)sysTotal).c_str(),
843 (iterUS / (double)innerCircle) / 1000.0,
844 ((double)bytes / (iterUS / 1000000.0)) / (1024.0*1024.0)
845 );
846
847 for (m = 0; m<innerCircle; m++) {
848 msg = temporalMemory.getCopyOfMessage(sentMessages[(n * 1000) + m]);
849 if (!msg) {
850 unittest::fail("TemporalMemory retrieve failed at [%llu][%llu]", n, m);
851 return false;
852 }
853 else if (!msg->isValid()) {
854 unittest::fail("TemporalMemory retrieve got invalid data at [%llu][%llu]", n, m);
855 delete(msg);
856 return false;
857 }
858 if (msg->getSerial() != (n * 1000) + m) {
859 unittest::detail("[%llu][%llu] TemporalMemory retrieve got wrong message back (%llu != %llu)...",
860 n, m, msg->getSerial(), (n * 1000) + m);
861 }
862 delete(msg);
863 }
864
865 unittest::progress(10 + (int)((n + 1) * 70 / outerCircle), "insert / retrieve round-trips");
866 }
867
868 // Record aggregate write performance metrics.
869 if (totalMsgs && totalWriteUS > 0.0) {
870 unittest::metric("write_throughput",
871 ((double)totalBytes / (totalWriteUS / 1000000.0)) / (1024.0*1024.0), "MB/s", true);
872 unittest::metric("avg_msg_time",
873 (totalWriteUS / (double)totalMsgs) / 1000.0, "ms", false);
874 }
875
876 // Exercise expiry / maintenance reclaim. Sleep just past the TTL once so
877 // messages expire, then run a bounded maintenance loop. Keeps total runtime
878 // to ~1-2s rather than the original ~20s of per-iteration sleeps.
879 unittest::progress(82, "expiry / maintenance reclaim");
880 utils::Sleep(200);
881 uint32 maintLoops = 20;
882 for (n = 0; n<maintLoops; n++) {
883 temporalMemory.maintenance();
884 if (temporalMemory.getMemoryUsage(sysTotal, sysUsage))
885 unittest::detail("[%llu] Temporal Memory in use:\t%10s\t[%s allocated]",
886 n, utils::BytifySize((double)sysUsage).c_str(), utils::BytifySize((double)sysTotal).c_str());
887 unittest::progress(82 + (int)((n + 1) * 16 / maintLoops), "expiry / maintenance reclaim");
888 }
889
890 unittest::progress(100, "done");
891 return true;
892}
893
896 "Temporal shared-memory insert/retrieve, growth and expiry reclaim", "memory");
897}
898
899} // namespace cmlabs
900
901
902
903
904
905
906
907
908
909
910
911
912
#define TEMPORALMEMORYID
Definition ObjectIDs.h:48
#define clmax(a, b)
Definition Standard.h:114
Slot/bin shared-memory store for time-limited DataMessages (the node's "dynamic" memory).
#define CHECKTEMPORALMEMORYSERIAL
Re-open the temporal segment if the master's resize serial no longer matches ours (i....
#define GETMSGNODE(id)
#define SETMSGID(id, node, serial, slot)
#define GETMSGSERIAL(id)
#define GETMSGSLOT(id)
#define BITOCCUPIED
Definition Types.h:34
#define BITFREE
Definition Types.h:33
Small, dependency-free unit test harness used by all CMSDK object tests.
#define LOG_MEMORY
Definition Utils.h:199
#define LogPrint
Definition Utils.h:313
The central Psyclone data container: a self-contained binary message with typed, named user entries.
bool setTTL(uint64 ttl)
setTTL(uint64 ttl)
DataMessageHeader * data
Pointer to the message's flat memory block (header + user entries).
uint32 getSize()
getSize() Get message size Many types of data of any size can be put into a message as user entries; ...
bool setCreatedTime(uint64 t)
setCreatedTime(uint64 t)
bool isValid()
isValid() Checks that the message memory block exists and carries the current-format object id (DATAM...
uint64 getEOL()
getEOL()
uint64 getSerial()
getSerial()
bool setData(const char *key, const char *value, uint32 size)
setData(const char* key, const char* value, uint32 size)
bool setSerial(uint64 serial)
setSerial(uint64 serial)
Handle to the node's master shared-memory segment (MemoryMasterStruct).
bool create(uint16 port)
Create the master segment for the node instance listening on port.
Pure-virtual interface for shared-segment bookkeeping (implemented by MasterMemory).
bool getMemoryUsage(uint64 &alloc, uint64 &usage)
Report allocation/usage of the temporal segment.
bool maintenance()
Expire slots whose EOL has passed and reclaim their blocks.
bool logUsage()
Write current usage statistics to the log.
DataMessage * getCopyOfMessage(uint64 id)
Retrieve a private copy of a stored message.
bool setNodeID(uint16 id)
Set the node id used when composing message ids.
bool insertMessage(DataMessage *msg, uint64 &id)
Copy msg into shared memory until its EOL passes.
bool create(uint32 slotCount, uint16 binCount, uint32 minBlockSize, uint32 maxBlockSize, uint64 initSize, uint64 maxSize, uint32 growSteps=8)
Create the temporal segment (master process only).
TemporalMemory(MemoryController *master)
static bool UnitTest()
Self-test of the temporal store.
bool open()
Attach to the existing temporal segment (size/serial from the master).
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
Recursive mutual-exclusion lock, optionally named for cross-process use.
Definition Utils.h:463
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
Definition PsyTime.cpp:69
int64 GetTimeAge(uint64 t)
Age of a timestamp relative to now.
Definition PsyTime.cpp:25
bool GetLastOccupiedBitLoc(const char *bitfield, uint32 bytesize, uint32 &loc)
Find the highest set (occupied) bit.
Definition Utils.cpp:2689
bool Sleep(uint32 ms)
Suspend the calling thread.
Definition Utils.cpp:2802
char * OpenSharedMemorySegment(const char *name, uint64 size)
Open and map an existing named shared memory segment.
Definition Utils.cpp:2259
char * CreateSharedMemorySegment(const char *name, uint64 size, bool force=false)
Create a named shared memory segment and map it into this process.
Definition Utils.cpp:2156
double RandomValue()
Uniform random double in [0,1).
Definition Utils.cpp:7678
bool CloseSharedMemorySegment(char *data, uint64 size)
Unmap a segment previously created/opened here.
Definition Utils.cpp:2374
bool GetFirstFreeBitLocN(const char *bitfield, uint32 bytesize, uint32 num, uint32 &loc)
Find the first run of num consecutive 0 bits.
Definition Utils.cpp:2468
std::string BytifySize(double val)
Format a byte count with binary units, e.g.
Definition Utils.cpp:7724
bool GetFirstFreeBitLoc(const char *bitfield, uint32 bytesize, uint32 &loc)
Find the first 0 (free) bit.
Definition Utils.cpp:2445
std::string StringFormat(const char *format,...)
printf into a std::string.
Definition Utils.cpp:6626
bool SetBitN(uint32 loc, uint32 num, bit value, char *bitfield, uint32 bytesize)
Set num consecutive bits starting at loc to value.
Definition Utils.cpp:2601
bool SetBit(uint32 loc, bit value, char *bitfield, uint32 bytesize)
Set bit loc to value.
Definition Utils.cpp:2569
uint32 Calc32BitFieldSize(uint32 bitsize)
Compute the byte size needed for a bitfield of bitsize bits, rounded up to a 32-bit boundary.
Definition Utils.cpp:2435
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
void Register_TemporalMemory_Tests()
Header of one fixed-block-size bin inside the temporal segment.
uint64 usage
Payload bytes currently stored.
uint32 blocksInUse
Blocks currently allocated.
uint32 blockSize
Byte size of each block.
uint64 size
Total bin size in bytes (header + bitfield + data).
uint32 blockCount
Number of fixed-size blocks in this bin.
Per-message slot record: which bin/blocks hold the message and until when.
uint64 size
Message byte size; 0 means the slot is not in use.
uint64 eol
Absolute end-of-life timestamp (µs); slot reclaimable after this.
uint32 bin
Index of the bin holding the message data.
uint32 startBlock
First block index used within the bin.
uint16 serial
Reuse serial; incremented when the slot is recycled and checked against the serial packed in the mess...
uint64 usage
Bytes actually reserved (blocks used * blocksize >= size).
uint32 blockUsage
Number of contiguous blocks used.
uint64 offset
Byte offset of the message inside the bin's data area.
Root header of the temporal-memory segment.
uint32 cid
Check/magic id validated on attach.