root/src/tools/fs_shell/lock.cpp
/*
 * Copyright 2002-2012, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
 * Distributed under the terms of the NewOS License.
 */

/* Mutex and recursive_lock code */

#include "fssh_lock.h"

#include "fssh_kernel_export.h"


#define FSSH_RW_MAX_READERS 100000


extern "C" int32_t
fssh_recursive_lock_get_recursion(fssh_recursive_lock *lock)
{
        if (lock->holder == fssh_find_thread(NULL))
                return lock->recursion;

        return -1;
}


extern "C" void
fssh_recursive_lock_init_etc(fssh_recursive_lock *lock, const char *name,
        uint32_t flags)
{
        // TODO: No fssh_create_sem_etc for flags?

        if (lock == NULL)
                return;

        if (name == NULL)
                name = "recursive lock";

        lock->holder = -1;
        lock->recursion = 0;
        lock->sem = fssh_create_sem(1, name);
        if (lock->sem < FSSH_B_OK)
                fssh_panic("could not create recursive lock");
}


extern "C" void
fssh_recursive_lock_init(fssh_recursive_lock *lock, const char *name)
{
        fssh_recursive_lock_init_etc(lock, name, 0);
}


extern "C" void
fssh_recursive_lock_destroy(fssh_recursive_lock *lock)
{
        if (lock == NULL)
                return;

        fssh_delete_sem(lock->sem);
        lock->sem = -1;
}


extern "C" fssh_status_t
fssh_recursive_lock_lock(fssh_recursive_lock *lock)
{
        fssh_thread_id thread = fssh_find_thread(NULL);

        if (thread != lock->holder) {
                fssh_status_t status = fssh_acquire_sem(lock->sem);
                if (status < FSSH_B_OK)
                        return status;

                lock->holder = thread;
        }
        lock->recursion++;
        return FSSH_B_OK;
}


extern "C" fssh_status_t
fssh_recursive_lock_trylock(fssh_recursive_lock *lock)
{
        fssh_thread_id thread = fssh_find_thread(NULL);

        if (thread != lock->holder) {
                fssh_status_t status = fssh_acquire_sem_etc(lock->sem, 1,
                        FSSH_B_RELATIVE_TIMEOUT, 0);
                if (status < FSSH_B_OK)
                        return status;

                lock->holder = thread;
        }
        lock->recursion++;
        return FSSH_B_OK;
}


extern "C" void
fssh_recursive_lock_unlock(fssh_recursive_lock *lock)
{
        if (fssh_find_thread(NULL) != lock->holder)
                fssh_panic("recursive_lock %p unlocked by non-holder thread!\n", lock);

        if (--lock->recursion == 0) {
                lock->holder = -1;
                fssh_release_sem(lock->sem);
        }
}


extern "C" void
fssh_recursive_lock_transfer_lock(fssh_recursive_lock *lock,
        fssh_thread_id thread)
{
        if (lock->recursion != 1)
                fssh_panic("invalid recursion level for lock transfer!");

        lock->holder = thread;
}


//      #pragma mark -


extern "C" void
fssh_mutex_init(fssh_mutex *m, const char *name)
{
        if (m == NULL)
                return;

        if (name == NULL)
                name = "mutex_sem";

        m->holder = -1;

        m->sem = fssh_create_sem(1, name);
        if (m->sem < FSSH_B_OK)
                fssh_panic("could not create mutex");
}


extern "C" void
fssh_mutex_init_etc(fssh_mutex *m, const char *name, uint32_t flags)
{
        fssh_mutex_init(m, name);
}


extern "C" void
fssh_mutex_destroy(fssh_mutex *mutex)
{
        if (mutex == NULL)
                return;

        if (mutex->sem >= 0) {
                fssh_delete_sem(mutex->sem);
                mutex->sem = -1;
        }
        mutex->holder = -1;
}


extern "C" fssh_status_t
fssh_mutex_lock(fssh_mutex *mutex)
{
        fssh_thread_id me = fssh_find_thread(NULL);
        fssh_status_t status;

        status = fssh_acquire_sem(mutex->sem);
        if (status < FSSH_B_OK)
                return status;

        if (me == mutex->holder)
                fssh_panic("mutex_lock failure: mutex %p (sem = 0x%x) acquired twice by thread 0x%x\n", mutex, (int)mutex->sem, (int)me);

        mutex->holder = me;
        return FSSH_B_OK;
}


extern "C" void
fssh_mutex_unlock(fssh_mutex *mutex)
{
        fssh_thread_id me = fssh_find_thread(NULL);

        if (me != mutex->holder) {
                fssh_panic("mutex_unlock failure: thread 0x%x is trying to release mutex %p (current holder 0x%x)\n",
                        (int)me, mutex, (int)mutex->holder);
        }

        mutex->holder = -1;
        fssh_release_sem(mutex->sem);
}


extern "C" void
fssh_mutex_transfer_lock(fssh_mutex *mutex, fssh_thread_id thread)
{
        mutex->holder = thread;
}


//      #pragma mark -


extern "C" void
fssh_rw_lock_init(fssh_rw_lock *lock, const char *name)
{
        if (lock == NULL)
                return;

        if (name == NULL)
                name = "r/w lock";

        lock->count = 0;
        lock->holder = -1;

        lock->sem = fssh_create_sem(FSSH_RW_MAX_READERS, name);
        if (lock->sem < FSSH_B_OK)
                fssh_panic("could not create r/w lock");
}


extern "C" void
fssh_rw_lock_init_etc(fssh_rw_lock *lock, const char *name, uint32_t flags)
{
        fssh_rw_lock_init(lock, name);
}


extern "C" void
fssh_rw_lock_destroy(fssh_rw_lock *lock)
{
        if (lock == NULL)
                return;

        fssh_delete_sem(lock->sem);
}


extern "C" fssh_status_t
fssh_rw_lock_read_lock(fssh_rw_lock *lock)
{
        if (lock->holder == fssh_find_thread(NULL)) {
                lock->count++;
                return FSSH_B_OK;
        }

        return fssh_acquire_sem(lock->sem);
}


extern "C" fssh_status_t
fssh_rw_lock_read_unlock(fssh_rw_lock *lock)
{
        if (lock->holder == fssh_find_thread(NULL) && --lock->count > 0)
                return FSSH_B_OK;

        return fssh_release_sem(lock->sem);
}


extern "C" fssh_status_t
fssh_rw_lock_write_lock(fssh_rw_lock *lock)
{
        if (lock->holder == fssh_find_thread(NULL)) {
                lock->count++;
                return FSSH_B_OK;
        }

        fssh_status_t status = fssh_acquire_sem_etc(lock->sem, FSSH_RW_MAX_READERS,
                0, 0);
        if (status == FSSH_B_OK) {
                lock->holder = fssh_find_thread(NULL);
                lock->count = 1;
        }
        return status;
}


extern "C" fssh_status_t
fssh_rw_lock_write_unlock(fssh_rw_lock *lock)
{
        if (--lock->count > 0)
                return FSSH_B_OK;

        lock->holder = -1;

        return fssh_release_sem_etc(lock->sem, FSSH_RW_MAX_READERS, 0);
}