* Added a transaction listener mechanism to be notified when a

transaction ends or has been aborted.
* BFS now listens for transactions when it created an inode to see if 
  the transaction will be aborted without freeing the inode (in which 
  case it will panic for now).
* Started implementing tracing support, but it's not working yet.
* Minor cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23492 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2008-01-13 17:18:29 +00:00
parent 5276dad057
commit 4fc4f2c8ae
9 changed files with 298 additions and 17 deletions
+141 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2004-2007, Axel Dörfler, [email protected]. All rights reserved.
* Copyright 2004-2008, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*/
@@ -12,6 +12,7 @@
#include <block_cache.h>
#include <lock.h>
#include <vm_low_memory.h>
#include <tracing.h>
#include <util/kernel_cpp.h>
#include <util/DoublyLinkedList.h>
#include <util/AutoLock.h>
@@ -41,11 +42,64 @@
#define DEBUG_BLOCK_CACHE
//#define DEBUG_CHANGED
//#define TRANSACTION_TRACING
// This macro is used for fatal situations that are acceptable in a running
// system, like out of memory situations - should only panic for debugging.
#define FATAL(x) panic x
#ifdef TRANSACTION_TRACING
namespace TransactionTracing {
class Start : public AbstractTraceEntry {
public:
Start(cache_transaction *transaction)
:
fTransaction(transaction)
{
Initialized();
}
virtual void AddDump(char *buffer, size_t size)
{
}
private:
cache_transaction *fTransaction;
};
class Cancel : public AbstractTraceEntry {
public:
Cancel(cache_transaction *transaction)
:
fTransaction(transaction)
{
Initialized();
}
virtual void AddDump(char *buffer, size_t size)
{
}
private:
cache_transaction *fTransaction;
};
} // namespace TransactionTracing
# define T(x) new(std::nothrow) TransactionTracing::x;
#else
# define T(x) ;
#endif
struct cache_hook : DoublyLinkedListLinkImpl<cache_hook> {
transaction_notification_hook hook;
void *data;
};
typedef DoublyLinkedList<cache_hook> HookList;
struct cache_transaction {
cache_transaction();
@@ -57,6 +111,7 @@ struct cache_transaction {
block_list blocks;
transaction_notification_hook notification_hook;
void *notification_data;
HookList listeners;
bool open;
bool has_sub_transaction;
};
@@ -108,6 +163,24 @@ transaction_hash(void *_transaction, const void *_id, uint32 range)
}
/*! Notifies all listeners of this transaction, and removes them
afterwards.
*/
static void
notify_transaction_listeners(cache_transaction *transaction, int32 event)
{
HookList::Iterator iterator = transaction->listeners.GetIterator();
while (iterator.HasNext()) {
cache_hook *hook = iterator.Next();
hook->hook(transaction->id, event, hook->data);
iterator.Remove();
delete hook;
}
}
static void
delete_transaction(block_cache *cache, cache_transaction *transaction)
{
@@ -712,7 +785,7 @@ write_cached_block(block_cache *cache, cached_block *block,
TRACE(("cache transaction %ld finished!\n", previous->id));
if (previous->notification_hook != NULL) {
previous->notification_hook(previous->id,
previous->notification_hook(previous->id, TRANSACTION_WRITTEN,
previous->notification_data);
}
@@ -900,6 +973,8 @@ cache_end_transaction(void *_cache, int32 id,
transaction->notification_hook = hook;
transaction->notification_data = data;
notify_transaction_listeners(transaction, TRANSACTION_ENDED);
// iterate through all blocks and free the unchanged original contents
cached_block *block = transaction->first_block, *next;
@@ -949,6 +1024,8 @@ cache_abort_transaction(void *_cache, int32 id)
return B_BAD_VALUE;
}
notify_transaction_listeners(transaction, TRANSACTION_ABORTED);
// iterate through all blocks and restore their original contents
cached_block *block = transaction->first_block, *next;
@@ -1010,6 +1087,8 @@ cache_detach_sub_transaction(void *_cache, int32 id,
transaction->notification_hook = hook;
transaction->notification_data = data;
notify_transaction_listeners(transaction, TRANSACTION_ENDED);
// iterate through all blocks and free the unchanged original contents
cached_block *block = transaction->first_block, *next, *last = NULL;
@@ -1074,6 +1153,8 @@ cache_abort_sub_transaction(void *_cache, int32 id)
if (!transaction->has_sub_transaction)
return B_BAD_VALUE;
notify_transaction_listeners(transaction, TRANSACTION_ABORTED);
// revert all changes back to the version of the parent
cached_block *block = transaction->first_block, *next;
@@ -1118,6 +1199,8 @@ cache_start_sub_transaction(void *_cache, int32 id)
return B_BAD_VALUE;
}
notify_transaction_listeners(transaction, TRANSACTION_ENDED);
// move all changed blocks up to the parent
cached_block *block = transaction->first_block, *next;
@@ -1145,6 +1228,62 @@ cache_start_sub_transaction(void *_cache, int32 id)
}
/*! Adds a transaction listener that gets notified when the transaction
is ended or aborted.
The listener gets automatically removed in this case.
*/
status_t
cache_add_transaction_listener(void *_cache, int32 id,
transaction_notification_hook hookFunction, void *data)
{
block_cache *cache = (block_cache *)_cache;
cache_hook *hook = new(std::nothrow) cache_hook;
if (hook == NULL)
return B_NO_MEMORY;
BenaphoreLocker locker(&cache->lock);
cache_transaction *transaction = lookup_transaction(cache, id);
if (transaction == NULL) {
delete hook;
return B_BAD_VALUE;
}
hook->hook = hookFunction;
hook->data = data;
transaction->listeners.Add(hook);
return B_OK;
}
status_t
cache_remove_transaction_listener(void *_cache, int32 id,
transaction_notification_hook hookFunction, void *data)
{
block_cache *cache = (block_cache *)_cache;
BenaphoreLocker locker(&cache->lock);
cache_transaction *transaction = lookup_transaction(cache, id);
if (transaction == NULL)
return B_BAD_VALUE;
HookList::Iterator iterator = transaction->listeners.GetIterator();
while (iterator.HasNext()) {
cache_hook *hook = iterator.Next();
if (hook->data == data && hook->hook == hookFunction) {
iterator.Remove();
delete hook;
return B_OK;
}
}
return B_ENTRY_NOT_FOUND;
}
extern "C" status_t
cache_next_block_in_transaction(void *_cache, int32 id, uint32 *_cookie,
off_t *_blockNumber, void **_data, void **_unchangedData)