First go at an implementation of sub transactions - code is not yet tested, though.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@14429 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+103
-6
@@ -30,7 +30,7 @@
|
|||||||
// 3) dirty blocks are only written back if asked for
|
// 3) dirty blocks are only written back if asked for
|
||||||
// 4) blocks are never removed yet
|
// 4) blocks are never removed yet
|
||||||
|
|
||||||
//#define TRACE_BLOCK_CACHE
|
#define TRACE_BLOCK_CACHE
|
||||||
#ifdef TRACE_BLOCK_CACHE
|
#ifdef TRACE_BLOCK_CACHE
|
||||||
# define TRACE(x) dprintf x
|
# define TRACE(x) dprintf x
|
||||||
#else
|
#else
|
||||||
@@ -47,6 +47,7 @@ struct cache_transaction {
|
|||||||
transaction_notification_hook notification_hook;
|
transaction_notification_hook notification_hook;
|
||||||
void *notification_data;
|
void *notification_data;
|
||||||
bool open;
|
bool open;
|
||||||
|
bool has_sub_transaction;
|
||||||
};
|
};
|
||||||
|
|
||||||
static status_t write_cached_block(block_cache *cache, cached_block *block, bool deleteTransaction = true);
|
static status_t write_cached_block(block_cache *cache, cached_block *block, bool deleteTransaction = true);
|
||||||
@@ -243,6 +244,7 @@ block_cache::FreeBlock(cached_block *block)
|
|||||||
range->Free(this, block);
|
range->Free(this, block);
|
||||||
|
|
||||||
Free(block->original);
|
Free(block->original);
|
||||||
|
Free(block->parent_data);
|
||||||
#ifdef DEBUG_CHANGED
|
#ifdef DEBUG_CHANGED
|
||||||
Free(block->compare);
|
Free(block->compare);
|
||||||
#endif
|
#endif
|
||||||
@@ -271,6 +273,7 @@ block_cache::NewBlock(off_t blockNumber)
|
|||||||
block->transaction_next = NULL;
|
block->transaction_next = NULL;
|
||||||
block->transaction = block->previous_transaction = NULL;
|
block->transaction = block->previous_transaction = NULL;
|
||||||
block->original = NULL;
|
block->original = NULL;
|
||||||
|
block->parent_data = NULL;
|
||||||
block->is_dirty = false;
|
block->is_dirty = false;
|
||||||
#ifdef DEBUG_CHANGED
|
#ifdef DEBUG_CHANGED
|
||||||
block->compare = NULL;
|
block->compare = NULL;
|
||||||
@@ -424,13 +427,11 @@ get_writable_cached_block(block_cache *cache, off_t blockNumber, off_t base, off
|
|||||||
return block->data;
|
return block->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToDo: note, even if we panic, we should probably put the cached block
|
|
||||||
// back before we return
|
|
||||||
|
|
||||||
if (block->transaction != NULL && block->transaction->id != transactionID) {
|
if (block->transaction != NULL && block->transaction->id != transactionID) {
|
||||||
// ToDo: we have to wait here until the other transaction is done.
|
// ToDo: we have to wait here until the other transaction is done.
|
||||||
// Maybe we should even panic, since we can't prevent any deadlocks.
|
// Maybe we should even panic, since we can't prevent any deadlocks.
|
||||||
panic("get_writable_cached_block(): asked to get busy writable block (transaction %ld)\n", block->transaction->id);
|
panic("get_writable_cached_block(): asked to get busy writable block (transaction %ld)\n", block->transaction->id);
|
||||||
|
put_cached_block(cache, block);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (block->transaction == NULL && transactionID != -1) {
|
if (block->transaction == NULL && transactionID != -1) {
|
||||||
@@ -438,10 +439,12 @@ get_writable_cached_block(block_cache *cache, off_t blockNumber, off_t base, off
|
|||||||
cache_transaction *transaction = lookup_transaction(cache, transactionID);
|
cache_transaction *transaction = lookup_transaction(cache, transactionID);
|
||||||
if (transaction == NULL) {
|
if (transaction == NULL) {
|
||||||
panic("get_writable_cached_block(): invalid transaction %ld!\n", transactionID);
|
panic("get_writable_cached_block(): invalid transaction %ld!\n", transactionID);
|
||||||
|
put_cached_block(cache, block);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!transaction->open) {
|
if (!transaction->open) {
|
||||||
panic("get_writable_cached_block(): transaction already done!\n");
|
panic("get_writable_cached_block(): transaction already done!\n");
|
||||||
|
put_cached_block(cache, block);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,6 +466,17 @@ get_writable_cached_block(block_cache *cache, off_t blockNumber, off_t base, off
|
|||||||
|
|
||||||
memcpy(block->original, block->data, cache->block_size);
|
memcpy(block->original, block->data, cache->block_size);
|
||||||
}
|
}
|
||||||
|
if (block->parent_data == block->data) {
|
||||||
|
// remember any previous contents for the parent transaction
|
||||||
|
block->parent_data = cache->Allocate();
|
||||||
|
if (block->parent_data == NULL) {
|
||||||
|
// ToDo: maybe we should just continue the current transaction in this case...
|
||||||
|
put_cached_block(cache, block);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(block->parent_data, block->data, cache->block_size);
|
||||||
|
}
|
||||||
|
|
||||||
if (cleared)
|
if (cleared)
|
||||||
memset(block->data, 0, cache->block_size);
|
memset(block->data, 0, cache->block_size);
|
||||||
@@ -615,6 +629,10 @@ cache_end_transaction(void *_cache, int32 id, transaction_notification_hook hook
|
|||||||
cache->Free(block->original);
|
cache->Free(block->original);
|
||||||
block->original = NULL;
|
block->original = NULL;
|
||||||
}
|
}
|
||||||
|
if (transaction->has_sub_transaction && block->parent_data != block->data) {
|
||||||
|
cache->Free(block->parent_data);
|
||||||
|
block->parent_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// move the block to the previous transaction list
|
// move the block to the previous transaction list
|
||||||
transaction->blocks.Add(block);
|
transaction->blocks.Add(block);
|
||||||
@@ -657,6 +675,10 @@ cache_abort_transaction(void *_cache, int32 id)
|
|||||||
cache->Free(block->original);
|
cache->Free(block->original);
|
||||||
block->original = NULL;
|
block->original = NULL;
|
||||||
}
|
}
|
||||||
|
if (transaction->has_sub_transaction && block->parent_data != block->data) {
|
||||||
|
cache->Free(block->parent_data);
|
||||||
|
block->parent_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
block->transaction_next = NULL;
|
block->transaction_next = NULL;
|
||||||
block->transaction = NULL;
|
block->transaction = NULL;
|
||||||
@@ -670,6 +692,7 @@ cache_abort_transaction(void *_cache, int32 id)
|
|||||||
extern "C" int32
|
extern "C" int32
|
||||||
cache_detach_sub_transaction(void *_cache, int32 id)
|
cache_detach_sub_transaction(void *_cache, int32 id)
|
||||||
{
|
{
|
||||||
|
// ToDo: implement me!
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,14 +700,88 @@ cache_detach_sub_transaction(void *_cache, int32 id)
|
|||||||
extern "C" status_t
|
extern "C" status_t
|
||||||
cache_abort_sub_transaction(void *_cache, int32 id)
|
cache_abort_sub_transaction(void *_cache, int32 id)
|
||||||
{
|
{
|
||||||
return B_ERROR;
|
block_cache *cache = (block_cache *)_cache;
|
||||||
|
BenaphoreLocker locker(&cache->lock);
|
||||||
|
|
||||||
|
TRACE(("cache_abort_sub_transaction(id = %ld)\n", id));
|
||||||
|
|
||||||
|
cache_transaction *transaction = lookup_transaction(cache, id);
|
||||||
|
if (transaction == NULL) {
|
||||||
|
panic("cache_abort_sub_transaction(): invalid transaction ID\n");
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
if (!transaction->has_sub_transaction)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
// revert all changes back to the version of the parent
|
||||||
|
|
||||||
|
cached_block *block = transaction->first_block, *next;
|
||||||
|
for (; block != NULL; block = next) {
|
||||||
|
next = block->transaction_next;
|
||||||
|
|
||||||
|
if (block->original == NULL) {
|
||||||
|
// an unchanged block in the transaction?
|
||||||
|
panic("unchanged block in transaction!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block->parent_data != block->data) {
|
||||||
|
// the block has been changed and must be restored
|
||||||
|
TRACE(("cache_abort_sub_transaction(id = %ld): restored contents of block %Ld\n",
|
||||||
|
transaction->id, block->block_number));
|
||||||
|
memcpy(block->data, block->parent_data, cache->block_size);
|
||||||
|
cache->Free(block->parent_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
block->parent_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all subsequent changes will go into the main transaction
|
||||||
|
transaction->has_sub_transaction = false;
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" status_t
|
extern "C" status_t
|
||||||
cache_start_sub_transaction(void *_cache, int32 id)
|
cache_start_sub_transaction(void *_cache, int32 id)
|
||||||
{
|
{
|
||||||
return B_ERROR;
|
block_cache *cache = (block_cache *)_cache;
|
||||||
|
BenaphoreLocker locker(&cache->lock);
|
||||||
|
|
||||||
|
TRACE(("cache_start_sub_transaction(id = %ld)\n", id));
|
||||||
|
|
||||||
|
cache_transaction *transaction = lookup_transaction(cache, id);
|
||||||
|
if (transaction == NULL) {
|
||||||
|
panic("cache_start_sub_transaction(): invalid transaction ID\n");
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move all changed blocks up to the parent
|
||||||
|
|
||||||
|
cached_block *block = transaction->first_block, *next;
|
||||||
|
for (; block != NULL; block = next) {
|
||||||
|
next = block->transaction_next;
|
||||||
|
|
||||||
|
if (block->original == NULL) {
|
||||||
|
// an unchanged block in the transaction?
|
||||||
|
panic("unchanged block in transaction!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transaction->has_sub_transaction && block->parent_data != block->data) {
|
||||||
|
// there already is an older sub transaction - we acknowledge
|
||||||
|
// its changes and move its blocks up to the parent
|
||||||
|
cache->Free(block->parent_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we "allocate" the parent data lazily, that means, we don't copy
|
||||||
|
// the data (and allocate memory for it) until we need to
|
||||||
|
block->parent_data = block->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all subsequent changes will go into the sub transaction
|
||||||
|
transaction->has_sub_transaction = true;
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ struct cached_block {
|
|||||||
off_t block_number;
|
off_t block_number;
|
||||||
void *data;
|
void *data;
|
||||||
void *original;
|
void *original;
|
||||||
|
void *parent_data;
|
||||||
#ifdef DEBUG_CHANGED
|
#ifdef DEBUG_CHANGED
|
||||||
void *compare;
|
void *compare;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user