btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / system / kernel / vm / VMAddressSpaceLocking.cpp
blobd1bc1c32474b99c2fe87396043cece86c834d69a
1 /*
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.
5 */
8 #include "VMAddressSpaceLocking.h"
10 #include <AutoDeleter.h>
12 #include <vm/vm.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);
29 if (area != NULL) {
30 addressSpace = area->address_space;
31 addressSpace->Get();
34 VMAreaHash::ReadUnlock();
36 return addressSpace;
40 // #pragma mark - AddressSpaceReadLocker
43 AddressSpaceReadLocker::AddressSpaceReadLocker(team_id team)
45 fSpace(NULL),
46 fLocked(false)
48 SetTo(team);
52 /*! Takes over the reference of the address space, if \a getNewReference is
53 \c false.
55 AddressSpaceReadLocker::AddressSpaceReadLocker(VMAddressSpace* space,
56 bool getNewReference)
58 fSpace(NULL),
59 fLocked(false)
61 SetTo(space, getNewReference);
65 AddressSpaceReadLocker::AddressSpaceReadLocker()
67 fSpace(NULL),
68 fLocked(false)
73 AddressSpaceReadLocker::~AddressSpaceReadLocker()
75 Unset();
79 void
80 AddressSpaceReadLocker::Unset()
82 Unlock();
83 if (fSpace != NULL)
84 fSpace->Put();
88 status_t
89 AddressSpaceReadLocker::SetTo(team_id team)
91 fSpace = VMAddressSpace::Get(team);
92 if (fSpace == NULL)
93 return B_BAD_TEAM_ID;
95 fSpace->ReadLock();
96 fLocked = true;
97 return B_OK;
101 /*! Takes over the reference of the address space, if \a getNewReference is
102 \c false.
104 void
105 AddressSpaceReadLocker::SetTo(VMAddressSpace* space, bool getNewReference)
107 fSpace = space;
109 if (getNewReference)
110 fSpace->Get();
112 fSpace->ReadLock();
113 fLocked = true;
117 status_t
118 AddressSpaceReadLocker::SetFromArea(area_id areaID, VMArea*& area)
120 fSpace = GetAddressSpaceByAreaID(areaID);
121 if (fSpace == NULL)
122 return B_BAD_TEAM_ID;
124 fSpace->ReadLock();
126 area = VMAreaHash::Lookup(areaID);
128 if (area == NULL || area->address_space != fSpace) {
129 fSpace->ReadUnlock();
130 return B_BAD_VALUE;
133 fLocked = true;
134 return B_OK;
138 bool
139 AddressSpaceReadLocker::Lock()
141 if (fLocked)
142 return true;
143 if (fSpace == NULL)
144 return false;
146 fSpace->ReadLock();
147 fLocked = true;
149 return true;
153 void
154 AddressSpaceReadLocker::Unlock()
156 if (fLocked) {
157 fSpace->ReadUnlock();
158 fLocked = false;
163 // #pragma mark - AddressSpaceWriteLocker
166 AddressSpaceWriteLocker::AddressSpaceWriteLocker(team_id team)
168 fSpace(NULL),
169 fLocked(false),
170 fDegraded(false)
172 SetTo(team);
176 AddressSpaceWriteLocker::AddressSpaceWriteLocker(VMAddressSpace* space,
177 bool getNewReference)
179 fSpace(NULL),
180 fLocked(false),
181 fDegraded(false)
183 SetTo(space, getNewReference);
187 AddressSpaceWriteLocker::AddressSpaceWriteLocker()
189 fSpace(NULL),
190 fLocked(false),
191 fDegraded(false)
196 AddressSpaceWriteLocker::~AddressSpaceWriteLocker()
198 Unset();
202 void
203 AddressSpaceWriteLocker::Unset()
205 Unlock();
206 if (fSpace != NULL)
207 fSpace->Put();
211 status_t
212 AddressSpaceWriteLocker::SetTo(team_id team)
214 fSpace = VMAddressSpace::Get(team);
215 if (fSpace == NULL)
216 return B_BAD_TEAM_ID;
218 fSpace->WriteLock();
219 fLocked = true;
220 return B_OK;
224 void
225 AddressSpaceWriteLocker::SetTo(VMAddressSpace* space, bool getNewReference)
227 fSpace = space;
229 if (getNewReference)
230 fSpace->Get();
232 fSpace->WriteLock();
233 fLocked = true;
237 status_t
238 AddressSpaceWriteLocker::SetFromArea(area_id areaID, VMArea*& area)
240 fSpace = GetAddressSpaceByAreaID(areaID);
241 if (fSpace == NULL)
242 return B_BAD_VALUE;
244 fSpace->WriteLock();
246 area = VMAreaHash::Lookup(areaID);
248 if (area == NULL || area->address_space != fSpace) {
249 fSpace->WriteUnlock();
250 return B_BAD_VALUE;
253 fLocked = true;
254 return B_OK;
258 status_t
259 AddressSpaceWriteLocker::SetFromArea(team_id team, area_id areaID,
260 bool allowKernel, VMArea*& area)
262 VMAreaHash::ReadLock();
264 area = VMAreaHash::LookupLocked(areaID);
265 if (area != NULL
266 && (area->address_space->ID() == team
267 || (allowKernel && team == VMAddressSpace::KernelID()))) {
268 fSpace = area->address_space;
269 fSpace->Get();
272 VMAreaHash::ReadUnlock();
274 if (fSpace == NULL)
275 return B_BAD_VALUE;
277 // Second try to get the area -- this time with the address space
278 // write lock held
280 fSpace->WriteLock();
282 area = VMAreaHash::Lookup(areaID);
284 if (area == NULL) {
285 fSpace->WriteUnlock();
286 return B_BAD_VALUE;
289 fLocked = true;
290 return B_OK;
294 status_t
295 AddressSpaceWriteLocker::SetFromArea(team_id team, area_id areaID,
296 VMArea*& area)
298 return SetFromArea(team, areaID, false, area);
302 void
303 AddressSpaceWriteLocker::Unlock()
305 if (fLocked) {
306 if (fDegraded)
307 fSpace->ReadUnlock();
308 else
309 fSpace->WriteUnlock();
310 fLocked = false;
311 fDegraded = false;
316 void
317 AddressSpaceWriteLocker::DegradeToReadLock()
319 fSpace->ReadLock();
320 fSpace->WriteUnlock();
321 fDegraded = true;
325 // #pragma mark - MultiAddressSpaceLocker
328 MultiAddressSpaceLocker::MultiAddressSpaceLocker()
330 fItems(NULL),
331 fCapacity(0),
332 fCount(0),
333 fLocked(false)
338 MultiAddressSpaceLocker::~MultiAddressSpaceLocker()
340 Unset();
341 free(fItems);
345 /*static*/ int
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
355 bool
356 MultiAddressSpaceLocker::_ResizeIfNeeded()
358 if (fCount == fCapacity) {
359 lock_item* items = (lock_item*)realloc(fItems,
360 (fCapacity + 4) * sizeof(lock_item));
361 if (items == NULL)
362 return false;
364 fCapacity += 4;
365 fItems = items;
368 return true;
372 int32
373 MultiAddressSpaceLocker::_IndexOfAddressSpace(VMAddressSpace* space) const
375 for (int32 i = 0; i < fCount; i++) {
376 if (fItems[i].space == space)
377 return i;
380 return -1;
384 status_t
385 MultiAddressSpaceLocker::_AddAddressSpace(VMAddressSpace* space,
386 bool writeLock, VMAddressSpace** _space)
388 if (!space)
389 return B_BAD_VALUE;
391 int32 index = _IndexOfAddressSpace(space);
392 if (index < 0) {
393 if (!_ResizeIfNeeded()) {
394 space->Put();
395 return B_NO_MEMORY;
398 lock_item& item = fItems[fCount++];
399 item.space = space;
400 item.write_lock = writeLock;
401 } else {
403 // one reference is enough
404 space->Put();
406 fItems[index].write_lock |= writeLock;
409 if (_space != NULL)
410 *_space = space;
412 return B_OK;
416 void
417 MultiAddressSpaceLocker::Unset()
419 Unlock();
421 for (int32 i = 0; i < fCount; i++)
422 fItems[i].space->Put();
424 fCount = 0;
428 status_t
429 MultiAddressSpaceLocker::Lock()
431 ASSERT(!fLocked);
433 qsort(fItems, fCount, sizeof(lock_item), &_CompareItems);
435 for (int32 i = 0; i < fCount; i++) {
436 status_t status;
437 if (fItems[i].write_lock)
438 status = fItems[i].space->WriteLock();
439 else
440 status = fItems[i].space->ReadLock();
442 if (status < B_OK) {
443 while (--i >= 0) {
444 if (fItems[i].write_lock)
445 fItems[i].space->WriteUnlock();
446 else
447 fItems[i].space->ReadUnlock();
449 return status;
453 fLocked = true;
454 return B_OK;
458 void
459 MultiAddressSpaceLocker::Unlock()
461 if (!fLocked)
462 return;
464 for (int32 i = 0; i < fCount; i++) {
465 if (fItems[i].write_lock)
466 fItems[i].space->WriteUnlock();
467 else
468 fItems[i].space->ReadUnlock();
471 fLocked = false;
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.
480 status_t
481 MultiAddressSpaceLocker::AddAreaCacheAndLock(area_id areaID,
482 bool writeLockThisOne, bool writeLockOthers, VMArea*& _area,
483 VMCache** _cache)
485 // remember the original state
486 int originalCount = fCount;
487 lock_item* originalItems = NULL;
488 if (fCount > 0) {
489 originalItems = new(nothrow) lock_item[fCount];
490 if (originalItems == NULL)
491 return B_NO_MEMORY;
492 memcpy(originalItems, fItems, fCount * sizeof(lock_item));
494 ArrayDeleter<lock_item> _(originalItems);
496 // get the cache
497 VMCache* cache;
498 VMArea* area;
499 status_t error;
501 AddressSpaceReadLocker locker;
502 error = locker.SetFromArea(areaID, area);
503 if (error != B_OK)
504 return error;
506 cache = vm_area_get_locked_cache(area);
509 while (true) {
510 // add all areas
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);
516 if (error != B_OK) {
517 vm_area_put_locked_cache(cache);
518 return error;
522 // unlock the cache and attempt to lock the address spaces
523 vm_area_put_locked_cache(cache);
525 error = Lock();
526 if (error != B_OK)
527 return error;
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);
534 if (area == NULL) {
535 Unlock();
536 return B_BAD_VALUE;
539 // lock the cache
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
544 // done.
545 if (cache == oldCache && firstArea == cache->areas) {
546 _area = area;
547 if (_cache != NULL)
548 *_cache = cache;
549 return B_OK;
552 // Restore the original state and try again.
554 // Unlock the address spaces, but keep the cache locked for the next
555 // iteration.
556 Unlock();
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));