* cache_sync_transaction() now puts all blocks into the BlockWriter, and

therefore does not unlock anymore while iterating over the transactions.
  This gave other threads the opportunity to finish a transaction, causing
  bug #5412. Also, the BlockWriter will now always close transactions on its
  own, and you need to pass the transaction hash iterator to Add().
* Also, transactions that contain blocks that are currently written back will
  be ignored by the block writer, as well as cache_sync_transaction(). This
  fixes bug #5415.
* Improved error handling if BlockWriter fails to write back blocks. Most
  notably, they are no longer left busy_writing, and the functions calling
  it do proper error reporting (besides block_cache_discard() that does not
  return any erro code; I've added a TODO note there for now).
* The BlockWriter now starts with a larger array once it has to allocate one.
* One can now limit the number of blocks that go into a BlockWriter. This is
  used by the block writer thread, that shouldn't always write back everything
  every two seconds.
* Also, the fixed array is larger now (leaving enough space such that the
  block writer/notifier does not need to allocate anything).
* And finally, if allocating the array fails, the BlockWriter falls back to the
  synchronous write back used previously. IOW it will never write back less
  blocks than you ask for.
* Added static BlockWriter::WriteBlock() method replacing write_cached_block().
* Forgot to rename block_cache::busy_count to busy_reading_count.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35510 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2010-02-17 16:36:40 +00:00
parent d4af8f3e33
commit c5e52ecb78
+192 -123
View File
@@ -29,10 +29,8 @@
// TODO: this is a naive but growing implementation to test the API: // TODO: this is a naive but growing implementation to test the API:
// 1) block reading/writing is not at all optimized for speed, it will // block reading/writing is not at all optimized for speed, it will
// just read and write single blocks. // just read and write single blocks.
// 2) the locking could be improved; getting a block should not need to
// wait for blocks to be written
// TODO: the retrieval/copy of the original data could be delayed until the // TODO: the retrieval/copy of the original data could be delayed until the
// new data must be written, ie. in low memory situations. // new data must be written, ie. in low memory situations.
@@ -117,7 +115,7 @@ struct block_cache : DoublyLinkedListLinkImpl<block_cache> {
block_list unused_blocks; block_list unused_blocks;
ConditionVariable busy_reading_condition; ConditionVariable busy_reading_condition;
uint32 busy_count; uint32 busy_reading_count;
bool busy_reading_waiters; bool busy_reading_waiters;
ConditionVariable busy_writing_condition; ConditionVariable busy_writing_condition;
@@ -178,33 +176,49 @@ struct cache_transaction {
bool open; bool open;
bool has_sub_transaction; bool has_sub_transaction;
bigtime_t last_used; bigtime_t last_used;
int32 busy_writing_count;
}; };
class BlockWriter { class BlockWriter {
public: public:
BlockWriter(block_cache* cache, BlockWriter(block_cache* cache,
bool deleteTransaction = true); size_t max = SIZE_MAX);
~BlockWriter(); ~BlockWriter();
bool Add(cached_block* block); bool Add(cached_block* block,
hash_iterator* iterator = NULL);
bool Add(cache_transaction* transaction,
hash_iterator* iterator);
status_t Write(); status_t Write(hash_iterator* iterator = NULL,
bool canUnlock = true);
bool DeletedTransaction() const
{ return fDeletedTransaction; }
static status_t WriteBlock(block_cache* cache,
cached_block* block);
private: private:
void* _Data(cached_block* block) const; void* _Data(cached_block* block) const;
status_t _WriteBlock(cached_block* block); status_t _WriteBlock(cached_block* block);
void _BlockDone(cached_block* block); void _BlockDone(cached_block* block,
hash_iterator* iterator);
void _UnmarkWriting(cached_block* block);
private: private:
static const size_t kBufferSize = 16; static const size_t kBufferSize = 64;
block_cache* fCache; block_cache* fCache;
cached_block* fBuffer[kBufferSize]; cached_block* fBuffer[kBufferSize];
cached_block** fBlocks; cached_block** fBlocks;
size_t fCount; size_t fCount;
size_t fTotal;
size_t fCapacity;
size_t fMax; size_t fMax;
bool fDeleteTransaction; status_t fStatus;
bool fDeletedTransaction;
}; };
@@ -642,10 +656,6 @@ private:
#endif #endif
static status_t write_cached_block(block_cache* cache, cached_block* block,
bool deleteTransaction = true);
static DoublyLinkedList<block_cache> sCaches; static DoublyLinkedList<block_cache> sCaches;
static mutex sCachesLock = MUTEX_INITIALIZER("block caches"); static mutex sCachesLock = MUTEX_INITIALIZER("block caches");
static mutex sCachesMemoryUseLock static mutex sCachesMemoryUseLock
@@ -901,6 +911,7 @@ cache_transaction::cache_transaction()
open = true; open = true;
has_sub_transaction = false; has_sub_transaction = false;
last_used = system_time(); last_used = system_time();
busy_writing_count = 0;
} }
@@ -1001,13 +1012,16 @@ cached_block::Hash(void* _cacheEntry, const void* _block, uint32 range)
// #pragma mark - BlockWriter // #pragma mark - BlockWriter
BlockWriter::BlockWriter(block_cache* cache, bool deleteTransaction) BlockWriter::BlockWriter(block_cache* cache, size_t max)
: :
fCache(cache), fCache(cache),
fBlocks(fBuffer), fBlocks(fBuffer),
fCount(0), fCount(0),
fMax(kBufferSize), fTotal(0),
fDeleteTransaction(deleteTransaction) fCapacity(kBufferSize),
fMax(max),
fStatus(B_OK),
fDeletedTransaction(false)
{ {
} }
@@ -1019,33 +1033,71 @@ BlockWriter::~BlockWriter()
} }
/*! Adds the specified block to the to be written array. If no more blocks can
be added, false is returned, otherwise true.
*/
bool bool
BlockWriter::Add(cached_block* block) BlockWriter::Add(cached_block* block, hash_iterator* iterator)
{ {
ASSERT(block->CanBeWritten()); ASSERT(block->CanBeWritten());
if (fCount >= fMax) { if (fTotal == 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; return false;
if (fCount >= fCapacity) {
// Enlarge array if necessary
cached_block** newBlocks;
size_t newCapacity = max_c(256, fCapacity * 2);
if (fBlocks == fBuffer)
newBlocks = (cached_block**)malloc(newCapacity * sizeof(void*));
else {
newBlocks = (cached_block**)realloc(fBlocks,
newCapacity * sizeof(void*));
}
if (newBlocks == NULL) {
// Allocating a larger array failed - we need to write back what
// we have synchronously now (this will also clear the array)
Write(iterator, false);
} else {
if (fBlocks == fBuffer) if (fBlocks == fBuffer)
memcpy(newBlocks, fBuffer, kBufferSize * sizeof(void*)); memcpy(newBlocks, fBuffer, kBufferSize * sizeof(void*));
fBlocks = newBlocks; fBlocks = newBlocks;
fMax = max; fCapacity = newCapacity;
}
} }
fBlocks[fCount++] = block; fBlocks[fCount++] = block;
fTotal++;
block->busy_writing = true; block->busy_writing = true;
fCache->busy_writing_count++; fCache->busy_writing_count++;
if (block->previous_transaction != NULL)
block->previous_transaction->busy_writing_count++;
return true;
}
/*! Adds all blocks of the specified transaction to the to be written array.
If no more blocks can be added, false is returned, otherwise true.
*/
bool
BlockWriter::Add(cache_transaction* transaction, hash_iterator* iterator)
{
ASSERT(!transaction->open);
if (transaction->busy_writing_count != 0)
return true;
block_list::Iterator blockIterator = transaction->blocks.GetIterator();
while (cached_block* block = blockIterator.Next()) {
if (!Add(block))
return false;
if (DeletedTransaction())
break;
}
return true; return true;
} }
@@ -1055,35 +1107,57 @@ BlockWriter::Add(cached_block* block)
while the blocks are written back. while the blocks are written back.
*/ */
status_t status_t
BlockWriter::Write() BlockWriter::Write(hash_iterator* iterator, bool canUnlock)
{ {
if (fCount == 0) if (fCount == 0)
return B_OK; return B_OK;
if (canUnlock)
mutex_unlock(&fCache->lock); mutex_unlock(&fCache->lock);
// Sort blocks in their on-disk order // Sort blocks in their on-disk order
// TODO: ideally, this should be handled by the I/O scheduler // TODO: ideally, this should be handled by the I/O scheduler
qsort(fBlocks, fCount, sizeof(void*), &compare_blocks); qsort(fBlocks, fCount, sizeof(void*), &compare_blocks);
status_t status = B_OK; fDeletedTransaction = false;
for (uint32 i = 0; i < fCount; i++) { for (uint32 i = 0; i < fCount; i++) {
status_t blockStatus = _WriteBlock(fBlocks[i]); status_t status = _WriteBlock(fBlocks[i]);
if (blockStatus != B_OK) { if (status != B_OK) {
if (status == B_OK) // propagate to global error handling
status = blockStatus; if (fStatus == B_OK)
fStatus = status;
_UnmarkWriting(fBlocks[i]);
fBlocks[i] = NULL; fBlocks[i] = NULL;
// This block will not be marked clean // This block will not be marked clean
} }
} }
if (canUnlock)
mutex_lock(&fCache->lock); mutex_lock(&fCache->lock);
for (uint32 i = 0; i < fCount; i++) for (uint32 i = 0; i < fCount; i++)
_BlockDone(fBlocks[i]); _BlockDone(fBlocks[i], iterator);
return status; fCount = 0;
return fStatus;
}
/*! Writes the specified \a block back to disk. It will always only write back
the oldest change of the block if it is part of more than one transaction.
It will automatically send out TRANSACTION_WRITTEN notices, as well as
delete transactions when they are no longer used, and \a deleteTransaction
is \c true.
*/
/*static*/ status_t
BlockWriter::WriteBlock(block_cache* cache, cached_block* block)
{
BlockWriter writer(cache);
writer.Add(block);
return writer.Write();
} }
@@ -1125,7 +1199,7 @@ BlockWriter::_WriteBlock(cached_block* block)
void void
BlockWriter::_BlockDone(cached_block* block) BlockWriter::_BlockDone(cached_block* block, hash_iterator* iterator)
{ {
if (block == NULL) { if (block == NULL) {
// An error occured when trying to write this block // An error occured when trying to write this block
@@ -1138,6 +1212,8 @@ BlockWriter::_BlockDone(cached_block* block)
if (_Data(block) == block->current_data) if (_Data(block) == block->current_data)
block->is_dirty = false; block->is_dirty = false;
_UnmarkWriting(block);
cache_transaction* previous = block->previous_transaction; cache_transaction* previous = block->previous_transaction;
if (previous != NULL) { if (previous != NULL) {
previous->blocks.Remove(block); previous->blocks.Remove(block);
@@ -1158,10 +1234,13 @@ BlockWriter::_BlockDone(cached_block* block)
notify_transaction_listeners(fCache, previous, notify_transaction_listeners(fCache, previous,
TRANSACTION_WRITTEN); TRANSACTION_WRITTEN);
if (fDeleteTransaction) { if (iterator != NULL)
hash_remove_current(fCache->transaction_hash, iterator);
else
hash_remove(fCache->transaction_hash, previous); hash_remove(fCache->transaction_hash, previous);
delete_transaction(fCache, previous); delete_transaction(fCache, previous);
} fDeletedTransaction = true;
} }
} }
if (block->transaction == NULL && block->ref_count == 0) { if (block->transaction == NULL && block->ref_count == 0) {
@@ -1170,7 +1249,16 @@ BlockWriter::_BlockDone(cached_block* block)
fCache->unused_blocks.Add(block); fCache->unused_blocks.Add(block);
} }
TB2(BlockData(fCache, block, "after write"));
}
void
BlockWriter::_UnmarkWriting(cached_block* block)
{
block->busy_writing = false; block->busy_writing = false;
if (block->previous_transaction != NULL)
block->previous_transaction->busy_writing_count--;
fCache->busy_writing_count--; fCache->busy_writing_count--;
if ((fCache->busy_writing_waiters && fCache->busy_writing_count == 0) if ((fCache->busy_writing_waiters && fCache->busy_writing_count == 0)
@@ -1179,8 +1267,6 @@ BlockWriter::_BlockDone(cached_block* block)
block->busy_writing_waiters = false; block->busy_writing_waiters = false;
fCache->busy_writing_condition.NotifyAll(); fCache->busy_writing_condition.NotifyAll();
} }
TB2(BlockData(fCache, block, "after write"));
} }
@@ -1198,7 +1284,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), busy_reading_count(0),
busy_reading_waiters(false), busy_reading_waiters(false),
busy_writing_count(0), busy_writing_count(0),
busy_writing_waiters(0), busy_writing_waiters(0),
@@ -1374,7 +1460,8 @@ block_cache::RemoveUnusedBlocks(int32 maxAccessed, int32 count)
if (block->is_dirty && !block->discard) { if (block->is_dirty && !block->discard) {
if (block->busy_writing) if (block->busy_writing)
continue; continue;
write_cached_block(this, block, false);
BlockWriter::WriteBlock(this, block);
} }
// remove block from lists // remove block from lists
@@ -1469,7 +1556,7 @@ block_cache::_GetUnusedBlock()
TB(Flush(this, block, true)); TB(Flush(this, block, true));
// this can only happen if no transactions are used // this can only happen if no transactions are used
if (block->is_dirty && !block->busy_writing && !block->discard) if (block->is_dirty && !block->busy_writing && !block->discard)
write_cached_block(this, block, false); BlockWriter::WriteBlock(this, block);
// remove block from lists // remove block from lists
iterator.Remove(); iterator.Remove();
@@ -1498,7 +1585,7 @@ static void
mark_block_busy_reading(block_cache* cache, cached_block* block) mark_block_busy_reading(block_cache* cache, cached_block* block)
{ {
block->busy_reading = true; block->busy_reading = true;
cache->busy_count++; cache->busy_reading_count++;
} }
@@ -1508,9 +1595,9 @@ static void
mark_block_unbusy_reading(block_cache* cache, cached_block* block) mark_block_unbusy_reading(block_cache* cache, cached_block* block)
{ {
block->busy_reading = false; block->busy_reading = false;
cache->busy_count--; cache->busy_reading_count--;
if ((cache->busy_reading_waiters && cache->busy_count == 0) if ((cache->busy_reading_waiters && cache->busy_reading_count == 0)
|| block->busy_reading_waiters) { || block->busy_reading_waiters) {
cache->busy_reading_waiters = false; cache->busy_reading_waiters = false;
block->busy_reading_waiters = false; block->busy_reading_waiters = false;
@@ -1544,7 +1631,7 @@ wait_for_busy_reading_block(block_cache* cache, cached_block* block)
static void static void
wait_for_busy_reading_blocks(block_cache* cache) wait_for_busy_reading_blocks(block_cache* cache)
{ {
while (cache->busy_count != 0) { while (cache->busy_reading_count != 0) {
// wait for all blocks to be read in // wait for all blocks to be read in
ConditionVariableEntry entry; ConditionVariableEntry entry;
cache->busy_reading_condition.Add(&entry); cache->busy_reading_condition.Add(&entry);
@@ -1614,7 +1701,7 @@ put_cached_block(block_cache* cache, cached_block* block)
dump_block((const char*)block->current_data, 256, " "); dump_block((const char*)block->current_data, 256, " ");
dprintf("unchanged block:\n"); dprintf("unchanged block:\n");
dump_block((const char*)block->compare, 256, " "); dump_block((const char*)block->compare, 256, " ");
write_cached_block(cache, block); BlockWriter::WriteBlock(cache, block);
panic("block_cache: supposed to be clean block was changed!\n"); panic("block_cache: supposed to be clean block was changed!\n");
cache->Free(block->compare); cache->Free(block->compare);
@@ -1913,23 +2000,6 @@ get_writable_cached_block(block_cache* cache, off_t blockNumber, off_t base,
} }
/*! Writes the specified \a block back to disk. It will always only write back
the oldest change of the block if it is part of more than one transaction.
It will automatically send out TRANSACTION_WRITTEN notices, as well as
delete transactions when they are no longer used, and \a deleteTransaction
is \c true.
*/
static status_t
write_cached_block(block_cache* cache, cached_block* block,
bool deleteTransaction)
{
BlockWriter writer(cache, deleteTransaction);
writer.Add(block);
return writer.Write();
}
#if DEBUG_BLOCK_CACHE #if DEBUG_BLOCK_CACHE
@@ -2056,6 +2126,10 @@ dump_cache(int argc, char** argv)
kprintf(" max_blocks: %Ld\n", cache->max_blocks); kprintf(" max_blocks: %Ld\n", cache->max_blocks);
kprintf(" block_size: %lu\n", cache->block_size); kprintf(" block_size: %lu\n", cache->block_size);
kprintf(" next_transaction_id: %ld\n", cache->next_transaction_id); kprintf(" next_transaction_id: %ld\n", cache->next_transaction_id);
kprintf(" busy_reading: %lu, %s waiters\n", cache->busy_reading_count,
cache->busy_reading_waiters ? "has" : "no");
kprintf(" busy_writing: %lu, %s waiters\n", cache->busy_writing_count,
cache->busy_writing_waiters ? "has" : "no");
if (!cache->pending_notifications.IsEmpty()) { if (!cache->pending_notifications.IsEmpty()) {
kprintf(" pending notifications:\n"); kprintf(" pending notifications:\n");
@@ -2116,7 +2190,7 @@ 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 "
"busy, %ld in unused.\n", count, dirty, discarded, referenced, "busy, %ld in unused.\n", count, dirty, discarded, referenced,
cache->busy_count, cache->unused_blocks.Count()); cache->busy_reading_count, cache->unused_blocks.Count());
hash_close(cache->hash, &iterator, false); hash_close(cache->hash, &iterator, false);
return 0; return 0;
@@ -2380,7 +2454,7 @@ block_notifier_and_writer(void* /*data*/)
block_cache* cache = NULL; block_cache* cache = NULL;
while ((cache = get_next_locked_block_cache(cache)) != NULL) { while ((cache = get_next_locked_block_cache(cache)) != NULL) {
BlockWriter writer(cache); BlockWriter writer(cache, 64);
size_t cacheUsedMemory; size_t cacheUsedMemory;
object_cache_get_usage(cache->buffer_cache, &cacheUsedMemory); object_cache_get_usage(cache->buffer_cache, &cacheUsedMemory);
@@ -2403,11 +2477,10 @@ block_notifier_and_writer(void* /*data*/)
} else { } else {
hash_iterator iterator; hash_iterator iterator;
hash_open(cache->transaction_hash, &iterator); hash_open(cache->transaction_hash, &iterator);
bool stop = false;
cache_transaction* transaction; cache_transaction* transaction;
while ((transaction = (cache_transaction*)hash_next( while ((transaction = (cache_transaction*)hash_next(
cache->transaction_hash, &iterator)) != NULL && !stop) { cache->transaction_hash, &iterator)) != NULL) {
if (transaction->open) { if (transaction->open) {
if (system_time() > transaction->last_used if (system_time() > transaction->last_used
+ kTransactionIdleTime) { + kTransactionIdleTime) {
@@ -2418,17 +2491,9 @@ block_notifier_and_writer(void* /*data*/)
continue; continue;
} }
block_list::Iterator iterator if (!writer.Add(transaction, &iterator))
= transaction->blocks.GetIterator();
while (iterator.HasNext()) {
cached_block* block = iterator.Next();
if (block->CanBeWritten() && !writer.Add(block)) {
stop = true;
break; break;
} }
}
}
hash_close(cache->transaction_hash, &iterator, false); hash_close(cache->transaction_hash, &iterator, false);
} }
@@ -2599,11 +2664,15 @@ status_t
cache_sync_transaction(void* _cache, int32 id) cache_sync_transaction(void* _cache, int32 id)
{ {
block_cache* cache = (block_cache*)_cache; block_cache* cache = (block_cache*)_cache;
TransactionLocker locker(cache); bool hadBusy;
status_t status = B_ENTRY_NOT_FOUND;
TRACE(("cache_sync_transaction(id %ld)\n", id)); TRACE(("cache_sync_transaction(id %ld)\n", id));
do {
TransactionLocker locker(cache);
hadBusy = false;
BlockWriter writer(cache);
hash_iterator iterator; hash_iterator iterator;
hash_open(cache->transaction_hash, &iterator); hash_open(cache->transaction_hash, &iterator);
@@ -2612,31 +2681,24 @@ cache_sync_transaction(void* _cache, int32 id)
cache->transaction_hash, &iterator)) != NULL) { cache->transaction_hash, &iterator)) != NULL) {
// close all earlier transactions which haven't been closed yet // close all earlier transactions which haven't been closed yet
if (transaction->busy_writing_count != 0) {
hadBusy = true;
continue;
}
if (transaction->id <= id && !transaction->open) { if (transaction->id <= id && !transaction->open) {
// write back all of their remaining dirty blocks // write back all of their remaining dirty blocks
T(Action("sync", cache, transaction)); T(Action("sync", cache, transaction));
while (transaction->num_blocks > 0) {
BlockWriter writer(cache, false);
block_list::Iterator iterator
= transaction->blocks.GetIterator();
while (cached_block* block = iterator.Next()) { writer.Add(transaction, &iterator);
if (!writer.Add(block))
break;
}
status = writer.Write();
if (status != B_OK)
return status;
}
hash_remove_current(cache->transaction_hash, &iterator);
delete_transaction(cache, transaction);
} }
} }
hash_close(cache->transaction_hash, &iterator, false); hash_close(cache->transaction_hash, &iterator, false);
locker.Unlock();
status_t status = writer.Write();
if (status != B_OK)
return status;
} while (hadBusy);
wait_for_notifications(cache); wait_for_notifications(cache);
// make sure that all pending TRANSACTION_WRITTEN notifications // make sure that all pending TRANSACTION_WRITTEN notifications
@@ -2660,16 +2722,7 @@ cache_end_transaction(void* _cache, int32 id,
return B_BAD_VALUE; return B_BAD_VALUE;
} }
notify_transaction_listeners(cache, transaction, TRANSACTION_ENDED); // Write back all pending transaction blocks
if (add_transaction_listener(cache, transaction, TRANSACTION_WRITTEN, hook,
data) != B_OK) {
return B_NO_MEMORY;
}
T(Action("end", cache, transaction));
// iterate through all blocks and free the unchanged original contents
BlockWriter writer(cache); BlockWriter writer(cache);
cached_block* block = transaction->first_block; cached_block* block = transaction->first_block;
@@ -2680,7 +2733,20 @@ cache_end_transaction(void* _cache, int32 id,
} }
} }
writer.Write(); status_t status = writer.Write();
if (status != B_OK)
return status;
notify_transaction_listeners(cache, transaction, TRANSACTION_ENDED);
if (add_transaction_listener(cache, transaction, TRANSACTION_WRITTEN, hook,
data) != B_OK) {
return B_NO_MEMORY;
}
T(Action("end", cache, transaction));
// iterate through all blocks and free the unchanged original contents
cached_block* next; cached_block* next;
for (block = transaction->first_block; block != NULL; block = next) { for (block = transaction->first_block; block != NULL; block = next) {
@@ -2786,6 +2852,21 @@ cache_detach_sub_transaction(void* _cache, int32 id,
if (!transaction->has_sub_transaction) if (!transaction->has_sub_transaction)
return B_BAD_VALUE; return B_BAD_VALUE;
// iterate through all blocks and free the unchanged original contents
BlockWriter writer(cache);
cached_block* block = transaction->first_block;
for (; block != NULL; block = block->transaction_next) {
if (block->previous_transaction != NULL) {
// need to write back pending changes
writer.Add(block);
}
}
status_t status = writer.Write();
if (status != B_OK)
return status;
// create a new transaction for the sub transaction // create a new transaction for the sub transaction
cache_transaction* newTransaction = new(std::nothrow) cache_transaction; cache_transaction* newTransaction = new(std::nothrow) cache_transaction;
if (newTransaction == NULL) if (newTransaction == NULL)
@@ -2802,19 +2883,6 @@ cache_detach_sub_transaction(void* _cache, int32 id,
return B_NO_MEMORY; return B_NO_MEMORY;
} }
// iterate through all blocks and free the unchanged original contents
BlockWriter writer(cache);
cached_block* block = transaction->first_block;
for (; block != NULL; block = block->transaction_next) {
if (block->previous_transaction != NULL) {
// need to write back pending changes
writer.Add(block);
}
}
writer.Write();
cached_block* last = NULL; cached_block* last = NULL;
cached_block* next; cached_block* next;
for (block = transaction->first_block; block != NULL; block = next) { for (block = transaction->first_block; block != NULL; block = next) {
@@ -3276,6 +3344,7 @@ block_cache_discard(void* _cache, off_t blockNumber, size_t numBlocks)
} }
writer.Write(); writer.Write();
// TODO: this can fail, too!
for (; numBlocks > 0; numBlocks--, blockNumber++) { for (; numBlocks > 0; numBlocks--, blockNumber++) {
cached_block* block = (cached_block*)hash_lookup(cache->hash, cached_block* block = (cached_block*)hash_lookup(cache->hash,