* 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:
parent
5276dad057
commit
4fc4f2c8ae
@ -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.
|
||||
*/
|
||||
#ifndef _FS_CACHE_H
|
||||
@ -11,7 +11,14 @@
|
||||
#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
|
||||
extern "C" {
|
||||
@ -27,6 +34,10 @@ extern int32 cache_detach_sub_transaction(void *_cache, int32 id,
|
||||
transaction_notification_hook hook, void *data);
|
||||
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_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,
|
||||
uint32 *_cookie, off_t *_blockNumber, void **_data,
|
||||
void **_unchangedData);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de.
|
||||
* Copyright 2007-2008, Ingo Weinhold, bonefish@cs.tu-berlin.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _FSSH_API_WRAPPER_H
|
||||
@ -787,6 +787,10 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// #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
|
||||
|
||||
/* transactions */
|
||||
@ -797,6 +801,8 @@
|
||||
#define cache_detach_sub_transaction fssh_cache_detach_sub_transaction
|
||||
#define cache_abort_sub_transaction fssh_cache_abort_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_blocks_in_transaction fssh_cache_blocks_in_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.
|
||||
*/
|
||||
#ifndef _FSSH_FS_CACHE_H
|
||||
@ -11,7 +11,14 @@
|
||||
#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
|
||||
extern "C" {
|
||||
@ -31,6 +38,12 @@ extern fssh_status_t fssh_cache_abort_sub_transaction(void *_cache,
|
||||
int32_t id);
|
||||
extern fssh_status_t fssh_cache_start_sub_transaction(void *_cache,
|
||||
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,
|
||||
int32_t id, uint32_t *_cookie,
|
||||
fssh_off_t *_blockNumber, void **_data,
|
||||
|
@ -69,7 +69,7 @@ class Free : public AbstractTraceEntry {
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ class Create : public AbstractTraceEntry {
|
||||
virtual void AddDump(char *buffer, size_t size)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ class Resize : public AbstractTraceEntry {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -115,11 +115,14 @@ class InodeAllocator {
|
||||
InodeAllocator(Transaction &transaction);
|
||||
~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 Keep();
|
||||
|
||||
private:
|
||||
static void _TransactionListener(int32 id, int32 event, void *_inode);
|
||||
|
||||
Transaction *fTransaction;
|
||||
block_run fRun;
|
||||
Inode *fInode;
|
||||
@ -224,6 +227,11 @@ InodeAllocator::Keep()
|
||||
if (!fInode->IsSymLink() && volume->ID() >= 0)
|
||||
status = publish_vnode(volume->ID(), fInode->ID(), fInode);
|
||||
|
||||
if (status == B_OK) {
|
||||
cache_add_transaction_listener(volume->BlockCache(), fTransaction->ID(),
|
||||
&_TransactionListener, fInode);
|
||||
}
|
||||
|
||||
fTransaction = 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 -
|
||||
|
||||
|
||||
|
@ -451,10 +451,13 @@ Journal::ReplayLog()
|
||||
able to update the log start pointer.
|
||||
*/
|
||||
void
|
||||
Journal::_blockNotify(int32 transactionID, void *arg)
|
||||
Journal::_BlockNotify(int32 transactionID, int32 event, void *arg)
|
||||
{
|
||||
LogEntry *logEntry = (LogEntry *)arg;
|
||||
|
||||
if (event != TRANSACTION_WRITTEN)
|
||||
return;
|
||||
|
||||
PRINT(("Log entry %p has been finished, transaction ID = %ld\n", logEntry, transactionID));
|
||||
|
||||
Journal *journal = logEntry->GetJournal();
|
||||
@ -495,7 +498,7 @@ Journal::_blockNotify(int32 transactionID, void *arg)
|
||||
|
||||
status_t status = journal->fVolume->WriteSuperBlock();
|
||||
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)));
|
||||
}
|
||||
}
|
||||
@ -656,7 +659,7 @@ Journal::_WriteTransactionToLog()
|
||||
fUsed += logEntry->Length();
|
||||
fEntriesLock.Unlock();
|
||||
|
||||
cache_end_transaction(fVolume->BlockCache(), fTransactionID, _blockNotify,
|
||||
cache_end_transaction(fVolume->BlockCache(), fTransactionID, _BlockNotify,
|
||||
logEntry);
|
||||
|
||||
// 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 _ReplayRunArray(int32 *start);
|
||||
status_t _TransactionDone(bool success);
|
||||
static void _blockNotify(int32 transactionID, void *arg);
|
||||
static void _BlockNotify(int32 transactionID, int32 event, void *arg);
|
||||
|
||||
Volume *fVolume;
|
||||
RecursiveLock fLock;
|
||||
|
143
src/system/kernel/cache/block_cache.cpp
vendored
143
src/system/kernel/cache/block_cache.cpp
vendored
@ -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.
|
||||
*/
|
||||
|
||||
@ -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)
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
@ -45,6 +45,12 @@ namespace FSShell {
|
||||
|
||||
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 {
|
||||
cache_transaction();
|
||||
@ -57,6 +63,7 @@ struct cache_transaction {
|
||||
block_list blocks;
|
||||
fssh_transaction_notification_hook notification_hook;
|
||||
void *notification_data;
|
||||
HookList listeners;
|
||||
bool open;
|
||||
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
|
||||
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) {
|
||||
previous->notification_hook(previous->id,
|
||||
previous->notification_data);
|
||||
FSSH_TRANSACTION_WRITTEN, previous->notification_data);
|
||||
}
|
||||
|
||||
if (deleteTransaction) {
|
||||
@ -678,6 +703,8 @@ fssh_cache_end_transaction(void *_cache, int32_t id,
|
||||
transaction->notification_hook = hook;
|
||||
transaction->notification_data = data;
|
||||
|
||||
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ENDED);
|
||||
|
||||
// iterate through all blocks and free the unchanged original contents
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ABORTED);
|
||||
|
||||
// iterate through all blocks and restore their original contents
|
||||
|
||||
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_data = data;
|
||||
|
||||
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ENDED);
|
||||
|
||||
// iterate through all blocks and free the unchanged original contents
|
||||
|
||||
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)
|
||||
return FSSH_B_BAD_VALUE;
|
||||
|
||||
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ABORTED);
|
||||
|
||||
// revert all changes back to the version of the parent
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
notify_transaction_listeners(transaction, FSSH_TRANSACTION_ENDED);
|
||||
|
||||
// move all changed blocks up to the parent
|
||||
|
||||
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_cache_next_block_in_transaction(void *_cache, int32_t id, uint32_t *_cookie,
|
||||
fssh_off_t *_blockNumber, void **_data, void **_unchangedData)
|
||||
|
Loading…
Reference in New Issue
Block a user