* While reading in a block, the block is now marked busy, and the cache
unlocked, allowing for more parallel access. * Writing is still done synchronously, though. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34611 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+53
-5
@@ -109,6 +109,9 @@ struct block_cache : DoublyLinkedListLinkImpl<block_cache> {
|
|||||||
object_cache* buffer_cache;
|
object_cache* buffer_cache;
|
||||||
block_list unused_blocks;
|
block_list unused_blocks;
|
||||||
|
|
||||||
|
ConditionVariable busy_condition;
|
||||||
|
uint32 busy_count;
|
||||||
|
|
||||||
uint32 num_dirty_blocks;
|
uint32 num_dirty_blocks;
|
||||||
bool read_only;
|
bool read_only;
|
||||||
|
|
||||||
@@ -929,6 +932,7 @@ block_cache::block_cache(int _fd, off_t numBlocks, size_t blockSize,
|
|||||||
last_transaction(NULL),
|
last_transaction(NULL),
|
||||||
transaction_hash(NULL),
|
transaction_hash(NULL),
|
||||||
buffer_cache(NULL),
|
buffer_cache(NULL),
|
||||||
|
busy_count(0),
|
||||||
num_dirty_blocks(0),
|
num_dirty_blocks(0),
|
||||||
read_only(readOnly)
|
read_only(readOnly)
|
||||||
{
|
{
|
||||||
@@ -952,6 +956,7 @@ block_cache::~block_cache()
|
|||||||
status_t
|
status_t
|
||||||
block_cache::Init()
|
block_cache::Init()
|
||||||
{
|
{
|
||||||
|
busy_condition.Init(this, "cache block busy");
|
||||||
condition_variable.Init(this, "cache transaction sync");
|
condition_variable.Init(this, "cache transaction sync");
|
||||||
mutex_init(&lock, "block cache");
|
mutex_init(&lock, "block cache");
|
||||||
|
|
||||||
@@ -1058,6 +1063,8 @@ block_cache::NewBlock(off_t blockNumber)
|
|||||||
block->transaction = block->previous_transaction = NULL;
|
block->transaction = block->previous_transaction = NULL;
|
||||||
block->original_data = NULL;
|
block->original_data = NULL;
|
||||||
block->parent_data = NULL;
|
block->parent_data = NULL;
|
||||||
|
block->busy = false;
|
||||||
|
block->is_writing = false;
|
||||||
block->is_dirty = false;
|
block->is_dirty = false;
|
||||||
block->unused = false;
|
block->unused = false;
|
||||||
block->discard = false;
|
block->discard = false;
|
||||||
@@ -1287,40 +1294,61 @@ put_cached_block(block_cache* cache, off_t blockNumber)
|
|||||||
|
|
||||||
/*! Retrieves the block \a blockNumber from the hash table, if it's already
|
/*! Retrieves the block \a blockNumber from the hash table, if it's already
|
||||||
there, or reads it from the disk.
|
there, or reads it from the disk.
|
||||||
|
You need to have the cache locked when calling this function.
|
||||||
|
|
||||||
\param _allocated tells you wether or not a new block has been allocated
|
\param _allocated tells you wether or not a new block has been allocated
|
||||||
to satisfy your request.
|
to satisfy your request.
|
||||||
\param readBlock if \c false, the block will not be read in case it was
|
\param readBlock if \c false, the block will not be read in case it was
|
||||||
not already in the cache. The block you retrieve may contain random
|
not already in the cache. The block you retrieve may contain random
|
||||||
data.
|
data. If \c true, the cache will be temporarily unlocked while the
|
||||||
|
block is read in.
|
||||||
*/
|
*/
|
||||||
static cached_block*
|
static cached_block*
|
||||||
get_cached_block(block_cache* cache, off_t blockNumber, bool* _allocated,
|
get_cached_block(block_cache* cache, off_t blockNumber, bool* _allocated,
|
||||||
bool readBlock = true)
|
bool readBlock = true)
|
||||||
{
|
{
|
||||||
|
ASSERT_LOCKED_MUTEX(&cache->lock);
|
||||||
|
|
||||||
if (blockNumber < 0 || blockNumber >= cache->max_blocks) {
|
if (blockNumber < 0 || blockNumber >= cache->max_blocks) {
|
||||||
panic("get_cached_block: invalid block number %lld (max %lld)",
|
panic("get_cached_block: invalid block number %lld (max %lld)",
|
||||||
blockNumber, cache->max_blocks - 1);
|
blockNumber, cache->max_blocks - 1);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retry:
|
||||||
cached_block* block = (cached_block*)hash_lookup(cache->hash,
|
cached_block* block = (cached_block*)hash_lookup(cache->hash,
|
||||||
&blockNumber);
|
&blockNumber);
|
||||||
*_allocated = false;
|
*_allocated = false;
|
||||||
|
|
||||||
if (block == NULL) {
|
if (block == NULL) {
|
||||||
// read block into cache
|
// put block into cache
|
||||||
block = cache->NewBlock(blockNumber);
|
block = cache->NewBlock(blockNumber);
|
||||||
if (block == NULL)
|
if (block == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
hash_insert_grow(cache->hash, block);
|
hash_insert_grow(cache->hash, block);
|
||||||
*_allocated = true;
|
*_allocated = true;
|
||||||
|
} else if (block->busy) {
|
||||||
|
// The block is currently busy - wait and try again later
|
||||||
|
ConditionVariableEntry entry;
|
||||||
|
cache->busy_condition.Add(&entry);
|
||||||
|
|
||||||
|
mutex_unlock(&cache->lock);
|
||||||
|
|
||||||
|
entry.Wait();
|
||||||
|
|
||||||
|
mutex_lock(&cache->lock);
|
||||||
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*_allocated && readBlock) {
|
if (*_allocated && readBlock) {
|
||||||
|
// read block into cache
|
||||||
int32 blockSize = cache->block_size;
|
int32 blockSize = cache->block_size;
|
||||||
|
|
||||||
|
cache->busy_count++;
|
||||||
|
block->busy = true;
|
||||||
|
mutex_unlock(&cache->lock);
|
||||||
|
|
||||||
ssize_t bytesRead = read_pos(cache->fd, blockNumber * blockSize,
|
ssize_t bytesRead = read_pos(cache->fd, blockNumber * blockSize,
|
||||||
block->current_data, blockSize);
|
block->current_data, blockSize);
|
||||||
if (bytesRead < blockSize) {
|
if (bytesRead < blockSize) {
|
||||||
@@ -1332,6 +1360,12 @@ get_cached_block(block_cache* cache, off_t blockNumber, bool* _allocated,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
TB(Read(cache, block));
|
TB(Read(cache, block));
|
||||||
|
|
||||||
|
mutex_lock(&cache->lock);
|
||||||
|
block->busy = false;
|
||||||
|
cache->busy_count--;
|
||||||
|
|
||||||
|
cache->busy_condition.NotifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block->unused) {
|
if (block->unused) {
|
||||||
@@ -1602,6 +1636,8 @@ dump_cache(int argc, char** argv)
|
|||||||
kprintf(" ref_count: %ld\n", block->ref_count);
|
kprintf(" ref_count: %ld\n", block->ref_count);
|
||||||
kprintf(" accessed: %ld\n", block->accessed);
|
kprintf(" accessed: %ld\n", block->accessed);
|
||||||
kprintf(" flags: ");
|
kprintf(" flags: ");
|
||||||
|
if (block->busy)
|
||||||
|
kprintf(" busy");
|
||||||
if (block->is_writing)
|
if (block->is_writing)
|
||||||
kprintf(" is-writing");
|
kprintf(" is-writing");
|
||||||
if (block->is_dirty)
|
if (block->is_dirty)
|
||||||
@@ -1698,8 +1734,8 @@ dump_cache(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
kprintf(" %ld blocks total, %ld dirty, %ld discarded, %ld referenced, %ld "
|
kprintf(" %ld blocks total, %ld dirty, %ld discarded, %ld referenced, %ld "
|
||||||
"in unused.\n", count, dirty, discarded, referenced,
|
"busy, %ld in unused.\n", count, dirty, discarded, referenced,
|
||||||
cache->unused_blocks.Count());
|
cache->busy_count, cache->unused_blocks.Count());
|
||||||
|
|
||||||
hash_close(cache->hash, &iterator, false);
|
hash_close(cache->hash, &iterator, false);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1980,7 +2016,7 @@ block_notifier_and_writer(void* /*data*/)
|
|||||||
while (count < kMaxCount
|
while (count < kMaxCount
|
||||||
&& (block = (cached_block*)hash_next(cache->hash,
|
&& (block = (cached_block*)hash_next(cache->hash,
|
||||||
&iterator)) != NULL) {
|
&iterator)) != NULL) {
|
||||||
if (block->is_dirty)
|
if (block->is_dirty && !block->is_writing)
|
||||||
blocks[count++] = block;
|
blocks[count++] = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2740,6 +2776,18 @@ block_cache_delete(void* _cache, bool allowWrites)
|
|||||||
|
|
||||||
mutex_lock(&cache->lock);
|
mutex_lock(&cache->lock);
|
||||||
|
|
||||||
|
while (cache->busy_count != 0) {
|
||||||
|
// wait for all blocks to be read in/written back
|
||||||
|
ConditionVariableEntry entry;
|
||||||
|
cache->busy_condition.Add(&entry);
|
||||||
|
|
||||||
|
mutex_unlock(&cache->lock);
|
||||||
|
|
||||||
|
entry.Wait();
|
||||||
|
|
||||||
|
mutex_lock(&cache->lock);
|
||||||
|
}
|
||||||
|
|
||||||
// free all blocks
|
// free all blocks
|
||||||
|
|
||||||
uint32 cookie = 0;
|
uint32 cookie = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user