AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.5.17
BlockStore.hpp
Go to the documentation of this file.
1 //
3 // Copyright (c) 2010-2026 60East Technologies Inc., All Rights Reserved.
4 //
5 // This computer software is owned by 60East Technologies Inc. and is
6 // protected by U.S. copyright laws and other laws and by international
7 // treaties. This computer software is furnished by 60East Technologies
8 // Inc. pursuant to a written license agreement and may be used, copied,
9 // transmitted, and stored only in accordance with the terms of such
10 // license agreement and with the inclusion of the above copyright notice.
11 // This computer software or any other copies thereof may not be provided
12 // or otherwise made available to any other person.
13 //
14 // U.S. Government Restricted Rights. This computer software: (a) was
15 // developed at private expense and is in all respects the proprietary
16 // information of 60East Technologies Inc.; (b) was not developed with
17 // government funds; (c) is a trade secret of 60East Technologies Inc.
18 // for all purposes of the Freedom of Information Act; and (d) is a
19 // commercial item and thus, pursuant to Section 12.212 of the Federal
20 // Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
21 // Government's use, duplication or disclosure of the computer software
22 // is subject to the restrictions set forth by 60East Technologies Inc..
23 //
25 
26 #ifndef _BLOCKSTORE_H_
27 #define _BLOCKSTORE_H_
28 #include <amps/ampsplusplus.hpp>
29 #include <amps/Buffer.hpp>
30 #include <sstream>
31 #include <string>
32 #include <map>
33 #include <limits>
34 #include <amps/ampscrc.hpp>
35 #if __cplusplus >= 201103L || _MSC_VER >= 1900
36  #include <atomic>
37 #endif
38 
39 #ifdef _WIN32
40  #include <intrin.h>
41  #include <sys/timeb.h>
42 #else
43  #include <sys/time.h>
44 #endif
45 #include <iostream>
46 
51 
52 namespace AMPS
53 {
59  class BlockStore
60  {
61  public:
64  enum Constants : amps_uint32_t
65  {
66  DEFAULT_BLOCK_HEADER_SIZE = 32,
67  DEFAULT_BLOCKS_PER_REALLOC = 1000,
68  DEFAULT_BLOCK_SIZE = 2048,
69  MAX_BLOCKS_PER_REALLOC = DEFAULT_BLOCKS_PER_REALLOC * 10
70  };
71 
74  class Block
75  {
76  public:
77  // The offset of the Block's data in the buffer.
78  size_t _offset;
79  // The sequence number associated with the Block.
80  amps_uint64_t _sequence;
81  // The next Block in the chain when data is in multiple Blocks.
82  Block* _nextInChain;
83  // The next Block in list of available or free Blocks.
84  Block* _nextInList;
85 
86  // Create Block with given offset
87  Block(size_t offset_) : _offset(offset_), _sequence(0)
88  , _nextInChain(0), _nextInList(0)
89  { ; }
90 
91  // Create Block with _nextInList at an address one Block farther
92  // than self. Convenient for creating arrays of Blocks.
93  Block() : _offset(0), _sequence(0)
94  , _nextInChain(0), _nextInList((Block*)(this + 1))
95  { ; }
96 
97  // Init Block to an offset at index_ * blockSize_
98  Block* init(size_t index_, amps_uint32_t blockSize_)
99  {
100  _offset = index_ * blockSize_;
101  return this;
102  }
103 
104  // Set Block to given offset and return pointer to self
105  Block* setOffset(size_t offset_)
106  {
107  _offset = offset_;
108  return this;
109  }
110 
111  };
112 
113  private:
114  // Typedefs
115  typedef Lock<Mutex> BufferLock;
116  typedef Unlock<Mutex> BufferUnlock;
117  typedef bool (*ResizeHandler)(size_t, void*);
118  typedef std::vector<Block*> BlockList;
119 
120  public:
136  BlockStore(Buffer* buffer_,
137  amps_uint32_t blocksPerRealloc_ = DEFAULT_BLOCKS_PER_REALLOC,
138  amps_uint32_t blockHeaderSize_ = DEFAULT_BLOCK_HEADER_SIZE,
139  amps_uint32_t blockSize_ = DEFAULT_BLOCK_SIZE)
140  : _buffer(buffer_), _freeList(0), _usedList(0)
141  , _endOfUsedList(0), _blocksPerRealloc(blocksPerRealloc_)
142  , _blockSize(blockSize_), _blockHeaderSize(blockHeaderSize_)
143  , _blocksAvailable(0), _resizeHandler(0), _resizeUserData(0)
144  , _resizing(false)
145  {
146  if (_blocksPerRealloc == 0)
147  {
148  _blocksPerRealloc = DEFAULT_BLOCKS_PER_REALLOC;
149  }
150  else if (_blocksPerRealloc > MAX_BLOCKS_PER_REALLOC)
151  {
152  _blocksPerRealloc = MAX_BLOCKS_PER_REALLOC;
153  }
154  if (_blockHeaderSize < DEFAULT_BLOCK_HEADER_SIZE
155  || _blockHeaderSize > (std::numeric_limits<amps_uint32_t>::max)() / 2)
156  {
157  _blockHeaderSize = DEFAULT_BLOCK_HEADER_SIZE;
158  }
159  if (_blockSize == 0 || _blockSize < _blockHeaderSize * 2)
160  {
161  _blockSize = DEFAULT_BLOCK_SIZE;
162  if (_blockSize < _blockHeaderSize * 2)
163  {
164  _blockHeaderSize = DEFAULT_BLOCK_HEADER_SIZE;
165  }
166  }
167  if (_blocksPerRealloc > (std::numeric_limits<size_t>::max)() / _blockSize)
168  {
169  _blocksPerRealloc = DEFAULT_BLOCKS_PER_REALLOC;
170  _blockSize = DEFAULT_BLOCK_SIZE;
171  _blockHeaderSize = DEFAULT_BLOCK_HEADER_SIZE;
172  }
173  }
174 
178  {
179  for (BlockList::iterator i = _blockList.begin();
180  i != _blockList.end(); ++i)
181  {
182  delete[] *i;
183  }
184  delete _buffer;
185  }
186 
189  amps_uint32_t getBlockSize() const
190  {
191  return _blockSize;
192  }
193 
196  amps_uint32_t getBlockHeaderSize() const
197  {
198  return _blockHeaderSize;
199  }
200 
205  void acquireRead() const
206  {
207  _lock.acquireRead();
208  }
209 
212  void releaseRead() const
213  {
214  _lock.releaseRead();
215  }
216 
219  void signalAll()
220  {
221  _lock.signalAll();
222  }
223 
226  void wait()
227  {
228  _lock.wait();
229  }
230 
235  bool wait(long timeout_)
236  {
237  return _lock.wait(timeout_);
238  }
239 
249  void setResizeHandler(ResizeHandler resizeHandler_, void* userData_)
250  {
251  _resizeHandler = resizeHandler_;
252  _resizeUserData = userData_;
253  }
254 
258  // Lock should already be acquired
259  Block* front() const
260  {
261  return _usedList;
262  }
263 
268  // Lock should already be acquired
269  Block* back() const
270  {
271  return _endOfUsedList;
272  }
273 
277  // Lock should already be acquired
278  void setFreeList(Block* block_, amps_uint32_t freeCount_)
279  {
280  _freeList = block_;
281  _blocksAvailable = freeCount_;
282  }
283 
286  // Lock should already be acquired
287  void setUsedList(Block* block_)
288  {
289  _usedList = block_;
290  }
291 
294  // Lock should already be acquired
295  void setEndOfUsedList(Block* block_)
296  {
297  _endOfUsedList = block_;
298  }
299 
303  // Lock should already be acquired
304  void addBlocks(Block* blockArray_)
305  {
306  _blockList.push_back(blockArray_);
307  }
308 
313  // Lock should already be acquired
314  Block* get(amps_uint32_t numBlocksInChain_)
315  {
316  // Check that we have enough blocks
317  // Do this in a loop since resize can possibly return without resizing
318  // and may still leave us needing more space.
319  while (_blocksAvailable < numBlocksInChain_) // -V776
320  {
321  // Resize by required multiple of blockPerRealloc
322  unsigned int blocksNeeded = numBlocksInChain_ - _blocksAvailable;
323  amps_uint64_t addedBlocks64 = ((amps_uint64_t)blocksNeeded
324  / _blocksPerRealloc + 1)
325  * _blocksPerRealloc;
326  if (addedBlocks64 > (std::numeric_limits<amps_uint32_t>::max)()
327  || addedBlocks64 > (std::numeric_limits<amps_uint32_t>::max)()
328  - _blocksAvailable)
329  {
330  throw StoreException("BlockStore block count overflow while growing buffer.");
331  }
332  amps_uint32_t addedBlocks = (amps_uint32_t)addedBlocks64;
333  size_t resizeSize = getResizeSize(addedBlocks);
334  if (_buffer->getSize() > (std::numeric_limits<size_t>::max)() - resizeSize)
335  {
336  throw StoreException("BlockStore size overflow while growing buffer.");
337  }
338  size_t size = _buffer->getSize() + resizeSize;
339  resize(size);
340  }
341  // Return first free block with others as _nextInChain
342  Block* first = 0;
343  Block* last = 0;
344  Block* next = 0;
345  for (unsigned int i = 0; i < numBlocksInChain_; ++i)
346  {
347  // Take from free list and advance
348  next = _freeList;
349  _freeList = _freeList->_nextInList;
350  next->_nextInList = 0;
351  if (!first)
352  {
353  // First, set it up
354  first = next;
355  last = next;
356  }
357  else
358  {
359  // Not first, add it to chain
360  last->_nextInChain = next;
361  last = next;
362  }
363  }
364  assert(first);
365  // Set _usedList or add it to the end of the used list
366  if (!_usedList)
367  {
368  _usedList = first;
369  }
370  else
371  {
372  _endOfUsedList->_nextInList = first;
373  }
374  _endOfUsedList = first;
375  _blocksAvailable -= numBlocksInChain_;
376  return first;
377  }
378 
382  // Lock should already be acquired
383  void put(Block* block_)
384  {
385  assert(_usedList);
386  assert(_endOfUsedList);
387  // Remove from used list
388  if (_usedList == block_)
389  {
390  // Easy
391  _usedList = _usedList->_nextInList;
392  if (!_usedList)
393  {
394  _endOfUsedList = 0;
395  }
396  }
397  else
398  {
399  // Search and remove the block
400  Block* used = _usedList;
401  while (used)
402  {
403  if (used->_nextInList == block_)
404  {
405  used->_nextInList = block_->_nextInList;
406  break;
407  }
408  used = used->_nextInList;
409  if (!_usedList) // -V1051
410  {
411  _endOfUsedList = 0;
412  }
413  }
414  }
415  // Add to free list
416  _flattenToFreeList(block_);
417  }
418 
422  // Lock should already be acquired
423  AMPS_ATOMIC_BASE_TYPE put(amps_uint64_t sequence_)
424  {
425  assert(_usedList);
426  assert(_endOfUsedList);
427  Block* used = _usedList;
428  AMPS_ATOMIC_BASE_TYPE removalCount = 0;
429  while (used && used->_sequence <= sequence_)
430  {
431  Block* next = used->_nextInList;
432  // Add to free list
433  _flattenToFreeList(used);
434  used = next;
435  ++removalCount;
436  }
437  _usedList = used;
438  if (!used)
439  {
440  _endOfUsedList = 0;
441  }
442  return removalCount;
443  }
444 
448  // Lock should already be acquired
449  void putAll(Block* block_)
450  {
451  // Remove from used list
452  Block* newEndOfUsedList = 0;
453  for (Block* used = _usedList; used; used = used->_nextInList)
454  {
455  if (used == block_)
456  {
457  if (newEndOfUsedList)
458  {
459  newEndOfUsedList->_nextInList = 0;
460  }
461  else
462  {
463  _usedList = 0;
464  }
465  _endOfUsedList = newEndOfUsedList;
466  }
467  newEndOfUsedList = used;
468  }
469  // Add all remaining to free list
470  Block* next = 0;
471  for (Block* block = block_; block; block = next)
472  {
473  next = block->_nextInList;
474  _flattenToFreeList(block);
475  }
476  }
477 
480  // Lock should already be held
481  void init()
482  {
483  amps_uint32_t blockSize = getBlockSize();
484  size_t startSize = _buffer->getSize();
485  if (!startSize)
486  {
488  }
489  else if (startSize < blockSize)
490  {
491  resize(blockSize);
492  }
493  else
494  {
495  // How many blocks are we resizing
496  size_t availableBlocks = startSize / blockSize;
497  if (availableBlocks > (std::numeric_limits<amps_uint32_t>::max)())
498  {
499  throw StoreException("BlockStore contains more blocks than can be indexed.");
500  }
501  amps_uint32_t numBlocks = (amps_uint32_t)availableBlocks;
502  _freeList = new Block[numBlocks];
503  _blockList.push_back(_freeList);
504  for (size_t i = 0; i < numBlocks; ++i)
505  {
506  _freeList[i].init(i, blockSize);
507  }
508  _freeList[numBlocks - 1]._nextInList = 0;
509  _blocksAvailable += numBlocks;
510  }
511  assert(_freeList);
512  assert(_blocksAvailable);
513  }
514 
517  size_t getDefaultResizeSize() const
518  {
519  return getResizeSize(_blocksPerRealloc);
520  }
521 
524  amps_uint32_t getDefaultResizeBlocks() const
525  {
526  return _blocksPerRealloc;
527  }
528 
536  // Lock should already be held
537  Block* resizeBuffer(size_t size_, amps_uint32_t* pNewBlocks_)
538  {
539  Block* freeList = 0;
540  while (_resizing)
541  {
542  if (_buffer->getSize() >= size_)
543  {
544  return freeList;
545  }
546  if (!_lock.wait(1000))
547  {
548  amps_invoke_waiting_function();
549  }
550  }
551  AtomicFlagFlip flip(&_resizing);
552  bool okToResize = false;
553  if (true)
554  {
555  BufferUnlock u(_lock);
556  // Don't do anything if resizeHandler says no
557  okToResize = _canResize(size_);
558  }
559  if (!okToResize)
560  {
561  return freeList;
562  }
563  try
564  {
565  _lock.signalAll();
566  size_t oldSize = _buffer->getSize();
567  if (oldSize >= size_)
568  {
569  *pNewBlocks_ = 0;
570  return freeList;
571  }
572  const amps_uint32_t blockSize = getBlockSize();
573  size_t oldBlocks = oldSize / blockSize;
574  size_t requestedBlocks = size_ / blockSize;
575  size_t requestedAddedBlocks = requestedBlocks - oldBlocks;
576  if (requestedAddedBlocks
577  > (std::numeric_limits<amps_uint32_t>::max)())
578  {
579  throw StoreException("BlockStore block count overflow while resizing buffer.");
580  }
581  _buffer->setSize(size_);
582  size_t actualSize = _buffer->getSize();
583  size_t actualBlocks = actualSize / blockSize;
584  size_t addedBlocks = actualBlocks - oldBlocks;
585  if (addedBlocks > (std::numeric_limits<amps_uint32_t>::max)())
586  {
587  throw StoreException("BlockStore block count overflow after resizing buffer.");
588  }
589  _buffer->zero(oldSize, actualSize - oldSize);
590  // Count complete blocks in the actual resized buffer. Buffer
591  // implementations may round the requested size for alignment.
592  *pNewBlocks_ = (amps_uint32_t)addedBlocks;
593  if (!*pNewBlocks_)
594  {
595  return freeList;
596  }
597  freeList = new Block[*pNewBlocks_];
598  for (size_t i = 0; i < *pNewBlocks_; ++i)
599  {
600  freeList[i].init(oldBlocks + i, blockSize);
601  }
602  freeList[*pNewBlocks_ - 1]._nextInList = 0;
603  }
604 #ifdef _WIN32
605  catch (const std::bad_alloc&)
606 #else
607  catch (const std::bad_alloc& e)
608 #endif
609  {
610  std::ostringstream os;
611  os << "BlockStore failed to allocate " << size_
612  << " bytes for resize of store from " << _buffer->getSize()
613  << " bytes.";
614  throw StoreException(os.str());
615  }
616  return freeList;
617  }
618 
622  // Lock should already be held
623  void resize(size_t size_)
624  {
625  amps_uint32_t newBlocks = 0;
626  Block* addedBlockList = resizeBuffer(size_, &newBlocks);
627  if (!addedBlockList || !newBlocks)
628  {
629  // Maybe we didn't have to allocate in this thread
630  return;
631  }
632  if (newBlocks > (std::numeric_limits<amps_uint32_t>::max)()
633  - _blocksAvailable)
634  {
635  delete[] addedBlockList;
636  throw StoreException("BlockStore block count overflow after resizing buffer.");
637  }
638  _blockList.push_back(addedBlockList);
639  addedBlockList[newBlocks - 1]._nextInList = _freeList;
640  _freeList = addedBlockList;
641  _blocksAvailable += newBlocks;
642  assert(_blocksAvailable);
643  }
644 
647  // Lock should be held, no blocks should be used or allocated
648  amps_uint32_t setBlockSize(amps_uint32_t blockSize_)
649  {
650  if (_usedList || _freeList)
651  {
652  return 0;
653  }
654  amps_uint32_t oldSize = _blockSize;
655  _blockSize = blockSize_;
656  return oldSize;
657  }
658 
662  // Lock should be held, no blocks should be used or allocated
663  amps_uint32_t setBlockHeaderSize(amps_uint32_t blockHeaderSize_)
664  {
665  if (_usedList || _freeList)
666  {
667  return 0;
668  }
669  amps_uint32_t oldSize = _blockHeaderSize;
670  _blockHeaderSize = blockHeaderSize_;
671  return oldSize;
672  }
673 
676  // Lock should already be held
678  {
679  return _buffer;
680  }
681 
682  private:
683  size_t getResizeSize(amps_uint32_t blocks_) const
684  {
685  if (_blockSize && blocks_ > (std::numeric_limits<size_t>::max)() / _blockSize)
686  {
687  throw StoreException("BlockStore size overflow calculating buffer growth.");
688  }
689  return (size_t)blocks_ * (size_t)_blockSize;
690  }
691 
693  bool _canResize(size_t requestedSize_)
694  {
695  if (_resizeHandler)
696  {
697  return _resizeHandler(requestedSize_, _resizeUserData);
698  }
699  else
700  {
701  return true;
702  }
703  }
704 
705  // Lock should already be acquired
706  void _flattenToFreeList(Block* block_)
707  {
708  // Flatten chain to front of free list
709  Block* current = block_;
710  while (current)
711  {
712  Block* chain = current->_nextInChain;
713  // Clear the header
714  _buffer->zero(current->_offset, _blockHeaderSize);
715  // Prepend to the free list and clear other values
716  current->_nextInList = _freeList;
717  _freeList = current;
718  ++_blocksAvailable;
719  current->_sequence = (amps_uint64_t)0;
720  current->_nextInChain = 0;
721  current = chain;
722  }
723  assert(_freeList);
724  assert(_blocksAvailable);
725  }
726 
727  // Member variables
728  // Buffer to use for storage
729  Buffer* _buffer;
730 
731  // The Block accounting
732  Block* _freeList;
733  Block* _usedList;
734  Block* _endOfUsedList;
735  // How much to resize buffer when needed
736  amps_uint32_t _blocksPerRealloc;
737  // How big is each Block, and what part is header
738  amps_uint32_t _blockSize;
739  amps_uint32_t _blockHeaderSize;
740  // How many blocks are free
741  amps_uint32_t _blocksAvailable;
742  // ResizeHandler to call before resizing
743  ResizeHandler _resizeHandler;
744  // ResizeHandler data
745  void* _resizeUserData;
746  // List of every allocated slab of Blocks
747  BlockList _blockList;
748  // Flag to control resizing
749  AMPS_ATOMIC_TYPE_8 _resizing;
750 
751  // Lock for _buffer
752  mutable Mutex _lock;
753 
754  };
755 
756 }
757 
758 #endif
759 
Provides AMPS::Buffer, an abstract base class used by the store implementations in the AMPS client.
Core type, function, and class declarations for the AMPS C++ client.
Used as metadata for each block in a Buffer.
Definition: BlockStore.hpp:75
Used as a base class for other stores in the AMPS C++ client, this is an implementation that breaks a...
Definition: BlockStore.hpp:60
void signalAll()
Signal lock waiters.
Definition: BlockStore.hpp:219
void putAll(Block *block_)
Return all Blocks starting with the given Block to the free list.
Definition: BlockStore.hpp:449
Block * front() const
Get the first used block in the store.
Definition: BlockStore.hpp:259
Block * back() const
Get the last used block in the store.
Definition: BlockStore.hpp:269
Constants
Default constant values for BlockStore.
Definition: BlockStore.hpp:65
void addBlocks(Block *blockArray_)
Allow users to create Block arrays during recovery that are tracked for cleanup here with all other B...
Definition: BlockStore.hpp:304
BlockStore(Buffer *buffer_, amps_uint32_t blocksPerRealloc_=DEFAULT_BLOCKS_PER_REALLOC, amps_uint32_t blockHeaderSize_=DEFAULT_BLOCK_HEADER_SIZE, amps_uint32_t blockSize_=DEFAULT_BLOCK_SIZE)
Create a BlockStore using buffer_ and default block size, that grows by blocksPerRealloc_ blocks when...
Definition: BlockStore.hpp:136
void setEndOfUsedList(Block *block_)
Allow containing classes to initialize the used list in recovery.
Definition: BlockStore.hpp:295
void acquireRead() const
Acquire the lock for this object.
Definition: BlockStore.hpp:205
void setUsedList(Block *block_)
Allow containing classes to initialize the used list in recovery.
Definition: BlockStore.hpp:287
Block * resizeBuffer(size_t size_, amps_uint32_t *pNewBlocks_)
Resize the buffer to the requested size, returning all new space.
Definition: BlockStore.hpp:537
~BlockStore()
Destructor that cleans up the buffer and other associated memory.
Definition: BlockStore.hpp:177
bool wait(long timeout_)
Wait timeout_ ms for a signal.
Definition: BlockStore.hpp:235
Block * get(amps_uint32_t numBlocksInChain_)
Get the requested number of Blocks as a chain from the free list.
Definition: BlockStore.hpp:314
void setFreeList(Block *block_, amps_uint32_t freeCount_)
Allow containing classes to initialize the free list in recovery.
Definition: BlockStore.hpp:278
size_t getDefaultResizeSize() const
Return the default number of bytes for each resize.
Definition: BlockStore.hpp:517
Buffer * getBuffer()
Return the buffer underlying the store for direct write/read.
Definition: BlockStore.hpp:677
void releaseRead() const
Release the lock for this object. Used by RAII templates.
Definition: BlockStore.hpp:212
void resize(size_t size_)
Resize the buffer to the requested size, adding all new space as unused Blocks for the free list.
Definition: BlockStore.hpp:623
void setResizeHandler(ResizeHandler resizeHandler_, void *userData_)
Set a resize handler that is called with the new total size of the Buffer.
Definition: BlockStore.hpp:249
amps_uint32_t setBlockHeaderSize(amps_uint32_t blockHeaderSize_)
Set the size to use for the header for all Blocks.
Definition: BlockStore.hpp:663
AMPS_ATOMIC_BASE_TYPE put(amps_uint64_t sequence_)
Return all Blocks with sequence <= sequence_ for reuse.
Definition: BlockStore.hpp:423
amps_uint32_t setBlockSize(amps_uint32_t blockSize_)
Set the size to use for all Blocks.
Definition: BlockStore.hpp:648
void init()
Initialize, assuming that _buffer has no existing information.
Definition: BlockStore.hpp:481
amps_uint32_t getBlockSize() const
Get the size of each Block, as set in the constructor.
Definition: BlockStore.hpp:189
void wait()
Wait for a signal.
Definition: BlockStore.hpp:226
amps_uint32_t getDefaultResizeBlocks() const
Return the default number of blocks for each resize.
Definition: BlockStore.hpp:524
amps_uint32_t getBlockHeaderSize() const
Get the size of a header within each Block, as set in the constructor.
Definition: BlockStore.hpp:196
void put(Block *block_)
Return the given chain of Blocks to the free list for reuse.
Definition: BlockStore.hpp:383
Abstract base class for implementing a buffer to be used by a StoreImpl for storage of publish messag...
Definition: Buffer.hpp:41
virtual void zero(size_t offset_, size_t length_)=0
Set the given number of bytes in the buffer to zero starting at the given position.
virtual size_t getSize() const =0
Get the current size of the Buffer in bytes.
virtual void setSize(size_t newSize_)=0
Set the size for the buffer.