Fixed the buggy hoard lock implementation:

- we must not use static C++ locks: the heap is initialized before the
  global constructors are called which means that our sLockList was
  initialized twice, and lost all locks added inbetween
- *but* the lock implementation was completely wrong anyway: Hoard used
  to allocate superblocks, but it never freed them - instead, their memory
  was recycled when their time came. That caused semaphores to be in fact
  thrown away, but also corrupted the lock list
- I've now implemented the locks like they are implemented in the other
  architectures supported by Hoard: via a spinning (busy waiting) lock.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12200 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2005-03-31 19:18:37 +00:00
parent d3be3f2cb1
commit b58f01cef7
2 changed files with 47 additions and 78 deletions
@@ -28,14 +28,17 @@
using namespace BPrivate; using namespace BPrivate;
// How many iterations we spin waiting for a lock.
enum { SPIN_LIMIT = 50 };
// The values of a user-level lock.
enum { UNLOCKED = 0, LOCKED = 1 };
struct free_chunk { struct free_chunk {
free_chunk *next; free_chunk *next;
size_t size; size_t size;
}; };
typedef DoublyLinkedList<hoardLockType> LockList;
static const size_t kInitialHeapSize = 50 * B_PAGE_SIZE; static const size_t kInitialHeapSize = 50 * B_PAGE_SIZE;
// that's about what hoard allocates anyway // that's about what hoard allocates anyway
@@ -46,20 +49,14 @@ static void *sHeapBase;
static addr_t sFreeHeapBase; static addr_t sFreeHeapBase;
static size_t sFreeHeapSize, sHeapAreaSize; static size_t sFreeHeapSize, sHeapAreaSize;
static free_chunk *sFreeChunks; static free_chunk *sFreeChunks;
static LockList sLockList;
static void initialize_hoard_lock(hoardLockType &lock, const char *name);
static void reinitialize_hoard_lock(hoardLockType &lock);
static void static void
init_after_fork() init_after_fork(void)
{ {
// re-initialize all locks sHeapLock = create_sem(1, "heap");
for (LockList::Iterator it = sLockList.GetIterator(); it.HasNext();) { if (sHeapLock < B_OK)
hoardLockType *lock = it.Next(); exit(1);
reinitialize_hoard_lock(*lock);
}
// find the heap area // find the heap area
sHeapArea = area_for(sHeapBase); sHeapArea = area_for(sHeapBase);
@@ -86,20 +83,14 @@ __init_heap(void)
sHeapArea = create_area("heap", (void **)&sHeapBase, B_BASE_ADDRESS, sHeapArea = create_area("heap", (void **)&sHeapBase, B_BASE_ADDRESS,
sHeapAreaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); sHeapAreaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
if (sHeapArea < B_OK)
return sHeapArea;
sFreeHeapBase = (addr_t)sHeapBase; sFreeHeapBase = (addr_t)sHeapBase;
// init the lock list, and the heap lock sHeapLock = create_sem(1, "heap");
// Thereafter all locks should be initialized with hoardLockInit(). They if (sHeapLock < B_OK)
// will be properly re-initialized after a fork(). Note, that also the return sHeapLock;
// heap lock is initialized with hoardLockInit() -- this works fine
// and has the advantage, that it is in the lock list itself and we won't
// need any special handling on fork().
new (&sLockList) LockList;
hoardLockInit(sHeapLock, "heap");
if (sHeapArea < 0)
return sHeapArea;
atfork(&init_after_fork); atfork(&init_after_fork);
// Note: Needs malloc(). Hence we need to be fully initialized. // Note: Needs malloc(). Hence we need to be fully initialized.
@@ -112,31 +103,6 @@ __init_heap(void)
} }
static void
initialize_hoard_lock(hoardLockType &lock, const char *name)
{
lock.ben = 0;
lock.sem = create_sem(0, name);
if (lock.sem < 0) {
debug_printf("hoard: initialize_hoard_lock(): Failed to create "
"semaphore");
}
}
static void
reinitialize_hoard_lock(hoardLockType &lock)
{
// Get an info for the original semaphore, so we can name it just the same.
// This can fail e.g. in case the original team is already gone.
sem_info info;
if (get_sem_info(lock.sem, &info) == B_OK)
initialize_hoard_lock(lock, info.name);
else
initialize_hoard_lock(lock, "reinitialized hoard lock");
}
namespace BPrivate { namespace BPrivate {
void * void *
@@ -147,7 +113,12 @@ hoardSbrk(long size)
// align size request // align size request
size = (size + hoardHeap::ALIGNMENT - 1) & ~(hoardHeap::ALIGNMENT - 1); size = (size + hoardHeap::ALIGNMENT - 1) & ~(hoardHeap::ALIGNMENT - 1);
hoardLock(sHeapLock); status_t status;
do {
status = acquire_sem(sHeapLock);
} while (status == B_INTERRUPTED);
if (status < B_OK)
return NULL;
// find chunk in free list // find chunk in free list
free_chunk *chunk = sFreeChunks, *last = NULL; free_chunk *chunk = sFreeChunks, *last = NULL;
@@ -172,8 +143,7 @@ hoardSbrk(long size)
else else
sFreeChunks = chunk; sFreeChunks = chunk;
hoardUnlock(sHeapLock); release_sem(sHeapLock);
return address; return address;
} }
@@ -188,7 +158,7 @@ hoardSbrk(long size)
if (pageSize < sHeapAreaSize) { if (pageSize < sHeapAreaSize) {
SERIAL_PRINT(("HEAP-%ld: heap area large enough for %ld\n", find_thread(NULL), size)); SERIAL_PRINT(("HEAP-%ld: heap area large enough for %ld\n", find_thread(NULL), size));
// the area is large enough already // the area is large enough already
hoardUnlock(sHeapLock); release_sem(sHeapLock);
return (void *)(sFreeHeapBase + oldHeapSize); return (void *)(sFreeHeapBase + oldHeapSize);
} }
@@ -199,14 +169,13 @@ hoardSbrk(long size)
if (resize_area(sHeapArea, pageSize) < B_OK) { if (resize_area(sHeapArea, pageSize) < B_OK) {
// out of memory - ToDo: as a fall back, we could try to allocate another area // out of memory - ToDo: as a fall back, we could try to allocate another area
hoardUnlock(sHeapLock); release_sem(sHeapLock);
return NULL; return NULL;
} }
sHeapAreaSize = pageSize; sHeapAreaSize = pageSize;
hoardUnlock(sHeapLock); release_sem(sHeapLock);
return (void *)(sFreeHeapBase + oldHeapSize); return (void *)(sFreeHeapBase + oldHeapSize);
} }
@@ -221,37 +190,45 @@ hoardUnsbrk(void *ptr, long size)
void void
hoardLockInit(hoardLockType &lock, const char *name) hoardLockInit(hoardLockType &lock, const char *name)
{ {
new (&lock) hoardLockType; lock = UNLOCKED;
// init's the list link
initialize_hoard_lock(lock, name);
// add the lock to the lock list (the heap lock also protects the lock list)
hoardLock(sHeapLock);
sLockList.Add(&lock);
hoardUnlock(sHeapLock);
} }
void void
hoardLock(hoardLockType &lock) hoardLock(hoardLockType &lock)
{ {
if (atomic_add(&(lock.ben), 1) >= 1) // A yielding lock (with an initial spin).
acquire_sem(lock.sem); while (true) {
int32 i = 0;
while (i < SPIN_LIMIT) {
if (atomic_test_and_set(&lock, LOCKED, UNLOCKED) == UNLOCKED) {
// We got the lock.
return;
}
i++;
}
// The lock is still being held by someone else.
// Give up our quantum.
hoardYield();
}
} }
void void
hoardUnlock(hoardLockType &lock) hoardUnlock(hoardLockType &lock)
{ {
if (atomic_add(&(lock.ben), -1) > 1) atomic_set(&lock, UNLOCKED);
release_sem(lock.sem);
} }
void void
hoardYield(void) hoardYield(void)
{ {
// A thread's quantum is definitely larger than this, so this is
// an expensive yield function.
// ToDo: we should have a real one in the kernel
snooze(5);
} }
} // namespace BPrivate } // namespace BPrivate
@@ -27,16 +27,8 @@
#include <OS.h> #include <OS.h>
#include <assert.h> #include <assert.h>
#include <util/DoublyLinkedList.h>
typedef int32 hoardLockType;
// Note: Since we're currently locks are never uninitialized, a singly linked
// list would suffice. But we may change that some day, and the singly linked
// list interface is ugly, anyway. ;-)
struct hoardLockType : DoublyLinkedListLinkImpl<hoardLockType> {
int32 ben;
sem_id sem;
};
namespace BPrivate { namespace BPrivate {