* notify_transaction_listeners() would always remove all listeners when it

was asked to. Unfortunately, it also removed the TRANSACTION_WRITTEN
  listeners (that were added before notifying other listeners), so that BFS
  could never free up space in the log. Now, it will only remove the listeners
  it may remove. This fixes bug #2024.
* Rearranged functions a bit, moved notification/listener related private
  functions together.
* Added tracing entry for written transactions as well.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24853 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2008-04-07 10:39:01 +00:00
parent 797a92d84d
commit 391fdf1e99
+122 -98
View File
@@ -293,40 +293,20 @@ static DoublyLinkedListLink<block_cache> sMarkCache;
static object_cache *sBlockCache;
// #pragma mark - private transaction
// #pragma mark - notifications/listener
cache_transaction::cache_transaction()
static inline bool
is_closing_event(int32 event)
{
num_blocks = 0;
main_num_blocks = 0;
sub_num_blocks = 0;
first_block = NULL;
open = true;
last_used = system_time();
return (event & (TRANSACTION_ABORTED | TRANSACTION_ENDED)) != 0;
}
static int
transaction_compare(void *_transaction, const void *_id)
static inline bool
is_written_event(int32 event)
{
cache_transaction *transaction = (cache_transaction *)_transaction;
const int32 *id = (const int32 *)_id;
return transaction->id - *id;
}
static uint32
transaction_hash(void *_transaction, const void *_id, uint32 range)
{
cache_transaction *transaction = (cache_transaction *)_transaction;
const int32 *id = (const int32 *)_id;
if (transaction != NULL)
return transaction->id % range;
return (uint32)*id % range;
return (event & TRANSACTION_WRITTEN) != 0;
}
@@ -399,25 +379,31 @@ add_notification(block_cache *cache, cache_notification *notification,
/*! Notifies all interested listeners of this transaction about the \a event.
If requested via \a removeListeners, the listeners will be removed
afterwards.
If \a event is a closing event (ie. TRANSACTION_ENDED, and
TRANSACTION_ABORTED), all listeners except those listening to
TRANSACTION_WRITTEN will be removed.
*/
static void
notify_transaction_listeners(block_cache *cache, cache_transaction *transaction,
int32 event, bool removeListeners)
int32 event)
{
T(Action("notify", cache, transaction));
bool isClosing = is_closing_event(event);
bool isWritten = is_written_event(event);
ListenerList::Iterator iterator = transaction->listeners.GetIterator();
while (iterator.HasNext()) {
cache_listener *listener = iterator.Next();
if (removeListeners)
bool remove = isClosing && !is_written_event(listener->events)
|| isWritten && is_written_event(listener->events);
if (remove)
iterator.Remove();
if ((listener->events & event) != 0)
add_notification(cache, listener, event, removeListeners);
else if (removeListeners) {
add_notification(cache, listener, event, remove);
else if (remove) {
// we might need to defer the deletion if its currently in use
MutexLocker locker(sNotificationsLock);
if (listener->events_pending != 0)
@@ -429,6 +415,59 @@ notify_transaction_listeners(block_cache *cache, cache_transaction *transaction,
}
static void
flush_pending_notifications(block_cache *cache)
{
while (true) {
MutexLocker locker(sNotificationsLock);
cache_notification *notification = cache->pending_notifications.Head();
if (notification == NULL)
return;
bool deleteAfterEvent = false;
int32 event = -1;
if (!get_next_pending_event(notification, &event)) {
// remove the notification if this was the last pending event
cache->pending_notifications.Remove(notification);
deleteAfterEvent = notification->delete_after_event;
}
if (event >= 0) {
// Notify listener, we need to copy the notification, as it might
// be removed when we unlock the list.
cache_notification copy = *notification;
locker.Unlock();
copy.hook(copy.transaction_id, event, copy.data);
locker.Lock();
}
if (deleteAfterEvent)
delete notification;
}
}
/*! Flushes all pending notifications by calling the appropriate hook
functions.
Must not be called with a cache lock held.
*/
static void
flush_pending_notifications()
{
MutexLocker _(sCachesLock);
DoublyLinkedList<block_cache>::Iterator iterator = sCaches.GetIterator();
while (iterator.HasNext()) {
block_cache *cache = iterator.Next();
flush_pending_notifications(cache);
}
}
static void
remove_transaction_listeners(block_cache *cache, cache_transaction *transaction)
{
@@ -477,6 +516,43 @@ add_transaction_listener(block_cache *cache, cache_transaction *transaction,
}
// #pragma mark - private transaction
cache_transaction::cache_transaction()
{
num_blocks = 0;
main_num_blocks = 0;
sub_num_blocks = 0;
first_block = NULL;
open = true;
last_used = system_time();
}
static int
transaction_compare(void *_transaction, const void *_id)
{
cache_transaction *transaction = (cache_transaction *)_transaction;
const int32 *id = (const int32 *)_id;
return transaction->id - *id;
}
static uint32
transaction_hash(void *_transaction, const void *_id, uint32 range)
{
cache_transaction *transaction = (cache_transaction *)_transaction;
const int32 *id = (const int32 *)_id;
if (transaction != NULL)
return transaction->id % range;
return (uint32)*id % range;
}
static void
delete_transaction(block_cache *cache, cache_transaction *transaction)
{
@@ -1081,9 +1157,9 @@ write_cached_block(block_cache *cache, cached_block *block,
// Has the previous transation been finished with that write?
if (--previous->num_blocks == 0) {
TRACE(("cache transaction %ld finished!\n", previous->id));
T(Action("written", cache, previous));
notify_transaction_listeners(cache, previous, TRANSACTION_WRITTEN,
true);
notify_transaction_listeners(cache, previous, TRANSACTION_WRITTEN);
if (deleteTransaction) {
hash_remove(cache->transaction_hash, previous);
@@ -1396,59 +1472,6 @@ get_next_locked_block_cache(block_cache *last)
}
static void
flush_pending_notifications(block_cache *cache)
{
while (true) {
MutexLocker locker(sNotificationsLock);
cache_notification *notification = cache->pending_notifications.Head();
if (notification == NULL)
return;
bool deleteAfterEvent = false;
int32 event = -1;
if (!get_next_pending_event(notification, &event)) {
// remove the notification if this was the last pending event
cache->pending_notifications.Remove(notification);
deleteAfterEvent = notification->delete_after_event;
}
if (event >= 0) {
// Notify listener, we need to copy the notification, as it might
// be removed when we unlock the list.
cache_notification copy = *notification;
locker.Unlock();
copy.hook(copy.transaction_id, event, copy.data);
locker.Lock();
}
if (deleteAfterEvent)
delete notification;
}
}
/*! Flushes all pending notifications by calling the appropriate hook
functions.
Must not be called with a cache lock held.
*/
static void
flush_pending_notifications()
{
MutexLocker _(sCachesLock);
DoublyLinkedList<block_cache>::Iterator iterator = sCaches.GetIterator();
while (iterator.HasNext()) {
block_cache *cache = iterator.Next();
flush_pending_notifications(cache);
}
}
static status_t
block_notifier_and_writer(void *)
{
@@ -1504,7 +1527,7 @@ block_notifier_and_writer(void *)
+ kTransactionIdleTime) {
// Transaction is open but idle
notify_transaction_listeners(cache, transaction,
TRANSACTION_IDLE, false);
TRANSACTION_IDLE);
}
continue;
}
@@ -1720,6 +1743,8 @@ cache_end_transaction(void *_cache, int32 id,
return B_BAD_VALUE;
}
notify_transaction_listeners(cache, transaction, TRANSACTION_ENDED);
if (add_transaction_listener(cache, transaction, TRANSACTION_WRITTEN, hook,
data) != B_OK) {
return B_NO_MEMORY;
@@ -1757,8 +1782,6 @@ cache_end_transaction(void *_cache, int32 id,
}
transaction->open = false;
notify_transaction_listeners(cache, transaction, TRANSACTION_ENDED, true);
return B_OK;
}
@@ -1778,7 +1801,7 @@ cache_abort_transaction(void *_cache, int32 id)
}
T(Abort(cache, transaction));
notify_transaction_listeners(cache, transaction, TRANSACTION_ABORTED, true);
notify_transaction_listeners(cache, transaction, TRANSACTION_ABORTED);
// iterate through all blocks and restore their original contents
@@ -1835,15 +1858,17 @@ cache_detach_sub_transaction(void *_cache, int32 id,
if (transaction == NULL)
return B_NO_MEMORY;
newTransaction->id = atomic_add(&cache->next_transaction_id, 1);
T(Detach(cache, transaction, newTransaction));
notify_transaction_listeners(cache, transaction, TRANSACTION_ENDED);
if (add_transaction_listener(cache, transaction, TRANSACTION_WRITTEN, hook,
data) != B_OK) {
delete newTransaction;
return B_NO_MEMORY;
}
newTransaction->id = atomic_add(&cache->next_transaction_id, 1);
T(Detach(cache, transaction, newTransaction));
// iterate through all blocks and free the unchanged original contents
cached_block *block = transaction->first_block, *next, *last = NULL;
@@ -1892,7 +1917,6 @@ cache_detach_sub_transaction(void *_cache, int32 id,
transaction->has_sub_transaction = false;
transaction->num_blocks = transaction->main_num_blocks;
transaction->sub_num_blocks = 0;
notify_transaction_listeners(cache, transaction, TRANSACTION_ENDED, true);
hash_insert_grow(cache->transaction_hash, newTransaction);
cache->last_transaction = newTransaction;
@@ -1918,7 +1942,7 @@ cache_abort_sub_transaction(void *_cache, int32 id)
return B_BAD_VALUE;
T(Abort(cache, transaction));
notify_transaction_listeners(cache, transaction, TRANSACTION_ABORTED, true);
notify_transaction_listeners(cache, transaction, TRANSACTION_ABORTED);
// revert all changes back to the version of the parent
@@ -1966,7 +1990,7 @@ cache_start_sub_transaction(void *_cache, int32 id)
return B_BAD_VALUE;
}
notify_transaction_listeners(cache, transaction, TRANSACTION_ENDED, true);
notify_transaction_listeners(cache, transaction, TRANSACTION_ENDED);
// move all changed blocks up to the parent