26 #ifndef _BLOCKSTORE_H_
27 #define _BLOCKSTORE_H_
34 #include <amps/ampscrc.hpp>
35 #if __cplusplus >= 201103L || _MSC_VER >= 1900
41 #include <sys/timeb.h>
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
80 amps_uint64_t _sequence;
87 Block(
size_t offset_) : _offset(offset_), _sequence(0)
88 , _nextInChain(0), _nextInList(0)
93 Block() : _offset(0), _sequence(0)
94 , _nextInChain(0), _nextInList((
Block*)(
this + 1))
98 Block* init(
size_t index_, amps_uint32_t blockSize_)
100 _offset = index_ * blockSize_;
105 Block* setOffset(
size_t offset_)
115 typedef Lock<Mutex> BufferLock;
116 typedef Unlock<Mutex> BufferUnlock;
117 typedef bool (*ResizeHandler)(size_t,
void*);
118 typedef std::vector<Block*> BlockList;
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)
146 if (_blocksPerRealloc == 0)
148 _blocksPerRealloc = DEFAULT_BLOCKS_PER_REALLOC;
150 else if (_blocksPerRealloc > MAX_BLOCKS_PER_REALLOC)
152 _blocksPerRealloc = MAX_BLOCKS_PER_REALLOC;
154 if (_blockHeaderSize < DEFAULT_BLOCK_HEADER_SIZE
155 || _blockHeaderSize > (std::numeric_limits<amps_uint32_t>::max)() / 2)
157 _blockHeaderSize = DEFAULT_BLOCK_HEADER_SIZE;
159 if (_blockSize == 0 || _blockSize < _blockHeaderSize * 2)
161 _blockSize = DEFAULT_BLOCK_SIZE;
162 if (_blockSize < _blockHeaderSize * 2)
164 _blockHeaderSize = DEFAULT_BLOCK_HEADER_SIZE;
167 if (_blocksPerRealloc > (std::numeric_limits<size_t>::max)() / _blockSize)
169 _blocksPerRealloc = DEFAULT_BLOCKS_PER_REALLOC;
170 _blockSize = DEFAULT_BLOCK_SIZE;
171 _blockHeaderSize = DEFAULT_BLOCK_HEADER_SIZE;
179 for (BlockList::iterator i = _blockList.begin();
180 i != _blockList.end(); ++i)
198 return _blockHeaderSize;
237 return _lock.wait(timeout_);
251 _resizeHandler = resizeHandler_;
252 _resizeUserData = userData_;
271 return _endOfUsedList;
281 _blocksAvailable = freeCount_;
297 _endOfUsedList = block_;
306 _blockList.push_back(blockArray_);
319 while (_blocksAvailable < numBlocksInChain_)
322 unsigned int blocksNeeded = numBlocksInChain_ - _blocksAvailable;
323 amps_uint64_t addedBlocks64 = ((amps_uint64_t)blocksNeeded
324 / _blocksPerRealloc + 1)
326 if (addedBlocks64 > (std::numeric_limits<amps_uint32_t>::max)()
327 || addedBlocks64 > (std::numeric_limits<amps_uint32_t>::max)()
330 throw StoreException(
"BlockStore block count overflow while growing buffer.");
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)
336 throw StoreException(
"BlockStore size overflow while growing buffer.");
338 size_t size = _buffer->
getSize() + resizeSize;
345 for (
unsigned int i = 0; i < numBlocksInChain_; ++i)
349 _freeList = _freeList->_nextInList;
350 next->_nextInList = 0;
360 last->_nextInChain = next;
372 _endOfUsedList->_nextInList = first;
374 _endOfUsedList = first;
375 _blocksAvailable -= numBlocksInChain_;
386 assert(_endOfUsedList);
388 if (_usedList == block_)
391 _usedList = _usedList->_nextInList;
400 Block* used = _usedList;
403 if (used->_nextInList == block_)
405 used->_nextInList = block_->_nextInList;
408 used = used->_nextInList;
416 _flattenToFreeList(block_);
423 AMPS_ATOMIC_BASE_TYPE
put(amps_uint64_t sequence_)
426 assert(_endOfUsedList);
427 Block* used = _usedList;
428 AMPS_ATOMIC_BASE_TYPE removalCount = 0;
429 while (used && used->_sequence <= sequence_)
431 Block* next = used->_nextInList;
433 _flattenToFreeList(used);
452 Block* newEndOfUsedList = 0;
453 for (
Block* used = _usedList; used; used = used->_nextInList)
457 if (newEndOfUsedList)
459 newEndOfUsedList->_nextInList = 0;
465 _endOfUsedList = newEndOfUsedList;
467 newEndOfUsedList = used;
471 for (
Block* block = block_; block; block = next)
473 next = block->_nextInList;
474 _flattenToFreeList(block);
484 size_t startSize = _buffer->
getSize();
489 else if (startSize < blockSize)
496 size_t availableBlocks = startSize / blockSize;
497 if (availableBlocks > (std::numeric_limits<amps_uint32_t>::max)())
499 throw StoreException(
"BlockStore contains more blocks than can be indexed.");
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)
506 _freeList[i].init(i, blockSize);
508 _freeList[numBlocks - 1]._nextInList = 0;
509 _blocksAvailable += numBlocks;
512 assert(_blocksAvailable);
519 return getResizeSize(_blocksPerRealloc);
526 return _blocksPerRealloc;
542 if (_buffer->
getSize() >= size_)
546 if (!_lock.wait(1000))
548 amps_invoke_waiting_function();
551 AtomicFlagFlip flip(&_resizing);
552 bool okToResize =
false;
555 BufferUnlock u(_lock);
557 okToResize = _canResize(size_);
566 size_t oldSize = _buffer->
getSize();
567 if (oldSize >= size_)
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)())
579 throw StoreException(
"BlockStore block count overflow while resizing buffer.");
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)())
587 throw StoreException(
"BlockStore block count overflow after resizing buffer.");
589 _buffer->
zero(oldSize, actualSize - oldSize);
592 *pNewBlocks_ = (amps_uint32_t)addedBlocks;
597 freeList =
new Block[*pNewBlocks_];
598 for (
size_t i = 0; i < *pNewBlocks_; ++i)
600 freeList[i].init(oldBlocks + i, blockSize);
602 freeList[*pNewBlocks_ - 1]._nextInList = 0;
605 catch (
const std::bad_alloc&)
607 catch (
const std::bad_alloc& e)
610 std::ostringstream os;
611 os <<
"BlockStore failed to allocate " << size_
612 <<
" bytes for resize of store from " << _buffer->
getSize()
614 throw StoreException(os.str());
625 amps_uint32_t newBlocks = 0;
627 if (!addedBlockList || !newBlocks)
632 if (newBlocks > (std::numeric_limits<amps_uint32_t>::max)()
635 delete[] addedBlockList;
636 throw StoreException(
"BlockStore block count overflow after resizing buffer.");
638 _blockList.push_back(addedBlockList);
639 addedBlockList[newBlocks - 1]._nextInList = _freeList;
640 _freeList = addedBlockList;
641 _blocksAvailable += newBlocks;
642 assert(_blocksAvailable);
650 if (_usedList || _freeList)
654 amps_uint32_t oldSize = _blockSize;
655 _blockSize = blockSize_;
665 if (_usedList || _freeList)
669 amps_uint32_t oldSize = _blockHeaderSize;
670 _blockHeaderSize = blockHeaderSize_;
683 size_t getResizeSize(amps_uint32_t blocks_)
const
685 if (_blockSize && blocks_ > (std::numeric_limits<size_t>::max)() / _blockSize)
687 throw StoreException(
"BlockStore size overflow calculating buffer growth.");
689 return (
size_t)blocks_ * (size_t)_blockSize;
693 bool _canResize(
size_t requestedSize_)
697 return _resizeHandler(requestedSize_, _resizeUserData);
706 void _flattenToFreeList(Block* block_)
709 Block* current = block_;
712 Block* chain = current->_nextInChain;
714 _buffer->
zero(current->_offset, _blockHeaderSize);
716 current->_nextInList = _freeList;
719 current->_sequence = (amps_uint64_t)0;
720 current->_nextInChain = 0;
724 assert(_blocksAvailable);
734 Block* _endOfUsedList;
736 amps_uint32_t _blocksPerRealloc;
738 amps_uint32_t _blockSize;
739 amps_uint32_t _blockHeaderSize;
741 amps_uint32_t _blocksAvailable;
743 ResizeHandler _resizeHandler;
745 void* _resizeUserData;
747 BlockList _blockList;
749 AMPS_ATOMIC_TYPE_8 _resizing;
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.