* 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:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2004-2007, Haiku Inc. All Rights Reserved.
|
* Copyright 2004-2008, Haiku Inc. All Rights Reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef _FS_CACHE_H
|
#ifndef _FS_CACHE_H
|
||||||
@@ -11,7 +11,14 @@
|
|||||||
#include <fs_interface.h>
|
#include <fs_interface.h>
|
||||||
|
|
||||||
|
|
||||||
typedef void (*transaction_notification_hook)(int32 id, void *data);
|
enum {
|
||||||
|
TRANSACTION_WRITTEN,
|
||||||
|
TRANSACTION_ABORTED,
|
||||||
|
TRANSACTION_ENDED
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*transaction_notification_hook)(int32 id, int32 event,
|
||||||
|
void *data);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -27,6 +34,10 @@ extern int32 cache_detach_sub_transaction(void *_cache, int32 id,
|
|||||||
transaction_notification_hook hook, void *data);
|
transaction_notification_hook hook, void *data);
|
||||||
extern status_t cache_abort_sub_transaction(void *_cache, int32 id);
|
extern status_t cache_abort_sub_transaction(void *_cache, int32 id);
|
||||||
extern status_t cache_start_sub_transaction(void *_cache, int32 id);
|
extern status_t cache_start_sub_transaction(void *_cache, int32 id);
|
||||||
|
extern status_t cache_add_transaction_listener(void *_cache, int32 id,
|
||||||
|
transaction_notification_hook hook, void *data);
|
||||||
|
extern status_t cache_remove_transaction_listener(void *_cache, int32 id,
|
||||||
|
transaction_notification_hook hook, void *data);
|
||||||
extern status_t cache_next_block_in_transaction(void *_cache, int32 id,
|
extern status_t cache_next_block_in_transaction(void *_cache, int32 id,
|
||||||
uint32 *_cookie, off_t *_blockNumber, void **_data,
|
uint32 *_cookie, off_t *_blockNumber, void **_data,
|
||||||
void **_unchangedData);
|
void **_unchangedData);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
* Copyright 2007-2008, Ingo Weinhold, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef _FSSH_API_WRAPPER_H
|
#ifndef _FSSH_API_WRAPPER_H
|
||||||
@@ -787,6 +787,10 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// #pragma mark - fssh_fs_cache.h
|
// #pragma mark - fssh_fs_cache.h
|
||||||
|
|
||||||
|
#define TRANSACTION_WRITTEN FSSH_TRANSACTION_WRITTEN
|
||||||
|
#define TRANSACTION_ABORTED FSSH_TRANSACTION_ABORTED
|
||||||
|
#define TRANSACTION_ENDED FSSH_TRANSACTION_ENDED
|
||||||
|
|
||||||
#define transaction_notification_hook fssh_transaction_notification_hook
|
#define transaction_notification_hook fssh_transaction_notification_hook
|
||||||
|
|
||||||
/* transactions */
|
/* transactions */
|
||||||
@@ -797,6 +801,8 @@
|
|||||||
#define cache_detach_sub_transaction fssh_cache_detach_sub_transaction
|
#define cache_detach_sub_transaction fssh_cache_detach_sub_transaction
|
||||||
#define cache_abort_sub_transaction fssh_cache_abort_sub_transaction
|
#define cache_abort_sub_transaction fssh_cache_abort_sub_transaction
|
||||||
#define cache_start_sub_transaction fssh_cache_start_sub_transaction
|
#define cache_start_sub_transaction fssh_cache_start_sub_transaction
|
||||||
|
#define cache_add_transaction_listener fssh_cache_add_transaction_listener
|
||||||
|
#define cache_remove_transaction_listener fssh_cache_remove_transaction_listener
|
||||||
#define cache_next_block_in_transaction fssh_cache_next_block_in_transaction
|
#define cache_next_block_in_transaction fssh_cache_next_block_in_transaction
|
||||||
#define cache_blocks_in_transaction fssh_cache_blocks_in_transaction
|
#define cache_blocks_in_transaction fssh_cache_blocks_in_transaction
|
||||||
#define cache_blocks_in_sub_transaction fssh_cache_blocks_in_sub_transaction
|
#define cache_blocks_in_sub_transaction fssh_cache_blocks_in_sub_transaction
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2004-2007, Haiku Inc. All Rights Reserved.
|
* Copyright 2004-2008, Haiku Inc. All Rights Reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef _FSSH_FS_CACHE_H
|
#ifndef _FSSH_FS_CACHE_H
|
||||||
@@ -11,7 +11,14 @@
|
|||||||
#include "fssh_fs_interface.h"
|
#include "fssh_fs_interface.h"
|
||||||
|
|
||||||
|
|
||||||
typedef void (*fssh_transaction_notification_hook)(int32_t id, void *data);
|
enum {
|
||||||
|
FSSH_TRANSACTION_WRITTEN,
|
||||||
|
FSSH_TRANSACTION_ABORTED,
|
||||||
|
FSSH_TRANSACTION_ENDED
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*fssh_transaction_notification_hook)(int32_t id, int32_t event,
|
||||||
|
void *data);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -31,6 +38,12 @@ extern fssh_status_t fssh_cache_abort_sub_transaction(void *_cache,
|
|||||||
int32_t id);
|
int32_t id);
|
||||||
extern fssh_status_t fssh_cache_start_sub_transaction(void *_cache,
|
extern fssh_status_t fssh_cache_start_sub_transaction(void *_cache,
|
||||||
int32_t id);
|
int32_t id);
|
||||||
|
extern fssh_status_t fssh_cache_add_transaction_listener(void *_cache,
|
||||||
|
int32_t id, fssh_transaction_notification_hook hook,
|
||||||
|
void *data);
|
||||||
|
extern fssh_status_t fssh_cache_remove_transaction_listener(void *_cache,
|
||||||
|
int32_t id, fssh_transaction_notification_hook hook,
|
||||||
|
void *data);
|
||||||
extern fssh_status_t fssh_cache_next_block_in_transaction(void *_cache,
|
extern fssh_status_t fssh_cache_next_block_in_transaction(void *_cache,
|
||||||
int32_t id, uint32_t *_cookie,
|
int32_t id, uint32_t *_cookie,
|
||||||
fssh_off_t *_blockNumber, void **_data,
|
fssh_off_t *_blockNumber, void **_data,
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class Free : public AbstractTraceEntry {
|
|||||||
|
|
||||||
virtual void AddDump(char *buffer, size_t size)
|
virtual void AddDump(char *buffer, size_t size)
|
||||||
{
|
{
|
||||||
snprintf(buffer, size, "free %lu.%u.%u\n", fRun.AllocationGroup(),
|
snprintf(buffer, size, "free %lu.%u.%u", fRun.AllocationGroup(),
|
||||||
fRun.Start(), fRun.Length());
|
fRun.Start(), fRun.Length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class Create : public AbstractTraceEntry {
|
|||||||
virtual void AddDump(char *buffer, size_t size)
|
virtual void AddDump(char *buffer, size_t size)
|
||||||
{
|
{
|
||||||
snprintf(buffer, size, "CREATE %Ld (%p), parent %Ld (%p), \"%s\", "
|
snprintf(buffer, size, "CREATE %Ld (%p), parent %Ld (%p), \"%s\", "
|
||||||
"mode %lx, omode %x, type %lx\n", fID, fInode, fParentID,
|
"mode %lx, omode %x, type %lx", fID, fInode, fParentID,
|
||||||
fParent, fName, fMode, fOpenMode, fType);
|
fParent, fName, fMode, fOpenMode, fType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ class Resize : public AbstractTraceEntry {
|
|||||||
|
|
||||||
virtual void AddDump(char *buffer, size_t size)
|
virtual void AddDump(char *buffer, size_t size)
|
||||||
{
|
{
|
||||||
snprintf(buffer, size, "RESIZE %Ld (%p), %Ld -> %Ld\n", fID, fInode,
|
snprintf(buffer, size, "RESIZE %Ld (%p), %Ld -> %Ld", fID, fInode,
|
||||||
fOldSize, fNewSize);
|
fOldSize, fNewSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,11 +115,14 @@ class InodeAllocator {
|
|||||||
InodeAllocator(Transaction &transaction);
|
InodeAllocator(Transaction &transaction);
|
||||||
~InodeAllocator();
|
~InodeAllocator();
|
||||||
|
|
||||||
status_t New(block_run *parentRun, mode_t mode, block_run &run, Inode **_inode);
|
status_t New(block_run *parentRun, mode_t mode, block_run &run,
|
||||||
|
Inode **_inode);
|
||||||
status_t CreateTree();
|
status_t CreateTree();
|
||||||
status_t Keep();
|
status_t Keep();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static void _TransactionListener(int32 id, int32 event, void *_inode);
|
||||||
|
|
||||||
Transaction *fTransaction;
|
Transaction *fTransaction;
|
||||||
block_run fRun;
|
block_run fRun;
|
||||||
Inode *fInode;
|
Inode *fInode;
|
||||||
@@ -224,6 +227,11 @@ InodeAllocator::Keep()
|
|||||||
if (!fInode->IsSymLink() && volume->ID() >= 0)
|
if (!fInode->IsSymLink() && volume->ID() >= 0)
|
||||||
status = publish_vnode(volume->ID(), fInode->ID(), fInode);
|
status = publish_vnode(volume->ID(), fInode->ID(), fInode);
|
||||||
|
|
||||||
|
if (status == B_OK) {
|
||||||
|
cache_add_transaction_listener(volume->BlockCache(), fTransaction->ID(),
|
||||||
|
&_TransactionListener, fInode);
|
||||||
|
}
|
||||||
|
|
||||||
fTransaction = NULL;
|
fTransaction = NULL;
|
||||||
fInode = NULL;
|
fInode = NULL;
|
||||||
|
|
||||||
@@ -231,6 +239,16 @@ InodeAllocator::Keep()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ void
|
||||||
|
InodeAllocator::_TransactionListener(int32 id, int32 event, void *_inode)
|
||||||
|
{
|
||||||
|
Inode *inode = (Inode *)_inode;
|
||||||
|
|
||||||
|
if (event == TRANSACTION_ABORTED)
|
||||||
|
panic("transaction %d aborted, inode %p still around!\n", (int)id, inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -451,10 +451,13 @@ Journal::ReplayLog()
|
|||||||
able to update the log start pointer.
|
able to update the log start pointer.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
Journal::_blockNotify(int32 transactionID, void *arg)
|
Journal::_BlockNotify(int32 transactionID, int32 event, void *arg)
|
||||||
{
|
{
|
||||||
LogEntry *logEntry = (LogEntry *)arg;
|
LogEntry *logEntry = (LogEntry *)arg;
|
||||||
|
|
||||||
|
if (event != TRANSACTION_WRITTEN)
|
||||||
|
return;
|
||||||
|
|
||||||
PRINT(("Log entry %p has been finished, transaction ID = %ld\n", logEntry, transactionID));
|
PRINT(("Log entry %p has been finished, transaction ID = %ld\n", logEntry, transactionID));
|
||||||
|
|
||||||
Journal *journal = logEntry->GetJournal();
|
Journal *journal = logEntry->GetJournal();
|
||||||
@@ -495,7 +498,7 @@ Journal::_blockNotify(int32 transactionID, void *arg)
|
|||||||
|
|
||||||
status_t status = journal->fVolume->WriteSuperBlock();
|
status_t status = journal->fVolume->WriteSuperBlock();
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
FATAL(("blockNotify: could not write back super block: %s\n",
|
FATAL(("_BlockNotify: could not write back super block: %s\n",
|
||||||
strerror(status)));
|
strerror(status)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -656,7 +659,7 @@ Journal::_WriteTransactionToLog()
|
|||||||
fUsed += logEntry->Length();
|
fUsed += logEntry->Length();
|
||||||
fEntriesLock.Unlock();
|
fEntriesLock.Unlock();
|
||||||
|
|
||||||
cache_end_transaction(fVolume->BlockCache(), fTransactionID, _blockNotify,
|
cache_end_transaction(fVolume->BlockCache(), fTransactionID, _BlockNotify,
|
||||||
logEntry);
|
logEntry);
|
||||||
|
|
||||||
// If the log goes to the next round (the log is written as a
|
// If the log goes to the next round (the log is written as a
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class Journal {
|
|||||||
status_t _CheckRunArray(const run_array *array);
|
status_t _CheckRunArray(const run_array *array);
|
||||||
status_t _ReplayRunArray(int32 *start);
|
status_t _ReplayRunArray(int32 *start);
|
||||||
status_t _TransactionDone(bool success);
|
status_t _TransactionDone(bool success);
|
||||||
static void _blockNotify(int32 transactionID, void *arg);
|
static void _BlockNotify(int32 transactionID, int32 event, void *arg);
|
||||||
|
|
||||||
Volume *fVolume;
|
Volume *fVolume;
|
||||||
RecursiveLock fLock;
|
RecursiveLock fLock;
|
||||||
|
|||||||
+141
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
* Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <block_cache.h>
|
#include <block_cache.h>
|
||||||
#include <lock.h>
|
#include <lock.h>
|
||||||
#include <vm_low_memory.h>
|
#include <vm_low_memory.h>
|
||||||
|
#include <tracing.h>
|
||||||
#include <util/kernel_cpp.h>
|
#include <util/kernel_cpp.h>
|
||||||
#include <util/DoublyLinkedList.h>
|
#include <util/DoublyLinkedList.h>
|
||||||
#include <util/AutoLock.h>
|
#include <util/AutoLock.h>
|
||||||
@@ -41,11 +42,64 @@
|
|||||||
|
|
||||||
#define DEBUG_BLOCK_CACHE
|
#define DEBUG_BLOCK_CACHE
|
||||||
//#define DEBUG_CHANGED
|
//#define DEBUG_CHANGED
|
||||||
|
//#define TRANSACTION_TRACING
|
||||||
|
|
||||||
// This macro is used for fatal situations that are acceptable in a running
|
// This macro is used for fatal situations that are acceptable in a running
|
||||||
// system, like out of memory situations - should only panic for debugging.
|
// system, like out of memory situations - should only panic for debugging.
|
||||||
#define FATAL(x) panic x
|
#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 {
|
struct cache_transaction {
|
||||||
cache_transaction();
|
cache_transaction();
|
||||||
|
|
||||||
@@ -57,6 +111,7 @@ struct cache_transaction {
|
|||||||
block_list blocks;
|
block_list blocks;
|
||||||
transaction_notification_hook notification_hook;
|
transaction_notification_hook notification_hook;
|
||||||
void *notification_data;
|
void *notification_data;
|
||||||
|
HookList listeners;
|
||||||
bool open;
|
bool open;
|
||||||
bool has_sub_transaction;
|
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
|
static void
|
||||||
delete_transaction(block_cache *cache, cache_transaction *transaction)
|
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));
|
TRACE(("cache transaction %ld finished!\n", previous->id));
|
||||||
|
|
||||||
if (previous->notification_hook != NULL) {
|
if (previous->notification_hook != NULL) {
|
||||||
previous->notification_hook(previous->id,
|
previous->notification_hook(previous->id, TRANSACTION_WRITTEN,
|
||||||
previous->notification_data);
|
previous->notification_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -900,6 +973,8 @@ cache_end_transaction(void *_cache, int32 id,
|
|||||||
transaction->notification_hook = hook;
|
transaction->notification_hook = hook;
|
||||||
transaction->notification_data = data;
|
transaction->notification_data = data;
|
||||||
|
|
||||||
|
notify_transaction_listeners(transaction, TRANSACTION_ENDED);
|
||||||
|
|
||||||
// iterate through all blocks and free the unchanged original contents
|
// iterate through all blocks and free the unchanged original contents
|
||||||
|
|
||||||
cached_block *block = transaction->first_block, *next;
|
cached_block *block = transaction->first_block, *next;
|
||||||
@@ -949,6 +1024,8 @@ cache_abort_transaction(void *_cache, int32 id)
|
|||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notify_transaction_listeners(transaction, TRANSACTION_ABORTED);
|
||||||
|
|
||||||
// iterate through all blocks and restore their original contents
|
// iterate through all blocks and restore their original contents
|
||||||
|
|
||||||
cached_block *block = transaction->first_block, *next;
|
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_hook = hook;
|
||||||
transaction->notification_data = data;
|
transaction->notification_data = data;
|
||||||
|
|
||||||
|
notify_transaction_listeners(transaction, TRANSACTION_ENDED);
|
||||||
|
|
||||||
// iterate through all blocks and free the unchanged original contents
|
// iterate through all blocks and free the unchanged original contents
|
||||||
|
|
||||||
cached_block *block = transaction->first_block, *next, *last = NULL;
|
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)
|
if (!transaction->has_sub_transaction)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
notify_transaction_listeners(transaction, TRANSACTION_ABORTED);
|
||||||
|
|
||||||
// revert all changes back to the version of the parent
|
// revert all changes back to the version of the parent
|
||||||
|
|
||||||
cached_block *block = transaction->first_block, *next;
|
cached_block *block = transaction->first_block, *next;
|
||||||
@@ -1118,6 +1199,8 @@ cache_start_sub_transaction(void *_cache, int32 id)
|
|||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notify_transaction_listeners(transaction, TRANSACTION_ENDED);
|
||||||
|
|
||||||
// move all changed blocks up to the parent
|
// move all changed blocks up to the parent
|
||||||
|
|
||||||
cached_block *block = transaction->first_block, *next;
|
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
|
extern "C" status_t
|
||||||
cache_next_block_in_transaction(void *_cache, int32 id, uint32 *_cookie,
|
cache_next_block_in_transaction(void *_cache, int32 id, uint32 *_cookie,
|
||||||
off_t *_blockNumber, void **_data, void **_unchangedData)
|
off_t *_blockNumber, void **_data, void **_unchangedData)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
* Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -45,6 +45,12 @@ namespace FSShell {
|
|||||||
|
|
||||||
static const int32_t kMaxBlockCount = 1024;
|
static const int32_t kMaxBlockCount = 1024;
|
||||||
|
|
||||||
|
struct cache_hook : DoublyLinkedListLinkImpl<cache_hook> {
|
||||||
|
fssh_transaction_notification_hook hook;
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef DoublyLinkedList<cache_hook> HookList;
|
||||||
|
|
||||||
struct cache_transaction {
|
struct cache_transaction {
|
||||||
cache_transaction();
|
cache_transaction();
|
||||||
@@ -57,6 +63,7 @@ struct cache_transaction {
|
|||||||
block_list blocks;
|
block_list blocks;
|
||||||
fssh_transaction_notification_hook notification_hook;
|
fssh_transaction_notification_hook notification_hook;
|
||||||
void *notification_data;
|
void *notification_data;
|
||||||
|
HookList listeners;
|
||||||
bool open;
|
bool open;
|
||||||
bool has_sub_transaction;
|
bool has_sub_transaction;
|
||||||
};
|
};
|
||||||
@@ -102,6 +109,24 @@ transaction_hash(void *_transaction, const void *_id, uint32_t range)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Notifies all listeners of this transaction, and removes them
|
||||||
|
afterwards.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
notify_transaction_listeners(cache_transaction *transaction, int32_t 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
|
static void
|
||||||
delete_transaction(block_cache *cache, cache_transaction *transaction)
|
delete_transaction(block_cache *cache, cache_transaction *transaction)
|
||||||
{
|
{
|
||||||
@@ -570,7 +595,7 @@ write_cached_block(block_cache *cache, cached_block *block,
|
|||||||
|
|
||||||
if (previous->notification_hook != NULL) {
|
if (previous->notification_hook != NULL) {
|
||||||
previous->notification_hook(previous->id,
|
previous->notification_hook(previous->id,
|
||||||
previous->notification_data);
|
FSSH_TRANSACTION_WRITTEN, previous->notification_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleteTransaction) {
|
if (deleteTransaction) {
|
||||||
@@ -678,6 +703,8 @@ fssh_cache_end_transaction(void *_cache, int32_t id,
|
|||||||
transaction->notification_hook = hook;
|
transaction->notification_hook = hook;
|
||||||
transaction->notification_data = data;
|
transaction->notification_data = data;
|
||||||
|
|
||||||
|
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ENDED);
|
||||||
|
|
||||||
// iterate through all blocks and free the unchanged original contents
|
// iterate through all blocks and free the unchanged original contents
|
||||||
|
|
||||||
cached_block *block = transaction->first_block, *next;
|
cached_block *block = transaction->first_block, *next;
|
||||||
@@ -727,6 +754,8 @@ fssh_cache_abort_transaction(void *_cache, int32_t id)
|
|||||||
return FSSH_B_BAD_VALUE;
|
return FSSH_B_BAD_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ABORTED);
|
||||||
|
|
||||||
// iterate through all blocks and restore their original contents
|
// iterate through all blocks and restore their original contents
|
||||||
|
|
||||||
cached_block *block = transaction->first_block, *next;
|
cached_block *block = transaction->first_block, *next;
|
||||||
@@ -788,6 +817,8 @@ fssh_cache_detach_sub_transaction(void *_cache, int32_t id,
|
|||||||
transaction->notification_hook = hook;
|
transaction->notification_hook = hook;
|
||||||
transaction->notification_data = data;
|
transaction->notification_data = data;
|
||||||
|
|
||||||
|
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ENDED);
|
||||||
|
|
||||||
// iterate through all blocks and free the unchanged original contents
|
// iterate through all blocks and free the unchanged original contents
|
||||||
|
|
||||||
cached_block *block = transaction->first_block, *next, *last = NULL;
|
cached_block *block = transaction->first_block, *next, *last = NULL;
|
||||||
@@ -852,6 +883,8 @@ fssh_cache_abort_sub_transaction(void *_cache, int32_t id)
|
|||||||
if (!transaction->has_sub_transaction)
|
if (!transaction->has_sub_transaction)
|
||||||
return FSSH_B_BAD_VALUE;
|
return FSSH_B_BAD_VALUE;
|
||||||
|
|
||||||
|
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ABORTED);
|
||||||
|
|
||||||
// revert all changes back to the version of the parent
|
// revert all changes back to the version of the parent
|
||||||
|
|
||||||
cached_block *block = transaction->first_block, *next;
|
cached_block *block = transaction->first_block, *next;
|
||||||
@@ -896,6 +929,8 @@ fssh_cache_start_sub_transaction(void *_cache, int32_t id)
|
|||||||
return FSSH_B_BAD_VALUE;
|
return FSSH_B_BAD_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ENDED);
|
||||||
|
|
||||||
// move all changed blocks up to the parent
|
// move all changed blocks up to the parent
|
||||||
|
|
||||||
cached_block *block = transaction->first_block, *next;
|
cached_block *block = transaction->first_block, *next;
|
||||||
@@ -923,6 +958,62 @@ fssh_cache_start_sub_transaction(void *_cache, int32_t id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Adds a transaction listener that gets notified when the transaction
|
||||||
|
is ended or aborted.
|
||||||
|
The listener gets automatically removed in this case.
|
||||||
|
*/
|
||||||
|
fssh_status_t
|
||||||
|
fssh_cache_add_transaction_listener(void *_cache, int32_t id,
|
||||||
|
fssh_transaction_notification_hook hookFunction, void *data)
|
||||||
|
{
|
||||||
|
block_cache *cache = (block_cache *)_cache;
|
||||||
|
|
||||||
|
cache_hook *hook = new(std::nothrow) cache_hook;
|
||||||
|
if (hook == NULL)
|
||||||
|
return FSSH_B_NO_MEMORY;
|
||||||
|
|
||||||
|
BenaphoreLocker locker(&cache->lock);
|
||||||
|
|
||||||
|
cache_transaction *transaction = lookup_transaction(cache, id);
|
||||||
|
if (transaction == NULL) {
|
||||||
|
delete hook;
|
||||||
|
return FSSH_B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
hook->hook = hookFunction;
|
||||||
|
hook->data = data;
|
||||||
|
|
||||||
|
transaction->listeners.Add(hook);
|
||||||
|
return FSSH_B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fssh_status_t
|
||||||
|
fssh_cache_remove_transaction_listener(void *_cache, int32_t id,
|
||||||
|
fssh_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 FSSH_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 FSSH_B_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FSSH_B_ENTRY_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fssh_status_t
|
fssh_status_t
|
||||||
fssh_cache_next_block_in_transaction(void *_cache, int32_t id, uint32_t *_cookie,
|
fssh_cache_next_block_in_transaction(void *_cache, int32_t id, uint32_t *_cookie,
|
||||||
fssh_off_t *_blockNumber, void **_data, void **_unchangedData)
|
fssh_off_t *_blockNumber, void **_data, void **_unchangedData)
|
||||||
|
|||||||
Reference in New Issue
Block a user