Moved the three address space locker classes into a separate pair of

header/source files.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34451 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-12-02 20:16:31 +00:00
parent f34a1dd5d7
commit def9898c9b
4 changed files with 689 additions and 637 deletions
+1
View File
@@ -9,6 +9,7 @@ KernelMergeObject kernel_vm.o :
vm_daemons.cpp
vm_page.cpp
VMAddressSpace.cpp
VMAddressSpaceLocking.cpp
VMAnonymousCache.cpp
VMAnonymousNoSwapCache.cpp
VMArea.cpp
@@ -0,0 +1,549 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Copyright 2002-2009, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "VMAddressSpaceLocking.h"
#include <AutoDeleter.h>
#include <vm/vm.h>
#include <vm/VMAddressSpace.h>
#include <vm/VMArea.h>
#include <vm/VMCache.h>
// #pragma mark - AddressSpaceLockerBase
/*static*/ VMAddressSpace*
AddressSpaceLockerBase::GetAddressSpaceByAreaID(area_id id)
{
VMAddressSpace* addressSpace = NULL;
VMAreaHash::ReadLock();
VMArea* area = VMAreaHash::LookupLocked(id);
if (area != NULL) {
addressSpace = area->address_space;
addressSpace->Get();
}
VMAreaHash::ReadUnlock();
return addressSpace;
}
// #pragma mark - AddressSpaceReadLocker
AddressSpaceReadLocker::AddressSpaceReadLocker(team_id team)
:
fSpace(NULL),
fLocked(false)
{
SetTo(team);
}
/*! Takes over the reference of the address space, if \a getNewReference is
\c false.
*/
AddressSpaceReadLocker::AddressSpaceReadLocker(VMAddressSpace* space,
bool getNewReference)
:
fSpace(NULL),
fLocked(false)
{
SetTo(space, getNewReference);
}
AddressSpaceReadLocker::AddressSpaceReadLocker()
:
fSpace(NULL),
fLocked(false)
{
}
AddressSpaceReadLocker::~AddressSpaceReadLocker()
{
Unset();
}
void
AddressSpaceReadLocker::Unset()
{
Unlock();
if (fSpace != NULL)
fSpace->Put();
}
status_t
AddressSpaceReadLocker::SetTo(team_id team)
{
fSpace = VMAddressSpace::Get(team);
if (fSpace == NULL)
return B_BAD_TEAM_ID;
fSpace->ReadLock();
fLocked = true;
return B_OK;
}
/*! Takes over the reference of the address space, if \a getNewReference is
\c false.
*/
void
AddressSpaceReadLocker::SetTo(VMAddressSpace* space, bool getNewReference)
{
fSpace = space;
if (getNewReference)
fSpace->Get();
fSpace->ReadLock();
fLocked = true;
}
status_t
AddressSpaceReadLocker::SetFromArea(area_id areaID, VMArea*& area)
{
fSpace = GetAddressSpaceByAreaID(areaID);
if (fSpace == NULL)
return B_BAD_TEAM_ID;
fSpace->ReadLock();
area = VMAreaHash::Lookup(areaID);
if (area == NULL || area->address_space != fSpace) {
fSpace->ReadUnlock();
return B_BAD_VALUE;
}
fLocked = true;
return B_OK;
}
bool
AddressSpaceReadLocker::Lock()
{
if (fLocked)
return true;
if (fSpace == NULL)
return false;
fSpace->ReadLock();
fLocked = true;
return true;
}
void
AddressSpaceReadLocker::Unlock()
{
if (fLocked) {
fSpace->ReadUnlock();
fLocked = false;
}
}
// #pragma mark - AddressSpaceWriteLocker
AddressSpaceWriteLocker::AddressSpaceWriteLocker(team_id team)
:
fSpace(NULL),
fLocked(false),
fDegraded(false)
{
SetTo(team);
}
AddressSpaceWriteLocker::AddressSpaceWriteLocker()
:
fSpace(NULL),
fLocked(false),
fDegraded(false)
{
}
AddressSpaceWriteLocker::~AddressSpaceWriteLocker()
{
Unset();
}
void
AddressSpaceWriteLocker::Unset()
{
Unlock();
if (fSpace != NULL)
fSpace->Put();
}
status_t
AddressSpaceWriteLocker::SetTo(team_id team)
{
fSpace = VMAddressSpace::Get(team);
if (fSpace == NULL)
return B_BAD_TEAM_ID;
fSpace->WriteLock();
fLocked = true;
return B_OK;
}
status_t
AddressSpaceWriteLocker::SetFromArea(area_id areaID, VMArea*& area)
{
fSpace = GetAddressSpaceByAreaID(areaID);
if (fSpace == NULL)
return B_BAD_VALUE;
fSpace->WriteLock();
area = VMAreaHash::Lookup(areaID);
if (area == NULL || area->address_space != fSpace) {
fSpace->WriteUnlock();
return B_BAD_VALUE;
}
fLocked = true;
return B_OK;
}
status_t
AddressSpaceWriteLocker::SetFromArea(team_id team, area_id areaID,
bool allowKernel, VMArea*& area)
{
VMAreaHash::ReadLock();
area = VMAreaHash::LookupLocked(areaID);
if (area != NULL
&& (area->address_space->ID() == team
|| (allowKernel && team == VMAddressSpace::KernelID()))) {
fSpace = area->address_space;
fSpace->Get();
}
VMAreaHash::ReadUnlock();
if (fSpace == NULL)
return B_BAD_VALUE;
// Second try to get the area -- this time with the address space
// write lock held
fSpace->WriteLock();
area = VMAreaHash::Lookup(areaID);
if (area == NULL) {
fSpace->WriteUnlock();
return B_BAD_VALUE;
}
fLocked = true;
return B_OK;
}
status_t
AddressSpaceWriteLocker::SetFromArea(team_id team, area_id areaID,
VMArea*& area)
{
return SetFromArea(team, areaID, false, area);
}
void
AddressSpaceWriteLocker::Unlock()
{
if (fLocked) {
if (fDegraded)
fSpace->ReadUnlock();
else
fSpace->WriteUnlock();
fLocked = false;
fDegraded = false;
}
}
void
AddressSpaceWriteLocker::DegradeToReadLock()
{
// TODO: the current R/W lock implementation just keeps the write lock here
fSpace->ReadLock();
fSpace->WriteUnlock();
fDegraded = true;
}
// #pragma mark - MultiAddressSpaceLocker
MultiAddressSpaceLocker::MultiAddressSpaceLocker()
:
fItems(NULL),
fCapacity(0),
fCount(0),
fLocked(false)
{
}
MultiAddressSpaceLocker::~MultiAddressSpaceLocker()
{
Unset();
free(fItems);
}
/*static*/ int
MultiAddressSpaceLocker::_CompareItems(const void* _a, const void* _b)
{
lock_item* a = (lock_item*)_a;
lock_item* b = (lock_item*)_b;
return a->space->ID() - b->space->ID();
}
bool
MultiAddressSpaceLocker::_ResizeIfNeeded()
{
if (fCount == fCapacity) {
lock_item* items = (lock_item*)realloc(fItems,
(fCapacity + 4) * sizeof(lock_item));
if (items == NULL)
return false;
fCapacity += 4;
fItems = items;
}
return true;
}
int32
MultiAddressSpaceLocker::_IndexOfAddressSpace(VMAddressSpace* space) const
{
for (int32 i = 0; i < fCount; i++) {
if (fItems[i].space == space)
return i;
}
return -1;
}
status_t
MultiAddressSpaceLocker::_AddAddressSpace(VMAddressSpace* space,
bool writeLock, VMAddressSpace** _space)
{
if (!space)
return B_BAD_VALUE;
int32 index = _IndexOfAddressSpace(space);
if (index < 0) {
if (!_ResizeIfNeeded()) {
space->Put();
return B_NO_MEMORY;
}
lock_item& item = fItems[fCount++];
item.space = space;
item.write_lock = writeLock;
} else {
// one reference is enough
space->Put();
fItems[index].write_lock |= writeLock;
}
if (_space != NULL)
*_space = space;
return B_OK;
}
void
MultiAddressSpaceLocker::Unset()
{
Unlock();
for (int32 i = 0; i < fCount; i++)
fItems[i].space->Put();
fCount = 0;
}
status_t
MultiAddressSpaceLocker::Lock()
{
ASSERT(!fLocked);
qsort(fItems, fCount, sizeof(lock_item), &_CompareItems);
for (int32 i = 0; i < fCount; i++) {
status_t status;
if (fItems[i].write_lock)
status = fItems[i].space->WriteLock();
else
status = fItems[i].space->ReadLock();
if (status < B_OK) {
while (--i >= 0) {
if (fItems[i].write_lock)
fItems[i].space->WriteUnlock();
else
fItems[i].space->ReadUnlock();
}
return status;
}
}
fLocked = true;
return B_OK;
}
void
MultiAddressSpaceLocker::Unlock()
{
if (!fLocked)
return;
for (int32 i = 0; i < fCount; i++) {
if (fItems[i].write_lock)
fItems[i].space->WriteUnlock();
else
fItems[i].space->ReadUnlock();
}
fLocked = false;
}
/*! Adds all address spaces of the areas associated with the given area's cache,
locks them, and locks the cache (including a reference to it). It retries
until the situation is stable (i.e. the neither cache nor cache's areas
changed) or an error occurs.
*/
status_t
MultiAddressSpaceLocker::AddAreaCacheAndLock(area_id areaID,
bool writeLockThisOne, bool writeLockOthers, VMArea*& _area,
VMCache** _cache)
{
// remember the original state
int originalCount = fCount;
lock_item* originalItems = NULL;
if (fCount > 0) {
originalItems = new(nothrow) lock_item[fCount];
if (originalItems == NULL)
return B_NO_MEMORY;
memcpy(originalItems, fItems, fCount * sizeof(lock_item));
}
ArrayDeleter<lock_item> _(originalItems);
// get the cache
VMCache* cache;
VMArea* area;
status_t error;
{
AddressSpaceReadLocker locker;
error = locker.SetFromArea(areaID, area);
if (error != B_OK)
return error;
cache = vm_area_get_locked_cache(area);
}
while (true) {
// add all areas
VMArea* firstArea = cache->areas;
for (VMArea* current = firstArea; current;
current = current->cache_next) {
error = AddArea(current->id,
current == area ? writeLockThisOne : writeLockOthers);
if (error != B_OK) {
vm_area_put_locked_cache(cache);
return error;
}
}
// unlock the cache and attempt to lock the address spaces
vm_area_put_locked_cache(cache);
error = Lock();
if (error != B_OK)
return error;
// lock the cache again and check whether anything has changed
// check whether the area is gone in the meantime
area = VMAreaHash::Lookup(areaID);
if (area == NULL) {
Unlock();
return B_BAD_VALUE;
}
// lock the cache
VMCache* oldCache = cache;
cache = vm_area_get_locked_cache(area);
// If neither the area's cache has changed nor its area list we're
// done.
if (cache == oldCache && firstArea == cache->areas) {
_area = area;
if (_cache != NULL)
*_cache = cache;
return B_OK;
}
// Restore the original state and try again.
// Unlock the address spaces, but keep the cache locked for the next
// iteration.
Unlock();
// Get an additional reference to the original address spaces.
for (int32 i = 0; i < originalCount; i++)
originalItems[i].space->Get();
// Release all references to the current address spaces.
for (int32 i = 0; i < fCount; i++)
fItems[i].space->Put();
// Copy over the original state.
fCount = originalCount;
if (originalItems != NULL)
memcpy(fItems, originalItems, fCount * sizeof(lock_item));
}
}
@@ -0,0 +1,138 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Copyright 2002-2009, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef VM_ADDRESS_SPACE_LOCKING_H
#define VM_ADDRESS_SPACE_LOCKING_H
#include <OS.h>
#include <vm/VMAddressSpace.h>
struct VMAddressSpace;
struct VMArea;
struct VMCache;
class AddressSpaceLockerBase {
public:
static VMAddressSpace* GetAddressSpaceByAreaID(area_id id);
};
class AddressSpaceReadLocker : private AddressSpaceLockerBase {
public:
AddressSpaceReadLocker(team_id team);
AddressSpaceReadLocker(VMAddressSpace* space,
bool getNewReference);
AddressSpaceReadLocker();
~AddressSpaceReadLocker();
status_t SetTo(team_id team);
void SetTo(VMAddressSpace* space,
bool getNewReference);
status_t SetFromArea(area_id areaID, VMArea*& area);
bool IsLocked() const { return fLocked; }
bool Lock();
void Unlock();
void Unset();
VMAddressSpace* AddressSpace() const { return fSpace; }
private:
VMAddressSpace* fSpace;
bool fLocked;
};
class AddressSpaceWriteLocker : private AddressSpaceLockerBase {
public:
AddressSpaceWriteLocker(team_id team);
AddressSpaceWriteLocker();
~AddressSpaceWriteLocker();
status_t SetTo(team_id team);
status_t SetFromArea(area_id areaID, VMArea*& area);
status_t SetFromArea(team_id team, area_id areaID,
bool allowKernel, VMArea*& area);
status_t SetFromArea(team_id team, area_id areaID,
VMArea*& area);
bool IsLocked() const { return fLocked; }
void Unlock();
void DegradeToReadLock();
void Unset();
VMAddressSpace* AddressSpace() const { return fSpace; }
private:
VMAddressSpace* fSpace;
bool fLocked;
bool fDegraded;
};
class MultiAddressSpaceLocker : private AddressSpaceLockerBase {
public:
MultiAddressSpaceLocker();
~MultiAddressSpaceLocker();
inline status_t AddTeam(team_id team, bool writeLock,
VMAddressSpace** _space = NULL);
inline status_t AddArea(area_id area, bool writeLock,
VMAddressSpace** _space = NULL);
status_t AddAreaCacheAndLock(area_id areaID,
bool writeLockThisOne, bool writeLockOthers,
VMArea*& _area, VMCache** _cache = NULL);
status_t Lock();
void Unlock();
bool IsLocked() const { return fLocked; }
void Unset();
private:
struct lock_item {
VMAddressSpace* space;
bool write_lock;
};
bool _ResizeIfNeeded();
int32 _IndexOfAddressSpace(VMAddressSpace* space)
const;
status_t _AddAddressSpace(VMAddressSpace* space,
bool writeLock, VMAddressSpace** _space);
static int _CompareItems(const void* _a, const void* _b);
lock_item* fItems;
int32 fCapacity;
int32 fCount;
bool fLocked;
};
inline status_t
MultiAddressSpaceLocker::AddTeam(team_id team, bool writeLock,
VMAddressSpace** _space)
{
return _AddAddressSpace(VMAddressSpace::Get(team), writeLock, _space);
}
inline status_t
MultiAddressSpaceLocker::AddArea(area_id area, bool writeLock,
VMAddressSpace** _space)
{
return _AddAddressSpace(GetAddressSpaceByAreaID(area), writeLock, _space);
}
#endif // VM_ADDRESS_SPACE_LOCKING_H
+1 -637
View File
@@ -50,6 +50,7 @@
#include <vm/VMArea.h>
#include <vm/VMCache.h>
#include "VMAddressSpaceLocking.h"
#include "VMAnonymousCache.h"
#include "IORequest.h"
@@ -68,95 +69,6 @@
#endif
class AddressSpaceReadLocker {
public:
AddressSpaceReadLocker(team_id team);
AddressSpaceReadLocker(VMAddressSpace* space, bool getNewReference);
AddressSpaceReadLocker();
~AddressSpaceReadLocker();
status_t SetTo(team_id team);
void SetTo(VMAddressSpace* space, bool getNewReference);
status_t SetFromArea(area_id areaID, VMArea*& area);
bool IsLocked() const { return fLocked; }
bool Lock();
void Unlock();
void Unset();
VMAddressSpace* AddressSpace() { return fSpace; }
private:
VMAddressSpace* fSpace;
bool fLocked;
};
class AddressSpaceWriteLocker {
public:
AddressSpaceWriteLocker(team_id team);
AddressSpaceWriteLocker();
~AddressSpaceWriteLocker();
status_t SetTo(team_id team);
status_t SetFromArea(area_id areaID, VMArea*& area);
status_t SetFromArea(team_id team, area_id areaID, bool allowKernel,
VMArea*& area);
status_t SetFromArea(team_id team, area_id areaID, VMArea*& area);
bool IsLocked() const { return fLocked; }
void Unlock();
void DegradeToReadLock();
void Unset();
VMAddressSpace* AddressSpace() { return fSpace; }
private:
VMAddressSpace* fSpace;
bool fLocked;
bool fDegraded;
};
class MultiAddressSpaceLocker {
public:
MultiAddressSpaceLocker();
~MultiAddressSpaceLocker();
inline status_t AddTeam(team_id team, bool writeLock,
VMAddressSpace** _space = NULL);
inline status_t AddArea(area_id area, bool writeLock,
VMAddressSpace** _space = NULL);
status_t AddAreaCacheAndLock(area_id areaID, bool writeLockThisOne,
bool writeLockOthers, VMArea*& _area, VMCache** _cache = NULL);
status_t Lock();
void Unlock();
bool IsLocked() const { return fLocked; }
void Unset();
private:
struct lock_item {
VMAddressSpace* space;
bool write_lock;
};
bool _ResizeIfNeeded();
int32 _IndexOfAddressSpace(VMAddressSpace* space) const;
status_t _AddAddressSpace(VMAddressSpace* space, bool writeLock,
VMAddressSpace** _space);
static int _CompareItems(const void* _a, const void* _b);
lock_item* fItems;
int32 fCapacity;
int32 fCount;
bool fLocked;
};
class AreaCacheLocking {
public:
inline bool Lock(VMCache* lockable)
@@ -216,7 +128,6 @@ static cache_info* sCacheInfoTable;
// function declarations
static void delete_area(VMAddressSpace* addressSpace, VMArea* area);
static VMAddressSpace* get_address_space_by_area_id(area_id id);
static status_t vm_soft_fault(VMAddressSpace* addressSpace, addr_t address,
bool isWrite, bool isUser);
static status_t map_backing_store(VMAddressSpace* addressSpace,
@@ -231,534 +142,6 @@ static size_t sKernelAddressSpaceLeft = KERNEL_SIZE;
// #pragma mark -
AddressSpaceReadLocker::AddressSpaceReadLocker(team_id team)
:
fSpace(NULL),
fLocked(false)
{
SetTo(team);
}
/*! Takes over the reference of the address space, if \a getNewReference is
\c false.
*/
AddressSpaceReadLocker::AddressSpaceReadLocker(VMAddressSpace* space,
bool getNewReference)
:
fSpace(NULL),
fLocked(false)
{
SetTo(space, getNewReference);
}
AddressSpaceReadLocker::AddressSpaceReadLocker()
:
fSpace(NULL),
fLocked(false)
{
}
AddressSpaceReadLocker::~AddressSpaceReadLocker()
{
Unset();
}
void
AddressSpaceReadLocker::Unset()
{
Unlock();
if (fSpace != NULL)
fSpace->Put();
}
status_t
AddressSpaceReadLocker::SetTo(team_id team)
{
fSpace = VMAddressSpace::Get(team);
if (fSpace == NULL)
return B_BAD_TEAM_ID;
fSpace->ReadLock();
fLocked = true;
return B_OK;
}
/*! Takes over the reference of the address space, if \a getNewReference is
\c false.
*/
void
AddressSpaceReadLocker::SetTo(VMAddressSpace* space, bool getNewReference)
{
fSpace = space;
if (getNewReference)
fSpace->Get();
fSpace->ReadLock();
fLocked = true;
}
status_t
AddressSpaceReadLocker::SetFromArea(area_id areaID, VMArea*& area)
{
fSpace = get_address_space_by_area_id(areaID);
if (fSpace == NULL)
return B_BAD_TEAM_ID;
fSpace->ReadLock();
area = VMAreaHash::Lookup(areaID);
if (area == NULL || area->address_space != fSpace) {
fSpace->ReadUnlock();
return B_BAD_VALUE;
}
fLocked = true;
return B_OK;
}
bool
AddressSpaceReadLocker::Lock()
{
if (fLocked)
return true;
if (fSpace == NULL)
return false;
fSpace->ReadLock();
fLocked = true;
return true;
}
void
AddressSpaceReadLocker::Unlock()
{
if (fLocked) {
fSpace->ReadUnlock();
fLocked = false;
}
}
// #pragma mark -
AddressSpaceWriteLocker::AddressSpaceWriteLocker(team_id team)
:
fSpace(NULL),
fLocked(false),
fDegraded(false)
{
SetTo(team);
}
AddressSpaceWriteLocker::AddressSpaceWriteLocker()
:
fSpace(NULL),
fLocked(false),
fDegraded(false)
{
}
AddressSpaceWriteLocker::~AddressSpaceWriteLocker()
{
Unset();
}
void
AddressSpaceWriteLocker::Unset()
{
Unlock();
if (fSpace != NULL)
fSpace->Put();
}
status_t
AddressSpaceWriteLocker::SetTo(team_id team)
{
fSpace = VMAddressSpace::Get(team);
if (fSpace == NULL)
return B_BAD_TEAM_ID;
fSpace->WriteLock();
fLocked = true;
return B_OK;
}
status_t
AddressSpaceWriteLocker::SetFromArea(area_id areaID, VMArea*& area)
{
fSpace = get_address_space_by_area_id(areaID);
if (fSpace == NULL)
return B_BAD_VALUE;
fSpace->WriteLock();
area = VMAreaHash::Lookup(areaID);
if (area == NULL || area->address_space != fSpace) {
fSpace->WriteUnlock();
return B_BAD_VALUE;
}
fLocked = true;
return B_OK;
}
status_t
AddressSpaceWriteLocker::SetFromArea(team_id team, area_id areaID,
bool allowKernel, VMArea*& area)
{
VMAreaHash::ReadLock();
area = VMAreaHash::LookupLocked(areaID);
if (area != NULL
&& (area->address_space->ID() == team
|| (allowKernel && team == VMAddressSpace::KernelID()))) {
fSpace = area->address_space;
fSpace->Get();
}
VMAreaHash::ReadUnlock();
if (fSpace == NULL)
return B_BAD_VALUE;
// Second try to get the area -- this time with the address space
// write lock held
fSpace->WriteLock();
area = VMAreaHash::Lookup(areaID);
if (area == NULL) {
fSpace->WriteUnlock();
return B_BAD_VALUE;
}
fLocked = true;
return B_OK;
}
status_t
AddressSpaceWriteLocker::SetFromArea(team_id team, area_id areaID,
VMArea*& area)
{
return SetFromArea(team, areaID, false, area);
}
void
AddressSpaceWriteLocker::Unlock()
{
if (fLocked) {
if (fDegraded)
fSpace->ReadUnlock();
else
fSpace->WriteUnlock();
fLocked = false;
fDegraded = false;
}
}
void
AddressSpaceWriteLocker::DegradeToReadLock()
{
// TODO: the current R/W lock implementation just keeps the write lock here
fSpace->ReadLock();
fSpace->WriteUnlock();
fDegraded = true;
}
// #pragma mark -
MultiAddressSpaceLocker::MultiAddressSpaceLocker()
:
fItems(NULL),
fCapacity(0),
fCount(0),
fLocked(false)
{
}
MultiAddressSpaceLocker::~MultiAddressSpaceLocker()
{
Unset();
free(fItems);
}
/*static*/ int
MultiAddressSpaceLocker::_CompareItems(const void* _a, const void* _b)
{
lock_item* a = (lock_item*)_a;
lock_item* b = (lock_item*)_b;
return a->space->ID() - b->space->ID();
}
bool
MultiAddressSpaceLocker::_ResizeIfNeeded()
{
if (fCount == fCapacity) {
lock_item* items = (lock_item*)realloc(fItems,
(fCapacity + 4) * sizeof(lock_item));
if (items == NULL)
return false;
fCapacity += 4;
fItems = items;
}
return true;
}
int32
MultiAddressSpaceLocker::_IndexOfAddressSpace(VMAddressSpace* space) const
{
for (int32 i = 0; i < fCount; i++) {
if (fItems[i].space == space)
return i;
}
return -1;
}
status_t
MultiAddressSpaceLocker::_AddAddressSpace(VMAddressSpace* space,
bool writeLock, VMAddressSpace** _space)
{
if (!space)
return B_BAD_VALUE;
int32 index = _IndexOfAddressSpace(space);
if (index < 0) {
if (!_ResizeIfNeeded()) {
space->Put();
return B_NO_MEMORY;
}
lock_item& item = fItems[fCount++];
item.space = space;
item.write_lock = writeLock;
} else {
// one reference is enough
space->Put();
fItems[index].write_lock |= writeLock;
}
if (_space != NULL)
*_space = space;
return B_OK;
}
inline status_t
MultiAddressSpaceLocker::AddTeam(team_id team, bool writeLock,
VMAddressSpace** _space)
{
return _AddAddressSpace(VMAddressSpace::Get(team), writeLock,
_space);
}
inline status_t
MultiAddressSpaceLocker::AddArea(area_id area, bool writeLock,
VMAddressSpace** _space)
{
return _AddAddressSpace(get_address_space_by_area_id(area), writeLock,
_space);
}
void
MultiAddressSpaceLocker::Unset()
{
Unlock();
for (int32 i = 0; i < fCount; i++)
fItems[i].space->Put();
fCount = 0;
}
status_t
MultiAddressSpaceLocker::Lock()
{
ASSERT(!fLocked);
qsort(fItems, fCount, sizeof(lock_item), &_CompareItems);
for (int32 i = 0; i < fCount; i++) {
status_t status;
if (fItems[i].write_lock)
status = fItems[i].space->WriteLock();
else
status = fItems[i].space->ReadLock();
if (status < B_OK) {
while (--i >= 0) {
if (fItems[i].write_lock)
fItems[i].space->WriteUnlock();
else
fItems[i].space->ReadUnlock();
}
return status;
}
}
fLocked = true;
return B_OK;
}
void
MultiAddressSpaceLocker::Unlock()
{
if (!fLocked)
return;
for (int32 i = 0; i < fCount; i++) {
if (fItems[i].write_lock)
fItems[i].space->WriteUnlock();
else
fItems[i].space->ReadUnlock();
}
fLocked = false;
}
/*! Adds all address spaces of the areas associated with the given area's cache,
locks them, and locks the cache (including a reference to it). It retries
until the situation is stable (i.e. the neither cache nor cache's areas
changed) or an error occurs.
*/
status_t
MultiAddressSpaceLocker::AddAreaCacheAndLock(area_id areaID,
bool writeLockThisOne, bool writeLockOthers, VMArea*& _area,
VMCache** _cache)
{
// remember the original state
int originalCount = fCount;
lock_item* originalItems = NULL;
if (fCount > 0) {
originalItems = new(nothrow) lock_item[fCount];
if (originalItems == NULL)
return B_NO_MEMORY;
memcpy(originalItems, fItems, fCount * sizeof(lock_item));
}
ArrayDeleter<lock_item> _(originalItems);
// get the cache
VMCache* cache;
VMArea* area;
status_t error;
{
AddressSpaceReadLocker locker;
error = locker.SetFromArea(areaID, area);
if (error != B_OK)
return error;
cache = vm_area_get_locked_cache(area);
}
while (true) {
// add all areas
VMArea* firstArea = cache->areas;
for (VMArea* current = firstArea; current;
current = current->cache_next) {
error = AddArea(current->id,
current == area ? writeLockThisOne : writeLockOthers);
if (error != B_OK) {
vm_area_put_locked_cache(cache);
return error;
}
}
// unlock the cache and attempt to lock the address spaces
vm_area_put_locked_cache(cache);
error = Lock();
if (error != B_OK)
return error;
// lock the cache again and check whether anything has changed
// check whether the area is gone in the meantime
area = VMAreaHash::Lookup(areaID);
if (area == NULL) {
Unlock();
return B_BAD_VALUE;
}
// lock the cache
VMCache* oldCache = cache;
cache = vm_area_get_locked_cache(area);
// If neither the area's cache has changed nor its area list we're
// done.
if (cache == oldCache && firstArea == cache->areas) {
_area = area;
if (_cache != NULL)
*_cache = cache;
return B_OK;
}
// Restore the original state and try again.
// Unlock the address spaces, but keep the cache locked for the next
// iteration.
Unlock();
// Get an additional reference to the original address spaces.
for (int32 i = 0; i < originalCount; i++)
originalItems[i].space->Get();
// Release all references to the current address spaces.
for (int32 i = 0; i < fCount; i++)
fItems[i].space->Put();
// Copy over the original state.
fCount = originalCount;
if (originalItems != NULL)
memcpy(fItems, originalItems, fCount * sizeof(lock_item));
}
}
// #pragma mark -
#if VM_PAGE_FAULT_TRACING
namespace VMPageFaultTracing {
@@ -882,25 +265,6 @@ private:
// #pragma mark -
static VMAddressSpace*
get_address_space_by_area_id(area_id id)
{
VMAddressSpace* addressSpace = NULL;
VMAreaHash::ReadLock();
VMArea* area = VMAreaHash::LookupLocked(id);
if (area != NULL) {
addressSpace = area->address_space;
addressSpace->Get();
}
VMAreaHash::ReadUnlock();
return addressSpace;
}
//! You need to have the address space locked when calling this function
static VMArea*
lookup_area(VMAddressSpace* addressSpace, area_id id)