root/src/system/kernel/fs/Vnode.cpp
/*
 * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "Vnode.h"

#include <util/AutoLock.h>


vnode::Bucket vnode::sBuckets[kBucketCount];


vnode::Bucket::Bucket()
{
        mutex_init(&lock, "vnode bucket");
}


/*static*/ void
vnode::StaticInit()
{
        for (uint32 i = 0; i < kBucketCount; i++)
                new(&sBuckets[i]) Bucket;
}


void
vnode::_WaitForLock()
{
        LockWaiter waiter;
        waiter.thread = thread_get_current_thread();
        waiter.vnode = this;

        Bucket& bucket = _Bucket();
        MutexLocker bucketLocker(bucket.lock);

        if ((atomic_or(&fFlags, kFlagsWaitingLocker)
                        & (kFlagsLocked | kFlagsWaitingLocker)) == 0) {
                // The lock holder dropped it in the meantime and no-one else was faster
                // than us, so it's ours now. Just mark the node locked and clear the
                // waiting flag again.
                atomic_or(&fFlags, kFlagsLocked);
                atomic_and(&fFlags, ~kFlagsWaitingLocker);
                return;
        }

        // prepare for waiting
        bucket.waiters.Add(&waiter);
        thread_prepare_to_block(waiter.thread, 0, THREAD_BLOCK_TYPE_OTHER,
                "vnode lock");

        // start waiting
        bucketLocker.Unlock();
        thread_block();
}


void
vnode::_WakeUpLocker()
{
        Bucket& bucket = _Bucket();
        MutexLocker bucketLocker(bucket.lock);

        // mark the node locked again
        atomic_or(&fFlags, kFlagsLocked);

        // get the first waiter from the list
        LockWaiter* waiter = NULL;
        bool onlyWaiter = true;
        for (LockWaiterList::Iterator it = bucket.waiters.GetIterator();
                        LockWaiter* someWaiter = it.Next();) {
                if (someWaiter->vnode == this) {
                        if (waiter != NULL) {
                                onlyWaiter = false;
                                break;
                        }
                        waiter = someWaiter;
                        it.Remove();
                }
        }

        ASSERT(waiter != NULL);

        // if that's the only waiter, clear the flag
        if (onlyWaiter)
                atomic_and(&fFlags, ~kFlagsWaitingLocker);

        // and wake it up
        thread_unblock(waiter->thread, B_OK);
}