root/src/add-ons/kernel/file_systems/netfs/server/NodeHandleMap.cpp
// NodeHandleMap.cpp

#include "NodeHandleMap.h"

#include "AutoLocker.h"
#include "GlobalBlockerPool.h"


// constructor
NodeHandleMap::NodeHandleMap(const char* name)
        : Locker(name),
          fNextNodeHandleCookie(0)
{
}

// destructor
NodeHandleMap::~NodeHandleMap()
{
}

// Init
status_t
NodeHandleMap::Init()
{
        // check semaphore
        if (Sem() < 0)
                return Sem();

        return B_OK;
}

// AddNodeHandle
status_t
NodeHandleMap::AddNodeHandle(NodeHandle* handle)
{
        if (!handle)
                return B_BAD_VALUE;

        AutoLocker<Locker> _(this);

        handle->SetCookie(_NextNodeHandleCookie());

        status_t error = Put(handle->GetCookie(), handle);
        if (error == B_OK)
                handle->AcquireReference();

        return error;
}

// RemoveNodeHandle
bool
NodeHandleMap::RemoveNodeHandle(NodeHandle* handle)
{
        if (!handle)
                return false;

        AutoLocker<Locker> _(this);

        if (Get(handle->GetCookie()) != handle)
                return false;

        Remove(handle->GetCookie());
        handle->ReleaseReference();
        return true;
}

// LockNodeHandle
//
// VolumeManager must NOT be locked.
status_t
NodeHandleMap::LockNodeHandle(int32 cookie, NodeHandle** _handle)
{
        if (!_handle)
                return B_BAD_VALUE;

        NodeHandle* handle;
        {
                AutoLocker<Locker> _(this);

                // get the node handle
                handle = Get(cookie);
                if (!handle)
                        return B_ENTRY_NOT_FOUND;
                handle->AcquireReference();

                // first attempt: we just try to lock the node handle, which will fail,
                // if someone else has the lock at the momemt
                if (handle->Lock()) {
                        *_handle = handle;
                        return B_OK;
                }
        }

        // someone else is locking, get a blocker and wait for the lock
        BlockerPool* blockerPool = GlobalBlockerPool::GetDefault();
        Blocker blocker = blockerPool->GetBlocker();
        BlockerPutter blockerPutter(*blockerPool, blocker);
        LockerCandidate lockerCandidate(blocker);
        {
                AutoLocker<Locker> _(this);

                if (handle->Lock()) {
                        *_handle = handle;
                        return B_OK;
                }
                handle->QueueLockerCandidate(&lockerCandidate);
        }

        // wait for the lock
        status_t error = lockerCandidate.Block();
        if (error != B_OK) {
                handle->ReleaseReference();
                return error;
        }

        *_handle = handle;
        return B_OK;
}

// UnlockNodeHandle
//
// VolumeManager may or may not be locked.
void
NodeHandleMap::UnlockNodeHandle(NodeHandle* nodeHandle)
{
        if (!nodeHandle)
                return;

        if (nodeHandle->IsLocked()) {
                AutoLocker<Locker> _(this);

                nodeHandle->Unlock();
                nodeHandle->ReleaseReference();
        }
}

// _NextNodeHandleCookie
int32
NodeHandleMap::_NextNodeHandleCookie()
{
        int32 cookie;

        do {
                if (fNextNodeHandleCookie < 0)
                        fNextNodeHandleCookie = 0;
                cookie = fNextNodeHandleCookie++;
        } while (ContainsKey(cookie));

        return cookie;
}