2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
8 #include "VMAddressSpaceLocking.h"
10 #include <AutoDeleter.h>
13 #include <vm/VMAddressSpace.h>
14 #include <vm/VMArea.h>
15 #include <vm/VMCache.h>
18 // #pragma mark - AddressSpaceLockerBase
21 /*static*/ VMAddressSpace
*
22 AddressSpaceLockerBase::GetAddressSpaceByAreaID(area_id id
)
24 VMAddressSpace
* addressSpace
= NULL
;
26 VMAreaHash::ReadLock();
28 VMArea
* area
= VMAreaHash::LookupLocked(id
);
30 addressSpace
= area
->address_space
;
34 VMAreaHash::ReadUnlock();
40 // #pragma mark - AddressSpaceReadLocker
43 AddressSpaceReadLocker::AddressSpaceReadLocker(team_id team
)
52 /*! Takes over the reference of the address space, if \a getNewReference is
55 AddressSpaceReadLocker::AddressSpaceReadLocker(VMAddressSpace
* space
,
61 SetTo(space
, getNewReference
);
65 AddressSpaceReadLocker::AddressSpaceReadLocker()
73 AddressSpaceReadLocker::~AddressSpaceReadLocker()
80 AddressSpaceReadLocker::Unset()
89 AddressSpaceReadLocker::SetTo(team_id team
)
91 fSpace
= VMAddressSpace::Get(team
);
101 /*! Takes over the reference of the address space, if \a getNewReference is
105 AddressSpaceReadLocker::SetTo(VMAddressSpace
* space
, bool getNewReference
)
118 AddressSpaceReadLocker::SetFromArea(area_id areaID
, VMArea
*& area
)
120 fSpace
= GetAddressSpaceByAreaID(areaID
);
122 return B_BAD_TEAM_ID
;
126 area
= VMAreaHash::Lookup(areaID
);
128 if (area
== NULL
|| area
->address_space
!= fSpace
) {
129 fSpace
->ReadUnlock();
139 AddressSpaceReadLocker::Lock()
154 AddressSpaceReadLocker::Unlock()
157 fSpace
->ReadUnlock();
163 // #pragma mark - AddressSpaceWriteLocker
166 AddressSpaceWriteLocker::AddressSpaceWriteLocker(team_id team
)
176 AddressSpaceWriteLocker::AddressSpaceWriteLocker(VMAddressSpace
* space
,
177 bool getNewReference
)
183 SetTo(space
, getNewReference
);
187 AddressSpaceWriteLocker::AddressSpaceWriteLocker()
196 AddressSpaceWriteLocker::~AddressSpaceWriteLocker()
203 AddressSpaceWriteLocker::Unset()
212 AddressSpaceWriteLocker::SetTo(team_id team
)
214 fSpace
= VMAddressSpace::Get(team
);
216 return B_BAD_TEAM_ID
;
225 AddressSpaceWriteLocker::SetTo(VMAddressSpace
* space
, bool getNewReference
)
238 AddressSpaceWriteLocker::SetFromArea(area_id areaID
, VMArea
*& area
)
240 fSpace
= GetAddressSpaceByAreaID(areaID
);
246 area
= VMAreaHash::Lookup(areaID
);
248 if (area
== NULL
|| area
->address_space
!= fSpace
) {
249 fSpace
->WriteUnlock();
259 AddressSpaceWriteLocker::SetFromArea(team_id team
, area_id areaID
,
260 bool allowKernel
, VMArea
*& area
)
262 VMAreaHash::ReadLock();
264 area
= VMAreaHash::LookupLocked(areaID
);
266 && (area
->address_space
->ID() == team
267 || (allowKernel
&& team
== VMAddressSpace::KernelID()))) {
268 fSpace
= area
->address_space
;
272 VMAreaHash::ReadUnlock();
277 // Second try to get the area -- this time with the address space
282 area
= VMAreaHash::Lookup(areaID
);
285 fSpace
->WriteUnlock();
295 AddressSpaceWriteLocker::SetFromArea(team_id team
, area_id areaID
,
298 return SetFromArea(team
, areaID
, false, area
);
303 AddressSpaceWriteLocker::Unlock()
307 fSpace
->ReadUnlock();
309 fSpace
->WriteUnlock();
317 AddressSpaceWriteLocker::DegradeToReadLock()
320 fSpace
->WriteUnlock();
325 // #pragma mark - MultiAddressSpaceLocker
328 MultiAddressSpaceLocker::MultiAddressSpaceLocker()
338 MultiAddressSpaceLocker::~MultiAddressSpaceLocker()
346 MultiAddressSpaceLocker::_CompareItems(const void* _a
, const void* _b
)
348 lock_item
* a
= (lock_item
*)_a
;
349 lock_item
* b
= (lock_item
*)_b
;
350 return b
->space
->ID() - a
->space
->ID();
351 // descending order, i.e. kernel address space last
356 MultiAddressSpaceLocker::_ResizeIfNeeded()
358 if (fCount
== fCapacity
) {
359 lock_item
* items
= (lock_item
*)realloc(fItems
,
360 (fCapacity
+ 4) * sizeof(lock_item
));
373 MultiAddressSpaceLocker::_IndexOfAddressSpace(VMAddressSpace
* space
) const
375 for (int32 i
= 0; i
< fCount
; i
++) {
376 if (fItems
[i
].space
== space
)
385 MultiAddressSpaceLocker::_AddAddressSpace(VMAddressSpace
* space
,
386 bool writeLock
, VMAddressSpace
** _space
)
391 int32 index
= _IndexOfAddressSpace(space
);
393 if (!_ResizeIfNeeded()) {
398 lock_item
& item
= fItems
[fCount
++];
400 item
.write_lock
= writeLock
;
403 // one reference is enough
406 fItems
[index
].write_lock
|= writeLock
;
417 MultiAddressSpaceLocker::Unset()
421 for (int32 i
= 0; i
< fCount
; i
++)
422 fItems
[i
].space
->Put();
429 MultiAddressSpaceLocker::Lock()
433 qsort(fItems
, fCount
, sizeof(lock_item
), &_CompareItems
);
435 for (int32 i
= 0; i
< fCount
; i
++) {
437 if (fItems
[i
].write_lock
)
438 status
= fItems
[i
].space
->WriteLock();
440 status
= fItems
[i
].space
->ReadLock();
444 if (fItems
[i
].write_lock
)
445 fItems
[i
].space
->WriteUnlock();
447 fItems
[i
].space
->ReadUnlock();
459 MultiAddressSpaceLocker::Unlock()
464 for (int32 i
= 0; i
< fCount
; i
++) {
465 if (fItems
[i
].write_lock
)
466 fItems
[i
].space
->WriteUnlock();
468 fItems
[i
].space
->ReadUnlock();
475 /*! Adds all address spaces of the areas associated with the given area's cache,
476 locks them, and locks the cache (including a reference to it). It retries
477 until the situation is stable (i.e. the neither cache nor cache's areas
478 changed) or an error occurs.
481 MultiAddressSpaceLocker::AddAreaCacheAndLock(area_id areaID
,
482 bool writeLockThisOne
, bool writeLockOthers
, VMArea
*& _area
,
485 // remember the original state
486 int originalCount
= fCount
;
487 lock_item
* originalItems
= NULL
;
489 originalItems
= new(nothrow
) lock_item
[fCount
];
490 if (originalItems
== NULL
)
492 memcpy(originalItems
, fItems
, fCount
* sizeof(lock_item
));
494 ArrayDeleter
<lock_item
> _(originalItems
);
501 AddressSpaceReadLocker locker
;
502 error
= locker
.SetFromArea(areaID
, area
);
506 cache
= vm_area_get_locked_cache(area
);
511 VMArea
* firstArea
= cache
->areas
;
512 for (VMArea
* current
= firstArea
; current
;
513 current
= current
->cache_next
) {
514 error
= AddArea(current
,
515 current
== area
? writeLockThisOne
: writeLockOthers
);
517 vm_area_put_locked_cache(cache
);
522 // unlock the cache and attempt to lock the address spaces
523 vm_area_put_locked_cache(cache
);
529 // lock the cache again and check whether anything has changed
531 // check whether the area is gone in the meantime
532 area
= VMAreaHash::Lookup(areaID
);
540 VMCache
* oldCache
= cache
;
541 cache
= vm_area_get_locked_cache(area
);
543 // If neither the area's cache has changed nor its area list we're
545 if (cache
== oldCache
&& firstArea
== cache
->areas
) {
552 // Restore the original state and try again.
554 // Unlock the address spaces, but keep the cache locked for the next
558 // Get an additional reference to the original address spaces.
559 for (int32 i
= 0; i
< originalCount
; i
++)
560 originalItems
[i
].space
->Get();
562 // Release all references to the current address spaces.
563 for (int32 i
= 0; i
< fCount
; i
++)
564 fItems
[i
].space
->Put();
566 // Copy over the original state.
567 fCount
= originalCount
;
568 if (originalItems
!= NULL
)
569 memcpy(fItems
, originalItems
, fCount
* sizeof(lock_item
));