2006-05-31 10:53:00 +02:00
|
|
|
/*
|
|
|
|
* The contents of this file are subject to the Initial
|
|
|
|
* Developer's Public License Version 1.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the
|
|
|
|
* License. You may obtain a copy of the License at
|
|
|
|
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed AS IS,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing rights
|
|
|
|
* and limitations under the License.
|
|
|
|
*
|
|
|
|
* The Original Code was created by Dmitry Yemanov
|
|
|
|
* for the Firebird Open Source RDBMS project.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2006 Dmitry Yemanov <dimitr@users.sf.net>
|
|
|
|
* and all contributors signed below.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
* Contributor(s): ______________________________________.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "firebird.h"
|
|
|
|
|
2009-08-21 10:36:37 +02:00
|
|
|
#include "iberror.h"
|
2018-01-21 09:52:14 +01:00
|
|
|
#include "../common/classes/TempFile.h"
|
2006-05-31 10:53:00 +02:00
|
|
|
#include "../common/config/config.h"
|
|
|
|
#include "../common/config/dir_list.h"
|
2010-10-13 12:39:52 +02:00
|
|
|
#include "../common/gdsassert.h"
|
2010-10-12 10:02:57 +02:00
|
|
|
#include "../common/isc_proto.h"
|
|
|
|
#include "../common/os/path_utils.h"
|
2018-01-21 09:52:14 +01:00
|
|
|
#include "../jrd/jrd.h"
|
2006-05-31 10:53:00 +02:00
|
|
|
|
|
|
|
#include "../jrd/TempSpace.h"
|
|
|
|
|
2018-01-21 09:52:14 +01:00
|
|
|
using namespace Firebird;
|
|
|
|
using namespace Jrd;
|
2009-06-06 20:39:29 +02:00
|
|
|
|
2006-05-31 10:53:00 +02:00
|
|
|
// Static definitions/initializations
|
|
|
|
|
2018-01-21 09:52:14 +01:00
|
|
|
GlobalPtr<Mutex> TempSpace::initMutex;
|
|
|
|
TempDirectoryList* TempSpace::tempDirs = NULL;
|
2014-07-17 20:48:46 +02:00
|
|
|
FB_SIZE_T TempSpace::minBlockSize = 0;
|
2018-01-21 09:52:14 +01:00
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
const size_t MIN_TEMP_BLOCK_SIZE = 64 * 1024;
|
|
|
|
|
|
|
|
class TempCacheLimitGuard
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit TempCacheLimitGuard(FB_SIZE_T size)
|
|
|
|
: m_dbb(GET_DBB()), m_size(size),
|
|
|
|
m_guard(m_dbb->dbb_temp_cache_mutex, FB_FUNCTION)
|
|
|
|
{
|
|
|
|
m_allowed = (m_dbb->dbb_temp_cache_size + size <= m_dbb->dbb_config->getTempCacheLimit());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isAllowed() const
|
|
|
|
{
|
|
|
|
return m_allowed;
|
|
|
|
}
|
|
|
|
|
|
|
|
void increment()
|
|
|
|
{
|
|
|
|
fb_assert(m_allowed);
|
|
|
|
m_dbb->dbb_temp_cache_size += m_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void decrement(FB_SIZE_T size)
|
|
|
|
{
|
|
|
|
Database* const dbb = GET_DBB();
|
|
|
|
MutexLockGuard guard(dbb->dbb_temp_cache_mutex, FB_FUNCTION);
|
|
|
|
dbb->dbb_temp_cache_size -= size;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Database* const m_dbb;
|
|
|
|
FB_SIZE_T m_size;
|
|
|
|
MutexLockGuard m_guard;
|
|
|
|
bool m_allowed;
|
|
|
|
};
|
|
|
|
}
|
2006-05-31 10:53:00 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// In-memory block class
|
|
|
|
//
|
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
FB_SIZE_T TempSpace::MemoryBlock::read(offset_t offset, void* buffer, FB_SIZE_T length)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
|
|
|
if (offset + length > size)
|
|
|
|
{
|
|
|
|
length = size - offset;
|
|
|
|
}
|
2006-06-29 11:06:32 +02:00
|
|
|
memcpy(buffer, ptr + offset, length);
|
2006-05-31 10:53:00 +02:00
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
FB_SIZE_T TempSpace::MemoryBlock::write(offset_t offset, const void* buffer, FB_SIZE_T length)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
|
|
|
if (offset + length > size)
|
|
|
|
{
|
|
|
|
length = size - offset;
|
|
|
|
}
|
2006-06-29 11:06:32 +02:00
|
|
|
memcpy(ptr + offset, buffer, length);
|
2006-05-31 10:53:00 +02:00
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// On-disk block class
|
|
|
|
//
|
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
FB_SIZE_T TempSpace::FileBlock::read(offset_t offset, void* buffer, FB_SIZE_T length)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
|
|
|
if (offset + length > size)
|
|
|
|
{
|
|
|
|
length = size - offset;
|
|
|
|
}
|
|
|
|
offset += seek;
|
|
|
|
return file->read(offset, buffer, length);
|
|
|
|
}
|
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
FB_SIZE_T TempSpace::FileBlock::write(offset_t offset, const void* buffer, FB_SIZE_T length)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
|
|
|
if (offset + length > size)
|
|
|
|
{
|
|
|
|
length = size - offset;
|
|
|
|
}
|
|
|
|
offset += seek;
|
|
|
|
return file->write(offset, buffer, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::TempSpace
|
|
|
|
//
|
|
|
|
// Constructor
|
|
|
|
//
|
|
|
|
|
2018-01-21 09:52:14 +01:00
|
|
|
TempSpace::TempSpace(MemoryPool& p, const PathName& prefix, bool dynamic)
|
2012-03-14 15:00:38 +01:00
|
|
|
: pool(p), filePrefix(p, prefix),
|
|
|
|
logicalSize(0), physicalSize(0), localCacheUsage(0),
|
|
|
|
head(NULL), tail(NULL), tempFiles(p),
|
|
|
|
initialBuffer(p), initiallyDynamic(dynamic),
|
|
|
|
freeSegments(p)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
|
|
|
if (!tempDirs)
|
|
|
|
{
|
2018-01-21 09:52:14 +01:00
|
|
|
MutexLockGuard guard(initMutex, FB_FUNCTION);
|
2006-05-31 10:53:00 +02:00
|
|
|
if (!tempDirs)
|
|
|
|
{
|
2006-06-29 11:06:32 +02:00
|
|
|
MemoryPool& def_pool = *getDefaultMemoryPool();
|
2018-01-21 09:52:14 +01:00
|
|
|
tempDirs = FB_NEW_POOL(def_pool) TempDirectoryList(def_pool);
|
2010-03-03 16:02:01 +01:00
|
|
|
minBlockSize = Config::getTempBlockSize();
|
2010-01-19 09:25:42 +01:00
|
|
|
|
|
|
|
if (minBlockSize < MIN_TEMP_BLOCK_SIZE)
|
|
|
|
minBlockSize = MIN_TEMP_BLOCK_SIZE;
|
|
|
|
else
|
2014-07-28 15:01:10 +02:00
|
|
|
minBlockSize = FB_ALIGN(minBlockSize, MIN_TEMP_BLOCK_SIZE);
|
2006-05-31 10:53:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::~TempSpace
|
|
|
|
//
|
|
|
|
// Destructor
|
|
|
|
//
|
|
|
|
|
|
|
|
TempSpace::~TempSpace()
|
|
|
|
{
|
|
|
|
while (head)
|
|
|
|
{
|
|
|
|
Block* temp = head->next;
|
|
|
|
delete head;
|
|
|
|
head = temp;
|
|
|
|
}
|
|
|
|
|
2018-01-21 09:52:14 +01:00
|
|
|
TempCacheLimitGuard::decrement(localCacheUsage);
|
2006-05-31 10:53:00 +02:00
|
|
|
|
|
|
|
while (tempFiles.getCount())
|
|
|
|
delete tempFiles.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::read
|
|
|
|
//
|
|
|
|
// Reads bytes from the temporary space
|
|
|
|
//
|
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
FB_SIZE_T TempSpace::read(offset_t offset, void* buffer, FB_SIZE_T length)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
|
|
|
fb_assert(offset + length <= logicalSize);
|
|
|
|
|
|
|
|
if (length)
|
|
|
|
{
|
|
|
|
// search for the first needed block
|
|
|
|
Block* block = findBlock(offset);
|
|
|
|
|
2010-03-19 11:54:53 +01:00
|
|
|
UCHAR* p = static_cast<UCHAR*>(buffer);
|
2014-07-17 20:48:46 +02:00
|
|
|
FB_SIZE_T l = length;
|
2006-05-31 10:53:00 +02:00
|
|
|
|
|
|
|
// read data from the block chain
|
2008-04-19 11:42:01 +02:00
|
|
|
for (Block* itr = block; itr && l; itr = itr->next, offset = 0)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
2014-07-17 20:48:46 +02:00
|
|
|
const FB_SIZE_T n = itr->read(offset, p, l);
|
2006-05-31 10:53:00 +02:00
|
|
|
p += n;
|
|
|
|
l -= n;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(!l);
|
|
|
|
}
|
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::write
|
|
|
|
//
|
|
|
|
// Writes bytes to the temporary space
|
|
|
|
//
|
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
FB_SIZE_T TempSpace::write(offset_t offset, const void* buffer, FB_SIZE_T length)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
|
|
|
fb_assert(offset <= logicalSize);
|
|
|
|
|
|
|
|
if (offset + length > logicalSize)
|
|
|
|
{
|
|
|
|
// not enough space, allocate one more block
|
|
|
|
extend(offset + length - logicalSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length)
|
|
|
|
{
|
|
|
|
// search for the first needed block
|
2008-04-19 11:42:01 +02:00
|
|
|
Block* const block = findBlock(offset);
|
2006-05-31 10:53:00 +02:00
|
|
|
|
2010-03-19 11:54:53 +01:00
|
|
|
const UCHAR* p = static_cast<const UCHAR*>(buffer);
|
2014-07-17 20:48:46 +02:00
|
|
|
FB_SIZE_T l = length;
|
2006-05-31 10:53:00 +02:00
|
|
|
|
|
|
|
// write data to as many blocks as necessary
|
2008-04-19 11:42:01 +02:00
|
|
|
for (Block* itr = block; itr && l; itr = itr->next, offset = 0)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
2014-07-17 20:48:46 +02:00
|
|
|
const FB_SIZE_T n = itr->write(offset, p, l);
|
2006-05-31 10:53:00 +02:00
|
|
|
p += n;
|
|
|
|
l -= n;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(!l);
|
|
|
|
}
|
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::extend
|
|
|
|
//
|
|
|
|
// Increases size of the temporary space
|
|
|
|
//
|
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
void TempSpace::extend(FB_SIZE_T size)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
|
|
|
logicalSize += size;
|
|
|
|
|
|
|
|
if (logicalSize > physicalSize)
|
|
|
|
{
|
2014-07-17 20:48:46 +02:00
|
|
|
const FB_SIZE_T initialSize = initialBuffer.getCount();
|
2012-03-14 15:00:38 +01:00
|
|
|
|
|
|
|
// If the dynamic mode is specified, then we allocate new blocks
|
|
|
|
// by growing the same initial memory block in the specified chunks.
|
|
|
|
// Once the limit (64KB) is reached, we switch to the generic algorithm
|
|
|
|
// (1MB blocks), copy the existing data there and free the initial buffer.
|
|
|
|
//
|
|
|
|
// This mode should not be used if the caller never works with small blocks.
|
|
|
|
// Also, it MUST NOT be used if the caller deals with inMemory() or allocateBatch()
|
|
|
|
// routines and caches the pointers to use them later. These pointers may become
|
|
|
|
// invalid after resizing the initial block or after switching to large blocks.
|
|
|
|
|
|
|
|
if (initiallyDynamic && logicalSize < MIN_TEMP_BLOCK_SIZE)
|
|
|
|
{
|
|
|
|
// allocate or extend the initial dynamic block, it will grow up to 64KB
|
|
|
|
if (!initialSize)
|
|
|
|
{
|
|
|
|
fb_assert(!head && !tail);
|
2015-10-12 16:26:00 +02:00
|
|
|
head = tail = FB_NEW_POOL(pool) InitialBlock(initialBuffer.getBuffer(size), size);
|
2012-03-14 15:00:38 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fb_assert(head == tail);
|
|
|
|
size += initialSize;
|
|
|
|
initialBuffer.resize(size);
|
2015-10-12 16:26:00 +02:00
|
|
|
new(head) InitialBlock(initialBuffer.begin(), size);
|
2012-03-14 15:00:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
physicalSize = size;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (initialSize)
|
|
|
|
{
|
|
|
|
fb_assert(head == tail);
|
|
|
|
delete head;
|
|
|
|
head = tail = NULL;
|
2014-07-31 23:15:33 +02:00
|
|
|
size = static_cast<FB_SIZE_T>(FB_ALIGN(logicalSize, minBlockSize));
|
2012-03-14 15:00:38 +01:00
|
|
|
physicalSize = size;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-07-31 23:15:33 +02:00
|
|
|
size = static_cast<FB_SIZE_T>(FB_ALIGN(logicalSize - physicalSize, minBlockSize));
|
2012-03-14 15:00:38 +01:00
|
|
|
physicalSize += size;
|
|
|
|
}
|
2006-05-31 10:53:00 +02:00
|
|
|
|
|
|
|
Block* block = NULL;
|
|
|
|
|
2018-01-21 09:52:14 +01:00
|
|
|
{ // scope
|
|
|
|
TempCacheLimitGuard guard(size);
|
|
|
|
|
|
|
|
if (guard.isAllowed())
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
2018-01-21 09:52:14 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
// allocate block in virtual memory
|
|
|
|
block = FB_NEW_POOL(pool) MemoryBlock(FB_NEW_POOL(pool) UCHAR[size], tail, size);
|
|
|
|
localCacheUsage += size;
|
|
|
|
guard.increment();
|
|
|
|
}
|
|
|
|
catch (const BadAlloc&)
|
|
|
|
{
|
|
|
|
// not enough memory
|
|
|
|
}
|
2006-05-31 10:53:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-31 23:15:33 +02:00
|
|
|
// NS 2014-07-31: FIXME: missing exception handling.
|
|
|
|
// error thrown in block of code below will leave TempSpace in inconsistent state:
|
|
|
|
// logical/physical size already increased while allocation has in fact failed.
|
2006-05-31 10:53:00 +02:00
|
|
|
if (!block)
|
|
|
|
{
|
|
|
|
// allocate block in the temp file
|
2009-08-21 10:36:37 +02:00
|
|
|
TempFile* const file = setupFile(size);
|
|
|
|
fb_assert(file);
|
2007-04-03 14:57:32 +02:00
|
|
|
if (tail && tail->sameFile(file))
|
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
fb_assert(!initialSize);
|
2007-04-03 14:57:32 +02:00
|
|
|
tail->size += size;
|
|
|
|
return;
|
|
|
|
}
|
2015-10-12 16:26:00 +02:00
|
|
|
block = FB_NEW_POOL(pool) FileBlock(file, tail, size);
|
2006-05-31 10:53:00 +02:00
|
|
|
}
|
|
|
|
|
2012-03-14 15:00:38 +01:00
|
|
|
// preserve the initial contents, if any
|
|
|
|
if (initialSize)
|
|
|
|
{
|
|
|
|
block->write(0, initialBuffer.begin(), initialSize);
|
|
|
|
initialBuffer.free();
|
|
|
|
}
|
|
|
|
|
2006-05-31 10:53:00 +02:00
|
|
|
// append new block to the chain
|
|
|
|
if (!head)
|
|
|
|
{
|
|
|
|
head = block;
|
|
|
|
}
|
|
|
|
tail = block;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::findBlock
|
|
|
|
//
|
|
|
|
// Locates the space block corresponding to the given global offset
|
|
|
|
//
|
|
|
|
|
2007-04-03 14:57:32 +02:00
|
|
|
TempSpace::Block* TempSpace::findBlock(offset_t& offset) const
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
|
|
|
fb_assert(offset <= logicalSize);
|
|
|
|
|
|
|
|
Block* block = NULL;
|
|
|
|
|
|
|
|
if (offset < physicalSize / 2)
|
|
|
|
{
|
|
|
|
// walk forward
|
|
|
|
block = head;
|
|
|
|
while (block && offset >= block->size)
|
|
|
|
{
|
|
|
|
offset -= block->size;
|
|
|
|
block = block->next;
|
|
|
|
}
|
|
|
|
fb_assert(block);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// walk backward
|
|
|
|
block = tail;
|
|
|
|
while (block && physicalSize - offset > block->size)
|
|
|
|
{
|
|
|
|
offset += block->size;
|
|
|
|
block = block->prev;
|
|
|
|
}
|
|
|
|
fb_assert(block);
|
|
|
|
offset -= physicalSize - block->size;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(offset <= block->size);
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::setupFile
|
|
|
|
//
|
|
|
|
// Allocates the required space in some temporary file
|
|
|
|
//
|
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
TempFile* TempSpace::setupFile(FB_SIZE_T size)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
2018-01-21 09:52:14 +01:00
|
|
|
StaticStatusVector status_vector;
|
2006-05-31 10:53:00 +02:00
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
for (FB_SIZE_T i = 0; i < tempDirs->getCount(); i++)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
2009-08-21 10:36:37 +02:00
|
|
|
TempFile* file = NULL;
|
|
|
|
|
2018-01-21 09:52:14 +01:00
|
|
|
PathName directory = (*tempDirs)[i];
|
2006-06-03 03:01:51 +02:00
|
|
|
PathUtils::ensureSeparator(directory);
|
2006-05-31 10:53:00 +02:00
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
for (FB_SIZE_T j = 0; j < tempFiles.getCount(); j++)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
2018-01-21 09:52:14 +01:00
|
|
|
PathName dirname, filename;
|
2008-09-13 13:09:09 +02:00
|
|
|
PathUtils::splitLastComponent(dirname, filename, tempFiles[j]->getName());
|
2006-06-02 10:46:28 +02:00
|
|
|
PathUtils::ensureSeparator(dirname);
|
2006-06-05 16:39:33 +02:00
|
|
|
if (!directory.compare(dirname))
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
2008-02-06 20:27:57 +01:00
|
|
|
file = tempFiles[j];
|
2006-05-31 10:53:00 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2008-02-06 20:27:57 +01:00
|
|
|
if (!file)
|
|
|
|
{
|
2015-10-12 16:26:00 +02:00
|
|
|
file = FB_NEW_POOL(pool) TempFile(pool, filePrefix, directory);
|
2008-02-06 20:27:57 +01:00
|
|
|
tempFiles.add(file);
|
|
|
|
}
|
|
|
|
|
2006-05-31 10:53:00 +02:00
|
|
|
file->extend(size);
|
|
|
|
}
|
2018-01-21 09:52:14 +01:00
|
|
|
catch (const system_error& ex)
|
2006-05-31 10:53:00 +02:00
|
|
|
{
|
2015-03-27 15:36:30 +01:00
|
|
|
ex.stuffException(status_vector);
|
2009-08-21 10:36:37 +02:00
|
|
|
continue;
|
2006-05-31 10:53:00 +02:00
|
|
|
}
|
|
|
|
|
2009-08-21 10:36:37 +02:00
|
|
|
return file;
|
2006-05-31 10:53:00 +02:00
|
|
|
}
|
|
|
|
|
2009-08-21 10:36:37 +02:00
|
|
|
// no room in all directories
|
2018-01-21 09:52:14 +01:00
|
|
|
Arg::Gds status(isc_out_of_temp_space);
|
|
|
|
status.append(Arg::StatusVector(status_vector.begin()));
|
2009-08-21 10:36:37 +02:00
|
|
|
iscLogStatus(NULL, status.value());
|
|
|
|
status.raise();
|
|
|
|
|
|
|
|
return NULL; // compiler silencer
|
2006-05-31 10:53:00 +02:00
|
|
|
}
|
2007-04-03 14:57:32 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::allocateSpace
|
|
|
|
//
|
|
|
|
// Allocate available space in free segments. Extend file if necessary
|
|
|
|
//
|
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
offset_t TempSpace::allocateSpace(FB_SIZE_T size)
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
|
|
|
// Find the best available space. This is defined as the smallest free space
|
|
|
|
// that is big enough. This preserves large blocks.
|
2012-03-14 15:00:38 +01:00
|
|
|
Segment* best = NULL;
|
2007-04-03 14:57:32 +02:00
|
|
|
|
2007-04-05 12:28:11 +02:00
|
|
|
// Search through the available space in the not used segments list
|
2012-03-14 15:00:38 +01:00
|
|
|
for (bool found = freeSegments.getFirst(); found; found = freeSegments.getNext())
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
Segment* const space = &freeSegments.current();
|
2007-04-03 14:57:32 +02:00
|
|
|
// If this is smaller than our previous best, use it
|
2012-03-14 15:00:38 +01:00
|
|
|
if (space->size >= size && (!best || (space->size < best->size))) {
|
|
|
|
best = space;
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we didn't find any space, allocate it at the end of the file
|
2008-12-05 02:20:14 +01:00
|
|
|
if (!best)
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
|
|
|
extend(size);
|
|
|
|
return getSize() - size;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up the return parameters
|
2012-03-14 15:00:38 +01:00
|
|
|
const offset_t position = best->position;
|
|
|
|
best->size -= size;
|
|
|
|
best->position += size;
|
2007-04-03 14:57:32 +02:00
|
|
|
|
2012-03-14 15:00:38 +01:00
|
|
|
// If the hunk was an exact fit, remove the segment from the list
|
|
|
|
if (!best->size)
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
if (!freeSegments.locate(best->position))
|
|
|
|
fb_assert(false);
|
|
|
|
|
|
|
|
freeSegments.fastRemove();
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
|
|
|
|
2012-03-14 15:00:38 +01:00
|
|
|
return position;
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::releaseSpace
|
|
|
|
//
|
2008-12-05 02:20:14 +01:00
|
|
|
// Return previously allocated segment back into not used segments list and
|
2007-04-03 14:57:32 +02:00
|
|
|
// join it with adjacent segments if found
|
|
|
|
//
|
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
void TempSpace::releaseSpace(offset_t position, FB_SIZE_T size)
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
|
|
|
fb_assert(size > 0);
|
|
|
|
fb_assert(position < getSize()); // Block starts in file
|
|
|
|
const offset_t end = position + size;
|
|
|
|
fb_assert(end <= getSize()); // Block ends in file
|
|
|
|
|
2018-01-21 09:52:14 +01:00
|
|
|
if (freeSegments.locate(locEqual, end))
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
// The next segment is found to be adjacent
|
|
|
|
Segment* const next_seg = &freeSegments.current();
|
|
|
|
next_seg->position -= size;
|
|
|
|
next_seg->size += size;
|
2007-04-03 14:57:32 +02:00
|
|
|
|
2012-03-14 15:00:38 +01:00
|
|
|
if (freeSegments.getPrev())
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
// Check the prior segment for being adjacent
|
|
|
|
Segment* const prior_seg = &freeSegments.current();
|
|
|
|
if (position == prior_seg->position + prior_seg->size)
|
|
|
|
{
|
|
|
|
next_seg->position -= prior_seg->size;
|
|
|
|
next_seg->size += prior_seg->size;
|
|
|
|
freeSegments.fastRemove();
|
|
|
|
}
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
|
|
|
|
2012-03-14 15:00:38 +01:00
|
|
|
return;
|
|
|
|
}
|
2012-03-17 03:26:59 +01:00
|
|
|
|
2018-01-21 09:52:14 +01:00
|
|
|
if (freeSegments.locate(locLess, position))
|
2012-03-14 15:00:38 +01:00
|
|
|
{
|
|
|
|
// Check the prior segment for being adjacent
|
|
|
|
Segment* const prior_seg = &freeSegments.current();
|
|
|
|
if (position == prior_seg->position + prior_seg->size)
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
prior_seg->size += size;
|
2007-04-03 14:57:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2012-03-14 15:00:38 +01:00
|
|
|
|
|
|
|
freeSegments.add(Segment(position, size));
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::inMemory
|
|
|
|
//
|
|
|
|
// Return contiguous chunk of memory if present at given location
|
|
|
|
//
|
|
|
|
|
2010-03-19 11:54:53 +01:00
|
|
|
UCHAR* TempSpace::inMemory(offset_t begin, size_t size) const
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
2007-04-05 11:13:10 +02:00
|
|
|
const Block* block = findBlock(begin);
|
2008-01-16 11:25:04 +01:00
|
|
|
return block ? block->inMemory(begin, size) : NULL;
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::findMemory
|
|
|
|
//
|
2008-12-05 02:20:14 +01:00
|
|
|
// Return contiguous chunk of memory and adjust starting offset
|
2007-04-03 14:57:32 +02:00
|
|
|
// of search range if found
|
|
|
|
//
|
|
|
|
|
2010-03-19 11:54:53 +01:00
|
|
|
UCHAR* TempSpace::findMemory(offset_t& begin, offset_t end, size_t size) const
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
|
|
|
offset_t local_offset = begin;
|
|
|
|
const offset_t save_begin = begin;
|
2007-04-05 11:13:10 +02:00
|
|
|
const Block* block = findBlock(local_offset);
|
2008-01-16 11:25:04 +01:00
|
|
|
|
2007-04-03 14:57:32 +02:00
|
|
|
while (block && (begin + size <= end))
|
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
UCHAR* const mem = block->inMemory(local_offset, size);
|
2008-12-05 02:20:14 +01:00
|
|
|
if (mem)
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
|
|
|
return mem;
|
|
|
|
}
|
2008-01-16 11:25:04 +01:00
|
|
|
|
|
|
|
begin += block->size - local_offset;
|
|
|
|
local_offset = 0;
|
|
|
|
block = block->next;
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
2008-01-16 11:25:04 +01:00
|
|
|
|
2007-04-03 14:57:32 +02:00
|
|
|
begin = save_begin;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::validate
|
|
|
|
//
|
|
|
|
// Validate internal lists for consistency and return back to caller
|
|
|
|
// amount of available free space
|
|
|
|
//
|
|
|
|
|
|
|
|
bool TempSpace::validate(offset_t& free) const
|
|
|
|
{
|
|
|
|
free = 0;
|
2012-03-14 15:00:38 +01:00
|
|
|
FreeSegmentTree::ConstAccessor accessor(&freeSegments);
|
|
|
|
for (bool found = accessor.getFirst(); found; found = accessor.getNext())
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
const offset_t size = accessor.current().size;
|
|
|
|
fb_assert(size != 0);
|
|
|
|
free += size;
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
offset_t disk = 0;
|
2014-07-17 20:48:46 +02:00
|
|
|
for (FB_SIZE_T i = 0; i < tempFiles.getCount(); i++)
|
2007-04-03 14:57:32 +02:00
|
|
|
disk += tempFiles[i]->getSize();
|
|
|
|
|
2012-03-14 15:00:38 +01:00
|
|
|
return ((initialBuffer.getCount() + localCacheUsage + disk) == physicalSize);
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// TempSpace::allocateBatch
|
|
|
|
//
|
2008-12-05 02:20:14 +01:00
|
|
|
// Allocate up to 'count' contiguous chunks of memory available in free
|
2007-04-03 14:57:32 +02:00
|
|
|
// segments if any. Adjust size of chunks between minSize and maxSize
|
2008-12-05 02:20:14 +01:00
|
|
|
// accordingly to available free space (assuming all of the free space
|
2007-04-03 14:57:32 +02:00
|
|
|
// is in memory blocks). Algorithm is very simple and can be improved in future
|
2008-12-05 02:20:14 +01:00
|
|
|
//
|
2007-04-03 14:57:32 +02:00
|
|
|
|
2014-07-17 20:48:46 +02:00
|
|
|
ULONG TempSpace::allocateBatch(ULONG count, FB_SIZE_T minSize, FB_SIZE_T maxSize, Segments& segments)
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
|
|
|
// adjust passed chunk size to amount of free memory we have and number
|
2008-12-05 02:20:14 +01:00
|
|
|
// of runs still not allocated.
|
2007-04-03 14:57:32 +02:00
|
|
|
offset_t freeMem = 0;
|
2012-03-14 15:00:38 +01:00
|
|
|
|
|
|
|
for (bool found = freeSegments.getFirst(); found; found = freeSegments.getNext())
|
|
|
|
freeMem += freeSegments.current().size;
|
2007-04-03 14:57:32 +02:00
|
|
|
|
|
|
|
freeMem = MIN(freeMem / count, maxSize);
|
|
|
|
freeMem = MAX(freeMem, minSize);
|
|
|
|
freeMem = MIN(freeMem, minBlockSize);
|
2010-01-19 09:25:42 +01:00
|
|
|
freeMem &= ~(FB_ALIGNMENT - 1);
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2012-03-14 15:00:38 +01:00
|
|
|
bool is_positioned = freeSegments.getFirst();
|
|
|
|
while (segments.getCount() < count && is_positioned)
|
2007-04-03 14:57:32 +02:00
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
Segment* freeSpace = &freeSegments.current();
|
|
|
|
offset_t freeSeek = freeSpace->position;
|
|
|
|
const offset_t freeEnd = freeSpace->position + freeSpace->size;
|
|
|
|
|
|
|
|
UCHAR* const mem = findMemory(freeSeek, freeEnd, freeMem);
|
2007-04-03 14:57:32 +02:00
|
|
|
|
|
|
|
if (mem)
|
|
|
|
{
|
|
|
|
fb_assert(freeSeek + freeMem <= freeEnd);
|
|
|
|
#ifdef DEV_BUILD
|
|
|
|
offset_t seek1 = freeSeek;
|
2012-03-14 15:00:38 +01:00
|
|
|
UCHAR* const p = findMemory(seek1, freeEnd, freeMem);
|
2007-04-03 14:57:32 +02:00
|
|
|
fb_assert(p == mem);
|
|
|
|
fb_assert(seek1 == freeSeek);
|
|
|
|
#endif
|
|
|
|
if (freeSeek != freeSpace->position)
|
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
const offset_t skip_size = freeSeek - freeSpace->position;
|
|
|
|
const Segment skip_space(freeSpace->position, skip_size);
|
2007-04-03 14:57:32 +02:00
|
|
|
|
|
|
|
freeSpace->position += skip_size;
|
|
|
|
freeSpace->size -= skip_size;
|
2012-03-14 15:00:38 +01:00
|
|
|
fb_assert(freeSpace->size != 0);
|
|
|
|
|
|
|
|
if (!freeSegments.add(skip_space))
|
|
|
|
fb_assert(false);
|
|
|
|
|
|
|
|
if (!freeSegments.locate(skip_space.position + skip_size))
|
|
|
|
fb_assert(false);
|
|
|
|
|
|
|
|
freeSpace = &freeSegments.current();
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SegmentInMemory seg;
|
|
|
|
seg.memory = mem;
|
|
|
|
seg.position = freeSeek;
|
|
|
|
seg.size = freeMem;
|
|
|
|
segments.add(seg);
|
|
|
|
|
|
|
|
freeSpace->position += freeMem;
|
|
|
|
freeSpace->size -= freeMem;
|
2012-03-14 15:00:38 +01:00
|
|
|
|
2007-04-03 14:57:32 +02:00
|
|
|
if (!freeSpace->size)
|
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
is_positioned = freeSegments.fastRemove();
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-03-14 15:00:38 +01:00
|
|
|
is_positioned = freeSegments.getNext();
|
2007-04-03 14:57:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return segments.getCount();
|
|
|
|
}
|