* 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:
Axel Dörfler
2009-12-10 10:29:37 +00:00
parent d470839990
commit 33c82a30c0
+53 -5
View File
@@ -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;