Author: axeld Date: 2010-02-15 14:07:14 +0100 (Mon, 15 Feb 2010) New Revision: 35464 Changeset: http://dev.haiku-os.org/changeset/35464/haiku Modified: haiku/trunk/src/system/kernel/cache/block_cache.cpp Log: * Implemented a BlockWriter class that now performs writing back blocks. * Renamed the "busy" stuff to "busy_reading", and added a "busy_writing" concept. * This now allows reading a block (and other blocks), while blocks are written back. This should speed all operations needing to write back blocks, like unzipping or compiling. Modified: haiku/trunk/src/system/kernel/cache/block_cache.cpp =================================================================== --- haiku/trunk/src/system/kernel/cache/block_cache.cpp 2010-02-14 22:57:47 UTC (rev 35463) +++ haiku/trunk/src/system/kernel/cache/block_cache.cpp 2010-02-15 13:07:14 UTC (rev 35464) @@ -69,17 +69,21 @@ #endif int32 ref_count; int32 accessed; - bool busy : 1; + bool busy_reading : 1; + bool busy_writing : 1; bool is_writing : 1; // Block has been checked out for writing without transactions, and // cannot be written back if set bool is_dirty : 1; bool unused : 1; bool discard : 1; - bool busy_waiters : 1; + bool busy_reading_waiters : 1; + bool busy_writing_waiters : 1; cache_transaction* transaction; cache_transaction* previous_transaction; + bool CanBeWritten() const; + static int Compare(void* _cacheEntry, const void* _block); static uint32 Hash(void* _cacheEntry, const void* _block, uint32 range); }; @@ -112,10 +116,14 @@ object_cache* buffer_cache; block_list unused_blocks; - ConditionVariable busy_condition; + ConditionVariable busy_reading_condition; uint32 busy_count; - bool busy_block_waiters; + bool busy_reading_waiters; + ConditionVariable busy_writing_condition; + uint32 busy_writing_count; + bool busy_writing_waiters; + uint32 num_dirty_blocks; bool read_only; @@ -155,6 +163,7 @@ DoublyLinkedListMemberGetLink<cache_listener, &cache_listener::link> > ListenerList; + struct cache_transaction { cache_transaction(); @@ -171,6 +180,65 @@ bigtime_t last_used; }; + +class BlockWriter { +public: + BlockWriter(block_cache* cache, + bool deleteTransaction = true); + ~BlockWriter(); + + bool Add(cached_block* block); + + status_t Write(); + +private: + void* _Data(cached_block* block) const; + status_t _WriteBlock(cached_block* block); + void _BlockDone(cached_block* block); + +private: + static const size_t kBufferSize = 16; + + block_cache* fCache; + cached_block* fBuffer[kBufferSize]; + cached_block** fBlocks; + size_t fCount; + size_t fMax; + bool fDeleteTransaction; +}; + + +class TransactionLocking { +public: + inline bool Lock(block_cache* cache) + { + mutex_lock(&cache->lock); + + while (cache->busy_writing_count != 0) { + // wait for all blocks to be written + ConditionVariableEntry entry; + cache->busy_writing_condition.Add(&entry); + cache->busy_writing_waiters = true; + + mutex_unlock(&cache->lock); + + entry.Wait(); + + mutex_lock(&cache->lock); + } + + return true; + } + + inline void Unlock(block_cache* cache) + { + mutex_unlock(&cache->lock); + } +}; + +typedef AutoLocker<block_cache, TransactionLocking> TransactionLocker; + + #if BLOCK_CACHE_BLOCK_TRACING && !defined(BUILDING_USERLAND_FS_SERVER) namespace BlockTracing { @@ -894,6 +962,15 @@ } +bool +cached_block::CanBeWritten() const +{ + return !busy_writing && !busy_reading + && (previous_transaction != NULL + || (transaction == NULL && is_dirty && !is_writing)); +} + + /*static*/ int cached_block::Compare(void* _cacheEntry, const void* _block) { @@ -908,7 +985,6 @@ } - /*static*/ uint32 cached_block::Hash(void* _cacheEntry, const void* _block, uint32 range) { @@ -922,6 +998,192 @@ } +// #pragma mark - BlockWriter + + +BlockWriter::BlockWriter(block_cache* cache, bool deleteTransaction) + : + fCache(cache), + fBlocks(fBuffer), + fCount(0), + fMax(kBufferSize), + fDeleteTransaction(deleteTransaction) +{ +} + + +BlockWriter::~BlockWriter() +{ + if (fBlocks != fBuffer) + free(fBlocks); +} + + +bool +BlockWriter::Add(cached_block* block) +{ + ASSERT(block->CanBeWritten()); + + if (fCount >= fMax) { + // Enlarge array if necessary + cached_block** newBlocks; + size_t max = fMax * 2; + if (fBlocks == fBuffer) + newBlocks = (cached_block**)malloc(max * sizeof(void*)); + else + newBlocks = (cached_block**)realloc(fBlocks, max * sizeof(void*)); + + if (newBlocks == NULL) + return false; + + if (fBlocks == fBuffer) + memcpy(newBlocks, fBuffer, kBufferSize * sizeof(void*)); + + fBlocks = newBlocks; + fMax = max; + } + + fBlocks[fCount++] = block; + block->busy_writing = true; + fCache->busy_writing_count++; + + return true; +} + + +/*! Cache must be locked when calling this method, but it will be unlocked + while the blocks are written back. +*/ +status_t +BlockWriter::Write() +{ + if (fCount == 0) + return B_OK; + + mutex_unlock(&fCache->lock); + + // Sort blocks in their on-disk order + // TODO: ideally, this should be handled by the I/O scheduler + + qsort(fBlocks, fCount, sizeof(void*), &compare_blocks); + status_t status = B_OK; + + for (uint32 i = 0; i < fCount; i++) { + status_t blockStatus = _WriteBlock(fBlocks[i]); + if (blockStatus != B_OK) { + if (status == B_OK) + status = blockStatus; + fBlocks[i] = NULL; + // This block will not be marked clean + } + } + + mutex_lock(&fCache->lock); + + for (uint32 i = 0; i < fCount; i++) + _BlockDone(fBlocks[i]); + + return status; +} + + +void* +BlockWriter::_Data(cached_block* block) const +{ + return block->previous_transaction && block->original_data + ? block->original_data : block->current_data; + // We first need to write back changes from previous transactions +} + + +status_t +BlockWriter::_WriteBlock(cached_block* block) +{ + ASSERT(block->busy_writing); + + TRACE(("write_cached_block(block %Ld)\n", block->block_number)); + TB(Write(fCache, block)); + TB2(BlockData(fCache, block, "before write")); + + size_t blockSize = fCache->block_size; + + ssize_t written = write_pos(fCache->fd, + block->block_number * blockSize, _Data(block), blockSize); + + if (written != (ssize_t)blockSize) { + TB(Error(fCache, block->block_number, "write failed", written)); + FATAL(("could not write back block %Ld (%s)\n", block->block_number, + strerror(errno))); + if (written < 0) + return errno; + + return B_IO_ERROR; + } + + return B_OK; +} + + +void +BlockWriter::_BlockDone(cached_block* block) +{ + if (block == NULL) { + // An error occured when trying to write this block + return; + } + + if (fCache->num_dirty_blocks > 0) + fCache->num_dirty_blocks--; + + if (_Data(block) == block->current_data) + block->is_dirty = false; + + cache_transaction* previous = block->previous_transaction; + if (previous != NULL) { + previous->blocks.Remove(block); + block->previous_transaction = NULL; + + if (block->original_data != NULL && block->transaction == NULL) { + // This block is not part of a transaction, so it does not need + // its original pointer anymore. + fCache->Free(block->original_data); + block->original_data = NULL; + } + + // Has the previous transation been finished with that write? + if (--previous->num_blocks == 0) { + TRACE(("cache transaction %ld finished!\n", previous->id)); + T(Action("written", fCache, previous)); + + notify_transaction_listeners(fCache, previous, + TRANSACTION_WRITTEN); + + if (fDeleteTransaction) { + hash_remove(fCache->transaction_hash, previous); + delete_transaction(fCache, previous); + } + } + } + if (block->transaction == NULL && block->ref_count == 0) { + // the block is no longer used + block->unused = true; + fCache->unused_blocks.Add(block); + } + + block->busy_writing = false; + fCache->busy_writing_count--; + + if ((fCache->busy_writing_waiters && fCache->busy_writing_count == 0) + || block->busy_writing_waiters) { + fCache->busy_writing_waiters = false; + block->busy_writing_waiters = false; + fCache->busy_writing_condition.NotifyAll(); + } + + TB2(BlockData(fCache, block, "after write")); +} + + // #pragma mark - block_cache @@ -937,7 +1199,9 @@ transaction_hash(NULL), buffer_cache(NULL), busy_count(0), - busy_block_waiters(false), + busy_reading_waiters(false), + busy_writing_count(0), + busy_writing_waiters(0), num_dirty_blocks(0), read_only(readOnly) { @@ -961,7 +1225,8 @@ status_t block_cache::Init() { - busy_condition.Init(this, "cache block busy"); + busy_reading_condition.Init(this, "cache block busy_reading"); + busy_writing_condition.Init(this, "cache block busy writing"); condition_variable.Init(this, "cache transaction sync"); mutex_init(&lock, "block cache"); @@ -999,8 +1264,8 @@ void* block_cache::Allocate() { - if (low_resource_state(B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY) - != B_NO_LOW_RESOURCE) { + if (low_resource_state(B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY + | B_KERNEL_RESOURCE_ADDRESS_SPACE) != B_NO_LOW_RESOURCE) { // recycle existing before allocating a new one RemoveUnusedBlocks(1, 2); } @@ -1040,8 +1305,8 @@ { cached_block* block = NULL; - if (low_resource_state(B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY) - != B_NO_LOW_RESOURCE) { + if (low_resource_state(B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY + | B_KERNEL_RESOURCE_ADDRESS_SPACE) != B_NO_LOW_RESOURCE) { // recycle existing instead of allocating a new one block = _GetUnusedBlock(); } @@ -1075,12 +1340,14 @@ block->transaction = block->previous_transaction = NULL; block->original_data = NULL; block->parent_data = NULL; - block->busy = false; + block->busy_reading = false; + block->busy_writing = false; block->is_writing = false; block->is_dirty = false; block->unused = false; block->discard = false; - block->busy_waiters = false; + block->busy_reading_waiters = false; + block->busy_writing_waiters = false; #if BLOCK_CACHE_DEBUG_CHANGED block->compare = NULL; #endif @@ -1096,7 +1363,7 @@ for (block_list::Iterator iterator = unused_blocks.GetIterator(); cached_block* block = iterator.Next();) { - if (maxAccessed < block->accessed) + if (maxAccessed < block->accessed || block->busy_reading) continue; TB(Flush(this, block)); @@ -1104,8 +1371,11 @@ block->block_number, block->accessed)); // this can only happen if no transactions are used - if (block->is_dirty && !block->discard) + if (block->is_dirty && !block->discard) { + if (block->busy_writing) + continue; write_cached_block(this, block, false); + } // remove block from lists iterator.Remove(); @@ -1198,7 +1468,7 @@ cached_block* block = iterator.Next();) { TB(Flush(this, block, true)); // this can only happen if no transactions are used - if (block->is_dirty) + if (block->is_dirty && !block->busy_writing && !block->discard) write_cached_block(this, block, false); // remove block from lists @@ -1225,9 +1495,9 @@ /*! Cache must be locked. */ static void -mark_block_busy(block_cache* cache, cached_block* block) +mark_block_busy_reading(block_cache* cache, cached_block* block) { - block->busy = true; + block->busy_reading = true; cache->busy_count++; } @@ -1235,15 +1505,16 @@ /*! Cache must be locked. */ static void -mark_block_unbusy(block_cache* cache, cached_block* block) +mark_block_unbusy_reading(block_cache* cache, cached_block* block) { - block->busy = false; + block->busy_reading = 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(); + if ((cache->busy_reading_waiters && cache->busy_count == 0) + || block->busy_reading_waiters) { + cache->busy_reading_waiters = false; + block->busy_reading_waiters = false; + cache->busy_reading_condition.NotifyAll(); } } @@ -1251,31 +1522,33 @@ /*! Cache must be locked. */ static void -wait_for_busy_block(block_cache* cache, cached_block* block) +wait_for_busy_reading_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; + while (block->busy_reading) { + // wait for at least the specified block to be read in + ConditionVariableEntry entry; + cache->busy_reading_condition.Add(&entry); + block->busy_reading_waiters = true; - mutex_unlock(&cache->lock); + mutex_unlock(&cache->lock); - entry.Wait(); + entry.Wait(); - mutex_lock(&cache->lock); + mutex_lock(&cache->lock); + } } /*! Cache must be locked. */ static void -wait_for_busy_blocks(block_cache* cache) +wait_for_busy_reading_blocks(block_cache* cache) { while (cache->busy_count != 0) { - // wait for all blocks to be read in/written back + // wait for all blocks to be read in ConditionVariableEntry entry; - cache->busy_condition.Add(&entry); - cache->busy_block_waiters = true; + cache->busy_reading_condition.Add(&entry); + cache->busy_reading_waiters = true; mutex_unlock(&cache->lock); @@ -1286,6 +1559,46 @@ } +/*! Cache must be locked. +*/ +static void +wait_for_busy_writing_block(block_cache* cache, cached_block* block) +{ + while (block->busy_writing) { + // wait for all blocks to be written back + ConditionVariableEntry entry; + cache->busy_writing_condition.Add(&entry); + block->busy_writing_waiters = true; + + mutex_unlock(&cache->lock); + + entry.Wait(); + + mutex_lock(&cache->lock); + } +} + + +/*! Cache must be locked. +*/ +static void +wait_for_busy_writing_blocks(block_cache* cache) +{ + while (cache->busy_writing_count != 0) { + // wait for all blocks to be written back + ConditionVariableEntry entry; + cache->busy_writing_condition.Add(&entry); + cache->busy_writing_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, @@ -1407,9 +1720,9 @@ hash_insert_grow(cache->hash, block); *_allocated = true; - } else if (block->busy) { - // The block is currently busy - wait and try again later - wait_for_busy_block(cache, block); + } else if (block->busy_reading) { + // The block is currently busy_reading - wait and try again later + wait_for_busy_reading_block(cache, block); goto retry; } @@ -1417,7 +1730,7 @@ // read block into cache int32 blockSize = cache->block_size; - mark_block_busy(cache, block); + mark_block_busy_reading(cache, block); mutex_unlock(&cache->lock); ssize_t bytesRead = read_pos(cache->fd, blockNumber * blockSize, @@ -1433,7 +1746,7 @@ TB(Read(cache, block)); mutex_lock(&cache->lock); - mark_block_unbusy(cache, block); + mark_block_unbusy_reading(cache, block); } if (block->unused) { @@ -1474,18 +1787,21 @@ if (block == NULL) return NULL; + if (block->busy_writing) + wait_for_busy_writing_block(cache, block); + block->discard = false; // if there is no transaction support, we just return the current block if (transactionID == -1) { if (cleared) { - mark_block_busy(cache, block); + mark_block_busy_reading(cache, block); mutex_unlock(&cache->lock); memset(block->current_data, 0, cache->block_size); mutex_lock(&cache->lock); - mark_block_unbusy(cache, block); + mark_block_unbusy_reading(cache, block); } block->is_writing = true; @@ -1547,13 +1863,13 @@ return NULL; } - mark_block_busy(cache, block); + mark_block_busy_reading(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); + mark_block_unbusy_reading(cache, block); } if (block->parent_data == block->current_data) { // remember any previous contents for the parent transaction @@ -1566,13 +1882,13 @@ return NULL; } - mark_block_busy(cache, block); + mark_block_busy_reading(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); + mark_block_unbusy_reading(cache, block); transaction->sub_num_blocks++; } else if (transaction != NULL && transaction->has_sub_transaction @@ -1580,13 +1896,13 @@ transaction->sub_num_blocks++; if (cleared) { - mark_block_busy(cache, block); + mark_block_busy_reading(cache, block); mutex_unlock(&cache->lock); memset(block->current_data, 0, cache->block_size); mutex_lock(&cache->lock); - mark_block_unbusy(cache, block); + mark_block_unbusy_reading(cache, block); } block->is_dirty = true; @@ -1607,65 +1923,10 @@ write_cached_block(block_cache* cache, cached_block* block, bool deleteTransaction) { - cache_transaction* previous = block->previous_transaction; - int32 blockSize = cache->block_size; - - void* data = previous && block->original_data - ? block->original_data : block->current_data; - // we first need to write back changes from previous transactions - - TRACE(("write_cached_block(block %Ld)\n", block->block_number)); - TB(Write(cache, block)); - TB2(BlockData(cache, block, "before write")); - - ssize_t written = write_pos(cache->fd, block->block_number * blockSize, - data, blockSize); - - if (written < blockSize) { - TB(Error(cache, block->block_number, "write failed", written)); - FATAL(("could not write back block %Ld (%s)\n", block->block_number, - strerror(errno))); - return B_IO_ERROR; - } - - if (cache->num_dirty_blocks > 0) - cache->num_dirty_blocks--; - - if (data == block->current_data) - block->is_dirty = false; - - if (previous != NULL) { - previous->blocks.Remove(block); - block->previous_transaction = NULL; - - if (block->original_data != NULL && block->transaction == NULL) { - // This block is not part of a transaction, so it does not need - // its original pointer anymore. - cache->Free(block->original_data); - block->original_data = NULL; - } - - // Has the previous transation been finished with that write? - if (--previous->num_blocks == 0) { - TRACE(("cache transaction %ld finished!\n", previous->id)); - T(Action("written", cache, previous)); - - notify_transaction_listeners(cache, previous, TRANSACTION_WRITTEN); - - if (deleteTransaction) { - hash_remove(cache->transaction_hash, previous); - delete_transaction(cache, previous); - } - } - } - if (block->transaction == NULL && block->ref_count == 0) { - // the block is no longer used - block->unused = true; - cache->unused_blocks.Add(block); - } - - TB2(BlockData(cache, block, "after write")); - return B_OK; + BlockWriter writer(cache, deleteTransaction); + + writer.Add(block); + return writer.Write(); } @@ -1678,7 +1939,7 @@ (addr_t)block, block->block_number, (addr_t)block->current_data, (addr_t)block->original_data, (addr_t)block->parent_data, block->ref_count, block->accessed, - block->busy ? 'B' : '-', block->is_writing ? 'W' : '-', + block->busy_reading ? 'B' : '-', block->is_writing ? 'W' : '-', block->is_dirty ? 'D' : '-', block->unused ? 'U' : '-', block->discard ? 'D' : '-', (addr_t)block->transaction, @@ -1733,8 +1994,8 @@ kprintf(" ref_count: %ld\n", block->ref_count); kprintf(" accessed: %ld\n", block->accessed); kprintf(" flags: "); - if (block->busy) - kprintf(" busy"); + if (block->busy_reading) + kprintf(" busy_reading"); if (block->is_writing) kprintf(" is-writing"); if (block->is_dirty) @@ -2095,9 +2356,7 @@ block_cache* cache = NULL; while ((cache = get_next_locked_block_cache(cache)) != NULL) { - const uint32 kMaxCount = 64; - cached_block* blocks[kMaxCount]; - uint32 count = 0; + BlockWriter writer(cache); size_t cacheUsedMemory; object_cache_get_usage(cache->buffer_cache, &cacheUsedMemory); @@ -2110,22 +2369,21 @@ hash_open(cache->hash, &iterator); cached_block* block; - while (count < kMaxCount - && (block = (cached_block*)hash_next(cache->hash, - &iterator)) != NULL) { - if (block->is_dirty && !block->is_writing && !block->busy) - blocks[count++] = block; + while ((block = (cached_block*)hash_next(cache->hash, &iterator)) + != NULL) { + if (block->CanBeWritten() && !writer.Add(block)) + break; } hash_close(cache->hash, &iterator, false); } else { hash_iterator iterator; hash_open(cache->transaction_hash, &iterator); + bool stop = false; cache_transaction* transaction; while ((transaction = (cache_transaction*)hash_next( - cache->transaction_hash, &iterator)) != NULL - && count < kMaxCount) { + cache->transaction_hash, &iterator)) != NULL && !stop) { if (transaction->open) { if (system_time() > transaction->last_used + kTransactionIdleTime) { @@ -2136,27 +2394,22 @@ continue; } - // sort blocks to speed up writing them back - // TODO: ideally, this should be handled by the I/O scheduler block_list::Iterator iterator = transaction->blocks.GetIterator(); - while (count < kMaxCount && iterator.HasNext()) { + while (iterator.HasNext()) { cached_block* block = iterator.Next(); - if (!block->busy) - blocks[count++] = block; + if (block->CanBeWritten() && !writer.Add(block)) { + stop = true; + break; + } } } hash_close(cache->transaction_hash, &iterator, false); } - qsort(blocks, count, sizeof(void*), &compare_blocks); - - for (uint32 i = 0; i < count; i++) { - if (write_cached_block(cache, blocks[i], true) != B_OK) - break; - } + writer.Write(); } MutexLocker _(sCachesMemoryUseLock); @@ -2293,7 +2546,7 @@ cache_start_transaction(void* _cache) { block_cache* cache = (block_cache*)_cache; - MutexLocker locker(&cache->lock); + TransactionLocker locker(cache); if (cache->last_transaction && cache->last_transaction->open) { panic("last transaction (%ld) still open!\n", @@ -2320,7 +2573,7 @@ cache_sync_transaction(void* _cache, int32 id) { block_cache* cache = (block_cache*)_cache; - MutexLocker locker(&cache->lock); + TransactionLocker locker(cache); status_t status = B_ENTRY_NOT_FOUND; TRACE(("cache_sync_transaction(id %ld)\n", id)); @@ -2337,34 +2590,16 @@ // write back all of their remaining dirty blocks T(Action("sync", cache, transaction)); while (transaction->num_blocks > 0) { - // sort blocks to speed up writing them back - // TODO: this should be handled by the I/O scheduler + BlockWriter writer(cache, transaction->num_blocks); block_list::Iterator iterator = transaction->blocks.GetIterator(); - uint32 maxCount = transaction->num_blocks; - cached_block* buffer[16]; - cached_block** blocks = (cached_block**)malloc(maxCount - * sizeof(void*)); - if (blocks == NULL) { - maxCount = 16; - blocks = buffer; - } - uint32 count = 0; - for (; count < maxCount && iterator.HasNext(); count++) { - blocks[count] = iterator.Next(); - } - qsort(blocks, count, sizeof(void*), &compare_blocks); - - for (uint32 i = 0; i < count; i++) { - status = write_cached_block(cache, blocks[i], false); - if (status != B_OK) + while (cached_block* block = iterator.Next()) { + if (!writer.Add(block)) break; } - if (blocks != buffer) - free(blocks); - + status = writer.Write(); if (status != B_OK) return status; } @@ -2389,7 +2624,7 @@ transaction_notification_hook hook, void* data) { block_cache* cache = (block_cache*)_cache; - MutexLocker locker(&cache->lock); + TransactionLocker locker(cache); TRACE(("cache_end_transaction(id = %ld)\n", id)); @@ -2410,6 +2645,7 @@ // iterate through all blocks and free the unchanged original contents + BlockWriter writer(cache); cached_block* block = transaction->first_block; cached_block* next; for (; block != NULL; block = next) { @@ -2453,7 +2689,7 @@ cache_abort_transaction(void* _cache, int32 id) { block_cache* cache = (block_cache*)_cache; - MutexLocker locker(&cache->lock); + TransactionLocker locker(cache); TRACE(("cache_abort_transaction(id = %ld)\n", id)); @@ -2506,7 +2742,7 @@ transaction_notification_hook hook, void* data) { block_cache* cache = (block_cache*)_cache; - MutexLocker locker(&cache->lock); + TransactionLocker locker(cache); TRACE(("cache_detach_sub_transaction(id = %ld)\n", id)); @@ -2600,7 +2836,7 @@ cache_abort_sub_transaction(void* _cache, int32 id) { block_cache* cache = (block_cache*)_cache; - MutexLocker locker(&cache->lock); + TransactionLocker locker(cache); TRACE(("cache_abort_sub_transaction(id = %ld)\n", id)); @@ -2653,7 +2889,7 @@ cache_start_sub_transaction(void* _cache, int32 id) { block_cache* cache = (block_cache*)_cache; - MutexLocker locker(&cache->lock); + TransactionLocker locker(cache); TRACE(("cache_start_sub_transaction(id = %ld)\n", id)); @@ -2717,9 +2953,8 @@ transaction_notification_hook hook, void* data) { block_cache* cache = (block_cache*)_cache; + TransactionLocker locker(cache); - MutexLocker locker(&cache->lock); - cache_transaction* transaction = lookup_transaction(cache, id); if (transaction == NULL) return B_BAD_VALUE; @@ -2733,9 +2968,8 @@ transaction_notification_hook hookFunction, void* data) { block_cache* cache = (block_cache*)_cache; + TransactionLocker locker(cache); - MutexLocker locker(&cache->lock); - cache_transaction* transaction = lookup_transaction(cache, id); if (transaction == NULL) return B_BAD_VALUE; @@ -2766,9 +3000,8 @@ { cached_block* block = (cached_block*)*_cookie; block_cache* cache = (block_cache*)_cache; + TransactionLocker locker(cache); - MutexLocker locker(&cache->lock); - cache_transaction* transaction = lookup_transaction(cache, id); if (transaction == NULL || !transaction->open) return B_BAD_VALUE; @@ -2809,7 +3042,7 @@ cache_blocks_in_transaction(void* _cache, int32 id) { block_cache* cache = (block_cache*)_cache; - MutexLocker locker(&cache->lock); + TransactionLocker locker(cache); cache_transaction* transaction = lookup_transaction(cache, id); if (transaction == NULL) @@ -2827,7 +3060,7 @@ cache_blocks_in_main_transaction(void* _cache, int32 id) { block_cache* cache = (block_cache*)_cache; - MutexLocker locker(&cache->lock); + TransactionLocker locker(cache); cache_transaction* transaction = lookup_transaction(cache, id); if (transaction == NULL) @@ -2844,7 +3077,7 @@ cache_blocks_in_sub_transaction(void* _cache, int32 id) { block_cache* cache = (block_cache*)_cache; - MutexLocker locker(&cache->lock); + TransactionLocker locker(cache); cache_transaction* transaction = lookup_transaction(cache, id); if (transaction == NULL) @@ -2872,7 +3105,8 @@ mutex_lock(&cache->lock); // wait for all blocks to become unbusy - wait_for_busy_blocks(cache); + wait_for_busy_reading_blocks(cache); + wait_for_busy_writing_blocks(cache); [... truncated: 86 lines follow ...]