Author: bonefish Date: 2010-01-28 14:58:06 +0100 (Thu, 28 Jan 2010) New Revision: 35328 Changeset: http://dev.haiku-os.org/changeset/35328/haiku Modified: haiku/trunk/src/system/kernel/cache/block_cache.cpp Log: * Moved handling of busy blocks into separate functions. * Added flags to avoid notifying the busy condition variable unnecessarily. * get_writable_cached_block(): Unlock the cache while memcpy()ing/memset()ing the block's data. The idea is to reduce lock contention. Less effective than I hoped, though. Modified: haiku/trunk/src/system/kernel/cache/block_cache.cpp =================================================================== --- haiku/trunk/src/system/kernel/cache/block_cache.cpp 2010-01-28 11:25:25 UTC (rev 35327) +++ haiku/trunk/src/system/kernel/cache/block_cache.cpp 2010-01-28 13:58:06 UTC (rev 35328) @@ -74,6 +74,7 @@ bool is_dirty : 1; bool unused : 1; bool discard : 1; + bool busy_waiters : 1; cache_transaction* transaction; cache_transaction* previous_transaction; @@ -111,6 +112,7 @@ ConditionVariable busy_condition; uint32 busy_count; + bool busy_block_waiters; uint32 num_dirty_blocks; bool read_only; @@ -933,6 +935,7 @@ transaction_hash(NULL), buffer_cache(NULL), busy_count(0), + busy_block_waiters(false), num_dirty_blocks(0), read_only(readOnly) { @@ -1075,6 +1078,7 @@ block->is_dirty = false; block->unused = false; block->discard = false; + block->busy_waiters = false; #if BLOCK_CACHE_DEBUG_CHANGED block->compare = NULL; #endif @@ -1216,6 +1220,70 @@ // #pragma mark - private block functions +/*! Cache must be locked. +*/ +static void +mark_block_busy(block_cache* cache, cached_block* block) +{ + block->busy = true; + cache->busy_count++; +} + + +/*! Cache must be locked. +*/ +static void +mark_block_unbusy(block_cache* cache, cached_block* block) +{ + block->busy = false; + cache->busy_count--; + + if (cache->busy_block_waiters || block->busy_waiters) { + cache->busy_block_waiters = false; + block->busy_waiters = false; + cache->busy_condition.NotifyAll(); + } +} + + +/*! Cache must be locked. +*/ +static void +wait_for_busy_block(block_cache* cache, cached_block* block) +{ + // wait for all blocks to be read in/written back + ConditionVariableEntry entry; + cache->busy_condition.Add(&entry); + block->busy_waiters = true; + + mutex_unlock(&cache->lock); + + entry.Wait(); + + mutex_lock(&cache->lock); +} + + +/*! Cache must be locked. +*/ +static void +wait_for_busy_blocks(block_cache* cache) +{ + while (cache->busy_count != 0) { + // wait for all blocks to be read in/written back + ConditionVariableEntry entry; + cache->busy_condition.Add(&entry); + cache->busy_block_waiters = true; + + mutex_unlock(&cache->lock); + + entry.Wait(); + + mutex_lock(&cache->lock); + } +} + + /*! Removes a reference from the specified \a block. If this was the last reference, the block is moved into the unused list. In low memory situations, it will also free some blocks from that list, @@ -1337,14 +1405,7 @@ *_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); + wait_for_busy_block(cache, block); goto retry; } @@ -1352,8 +1413,7 @@ // read block into cache int32 blockSize = cache->block_size; - cache->busy_count++; - block->busy = true; + mark_block_busy(cache, block); mutex_unlock(&cache->lock); ssize_t bytesRead = read_pos(cache->fd, blockNumber * blockSize, @@ -1369,10 +1429,7 @@ TB(Read(cache, block)); mutex_lock(&cache->lock); - block->busy = false; - cache->busy_count--; - - cache->busy_condition.NotifyAll(); + mark_block_unbusy(cache, block); } if (block->unused) { @@ -1417,9 +1474,16 @@ // if there is no transaction support, we just return the current block if (transactionID == -1) { - if (cleared) + if (cleared) { + mark_block_busy(cache, block); + mutex_unlock(&cache->lock); + memset(block->current_data, 0, cache->block_size); + mutex_lock(&cache->lock); + mark_block_unbusy(cache, block); + } + if (!block->is_dirty) { cache->num_dirty_blocks++; block->is_dirty = true; @@ -1477,7 +1541,13 @@ return NULL; } + mark_block_busy(cache, block); + mutex_unlock(&cache->lock); + memcpy(block->original_data, block->current_data, cache->block_size); + + mutex_lock(&cache->lock); + mark_block_unbusy(cache, block); } if (block->parent_data == block->current_data) { // remember any previous contents for the parent transaction @@ -1490,15 +1560,29 @@ return NULL; } + mark_block_busy(cache, block); + mutex_unlock(&cache->lock); + memcpy(block->parent_data, block->current_data, cache->block_size); + + mutex_lock(&cache->lock); + mark_block_unbusy(cache, block); + transaction->sub_num_blocks++; } else if (transaction != NULL && transaction->has_sub_transaction && block->parent_data == NULL && wasUnchanged) transaction->sub_num_blocks++; - if (cleared) + if (cleared) { + mark_block_busy(cache, block); + mutex_unlock(&cache->lock); + memset(block->current_data, 0, cache->block_size); + mutex_lock(&cache->lock); + mark_block_unbusy(cache, block); + } + block->is_dirty = true; TB(Get(cache, block)); TB2(BlockData(cache, block, "get writable")); @@ -2023,7 +2107,7 @@ while (count < kMaxCount && (block = (cached_block*)hash_next(cache->hash, &iterator)) != NULL) { - if (block->is_dirty && !block->is_writing) + if (block->is_dirty && !block->is_writing && !block->busy) blocks[count++] = block; } @@ -2051,8 +2135,10 @@ block_list::Iterator iterator = transaction->blocks.GetIterator(); - for (; count < kMaxCount && iterator.HasNext(); count++) { - blocks[count] = iterator.Next(); + while (count < kMaxCount && iterator.HasNext()) { + cached_block* block = iterator.Next(); + if (!block->busy) + blocks[count++] = block; } } @@ -2779,18 +2865,9 @@ 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); + // wait for all blocks to become unbusy + wait_for_busy_blocks(cache); - mutex_unlock(&cache->lock); - - entry.Wait(); - - mutex_lock(&cache->lock); - } - // free all blocks uint32 cookie = 0;