* Changed the way how a cache is destructed: now, it is removed from the
global cache list before locking it. This allows to get rid of the block_cache::deleting field, as well as simplifies some code. * This also fixes a possible deadlock I recently introduced (on destruction, the locking order was wrong). * Now uses an anonymous condition variable instead. * Moved the block_cache initialization code into a dedicated method that will now also fail in case the low resource handler couldn't be registered (as pointed out by Salvatore). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27074 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+37
-61
@@ -115,12 +115,11 @@ struct block_cache : DoublyLinkedListLinkImpl<block_cache> {
|
|||||||
|
|
||||||
NotificationList pending_notifications;
|
NotificationList pending_notifications;
|
||||||
ConditionVariable condition_variable;
|
ConditionVariable condition_variable;
|
||||||
bool deleting;
|
|
||||||
|
|
||||||
block_cache(int fd, off_t numBlocks, size_t blockSize, bool readOnly);
|
block_cache(int fd, off_t numBlocks, size_t blockSize, bool readOnly);
|
||||||
~block_cache();
|
~block_cache();
|
||||||
|
|
||||||
status_t InitCheck();
|
status_t Init();
|
||||||
|
|
||||||
void RemoveUnusedBlocks(int32 maxAccessed = LONG_MAX,
|
void RemoveUnusedBlocks(int32 maxAccessed = LONG_MAX,
|
||||||
int32 count = LONG_MAX);
|
int32 count = LONG_MAX);
|
||||||
@@ -767,68 +766,50 @@ block_cache::block_cache(int _fd, off_t numBlocks, size_t blockSize,
|
|||||||
next_transaction_id(1),
|
next_transaction_id(1),
|
||||||
last_transaction(NULL),
|
last_transaction(NULL),
|
||||||
transaction_hash(NULL),
|
transaction_hash(NULL),
|
||||||
|
buffer_cache(NULL),
|
||||||
num_dirty_blocks(0),
|
num_dirty_blocks(0),
|
||||||
read_only(readOnly),
|
read_only(readOnly)
|
||||||
deleting(false)
|
|
||||||
{
|
{
|
||||||
condition_variable.Publish(this, "cache transaction sync");
|
|
||||||
|
|
||||||
buffer_cache = create_object_cache_etc("block cache buffers", blockSize,
|
|
||||||
8, 0, CACHE_LARGE_SLAB, NULL, NULL, NULL, NULL);
|
|
||||||
if (buffer_cache == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
hash = hash_init(1024, offsetof(cached_block, next), &cached_block::Compare,
|
|
||||||
&cached_block::Hash);
|
|
||||||
if (hash == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
transaction_hash = hash_init(16, offsetof(cache_transaction, next),
|
|
||||||
&transaction_compare, &::transaction_hash);
|
|
||||||
if (transaction_hash == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
mutex_init(&lock, "block cache");
|
|
||||||
|
|
||||||
register_low_resource_handler(&block_cache::LowMemoryHandler, this,
|
|
||||||
B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY, 0);
|
|
||||||
|
|
||||||
MutexLocker _(sCachesLock);
|
|
||||||
sCaches.Add(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Should be called with the cache's lock held. */
|
/*! Should be called with the cache's lock held. */
|
||||||
block_cache::~block_cache()
|
block_cache::~block_cache()
|
||||||
{
|
{
|
||||||
deleting = true;
|
unregister_low_resource_handler(&block_cache::LowMemoryHandler, this);
|
||||||
|
|
||||||
if (InitCheck() == B_OK) {
|
|
||||||
mutex_lock(&sCachesLock);
|
|
||||||
sCaches.Remove(this);
|
|
||||||
mutex_unlock(&sCachesLock);
|
|
||||||
|
|
||||||
unregister_low_resource_handler(&block_cache::LowMemoryHandler, this);
|
|
||||||
|
|
||||||
mutex_destroy(&lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
condition_variable.Unpublish();
|
|
||||||
|
|
||||||
hash_uninit(transaction_hash);
|
hash_uninit(transaction_hash);
|
||||||
hash_uninit(hash);
|
hash_uninit(hash);
|
||||||
|
|
||||||
delete_object_cache(buffer_cache);
|
delete_object_cache(buffer_cache);
|
||||||
|
|
||||||
|
mutex_destroy(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
block_cache::InitCheck()
|
block_cache::Init()
|
||||||
{
|
{
|
||||||
if (buffer_cache == NULL || hash == NULL || transaction_hash == NULL)
|
condition_variable.Init(this, "cache transaction sync");
|
||||||
|
mutex_init(&lock, "block cache");
|
||||||
|
|
||||||
|
buffer_cache = create_object_cache_etc("block cache buffers", block_size,
|
||||||
|
8, 0, CACHE_LARGE_SLAB, NULL, NULL, NULL, NULL);
|
||||||
|
if (buffer_cache == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
return B_OK;
|
hash = hash_init(1024, offsetof(cached_block, next), &cached_block::Compare,
|
||||||
|
&cached_block::Hash);
|
||||||
|
if (hash == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
transaction_hash = hash_init(16, offsetof(cache_transaction, next),
|
||||||
|
&transaction_compare, &::transaction_hash);
|
||||||
|
if (transaction_hash == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
return register_low_resource_handler(&block_cache::LowMemoryHandler, this,
|
||||||
|
B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1617,23 +1598,11 @@ get_next_locked_block_cache(block_cache *last)
|
|||||||
cache = sCaches.Head();
|
cache = sCaches.Head();
|
||||||
|
|
||||||
while (cache != NULL) {
|
while (cache != NULL) {
|
||||||
while (cache != NULL && cache->deleting) {
|
cache = sCaches.GetNext(cache);
|
||||||
cache = sCaches.GetNext(cache);
|
|
||||||
}
|
|
||||||
if (cache == NULL)
|
if (cache == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
status_t status = mutex_lock(&cache->lock);
|
mutex_lock(&cache->lock);
|
||||||
if (status != B_OK) {
|
|
||||||
// can only happen if the cache is being deleted right now
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cache->deleting) {
|
|
||||||
mutex_unlock(&cache->lock);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1649,7 +1618,7 @@ get_next_locked_block_cache(block_cache *last)
|
|||||||
Every two seconds, it will also write back up to 64 blocks per cache.
|
Every two seconds, it will also write back up to 64 blocks per cache.
|
||||||
*/
|
*/
|
||||||
static status_t
|
static status_t
|
||||||
block_notifier_and_writer(void *)
|
block_notifier_and_writer(void */*data*/)
|
||||||
{
|
{
|
||||||
const bigtime_t kTimeout = 2000000LL;
|
const bigtime_t kTimeout = 2000000LL;
|
||||||
bigtime_t timeout = kTimeout;
|
bigtime_t timeout = kTimeout;
|
||||||
@@ -1762,7 +1731,7 @@ wait_for_notifications(block_cache *cache)
|
|||||||
cache);
|
cache);
|
||||||
|
|
||||||
ConditionVariableEntry entry;
|
ConditionVariableEntry entry;
|
||||||
entry.Add(cache);
|
cache->condition_variable.Add(&entry);
|
||||||
|
|
||||||
add_notification(cache, ¬ification, TRANSACTION_WRITTEN, false);
|
add_notification(cache, ¬ification, TRANSACTION_WRITTEN, false);
|
||||||
|
|
||||||
@@ -2364,6 +2333,10 @@ block_cache_delete(void *_cache, bool allowWrites)
|
|||||||
if (allowWrites)
|
if (allowWrites)
|
||||||
block_cache_sync(cache);
|
block_cache_sync(cache);
|
||||||
|
|
||||||
|
mutex_lock(&sCachesLock);
|
||||||
|
sCaches.Remove(cache);
|
||||||
|
mutex_unlock(&sCachesLock);
|
||||||
|
|
||||||
mutex_lock(&cache->lock);
|
mutex_lock(&cache->lock);
|
||||||
|
|
||||||
// free all blocks
|
// free all blocks
|
||||||
@@ -2396,11 +2369,14 @@ block_cache_create(int fd, off_t numBlocks, size_t blockSize, bool readOnly)
|
|||||||
if (cache == NULL)
|
if (cache == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (cache->InitCheck() != B_OK) {
|
if (cache->Init() != B_OK) {
|
||||||
delete cache;
|
delete cache;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MutexLocker _(sCachesLock);
|
||||||
|
sCaches.Add(cache);
|
||||||
|
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user