root/src/servers/app/MultiLocker.h
/*
 * Copyright 2005-2009, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT license.
 *
 * Copyright 1999, Be Incorporated. All Rights Reserved.
 * This file may be used under the terms of the Be Sample Code License.
 */
#ifndef MULTI_LOCKER_H
#define MULTI_LOCKER_H


/*! multiple-reader single-writer locking class

        IMPORTANT:
         * nested read locks are not supported
         * a reader becoming the write is not supported
         * nested write locks are supported
         * a writer can do read locks, even nested ones
         * in case of problems, #define DEBUG 1 in the .cpp
*/


#include <OS.h>
#include <locks.h>


#define MULTI_LOCKER_TIMING     0
#if DEBUG
#       include <assert.h>
#       define MULTI_LOCKER_DEBUG       DEBUG
#endif

#if MULTI_LOCKER_DEBUG
#       define ASSERT_MULTI_LOCKED(x) assert((x).IsWriteLocked() || (x).IsReadLocked())
#       define ASSERT_MULTI_READ_LOCKED(x) assert((x).IsReadLocked())
#       define ASSERT_MULTI_WRITE_LOCKED(x) assert((x).IsWriteLocked())
#else
#       define MULTI_LOCKER_DEBUG       0
#       define ASSERT_MULTI_LOCKED(x) ;
#       define ASSERT_MULTI_READ_LOCKED(x) ;
#       define ASSERT_MULTI_WRITE_LOCKED(x) ;
#endif


class MultiLocker {
public:
                                                                MultiLocker(const char* baseName);
        virtual                                         ~MultiLocker();

                        status_t                        InitCheck();

                        // locking for reading or writing
                        bool                            ReadLock();
                        bool                            WriteLock();

                        // unlocking after reading or writing
                        bool                            ReadUnlock();
                        bool                            WriteUnlock();

                        // does the current thread hold a write lock?
                        bool                            IsWriteLocked() const;

#if MULTI_LOCKER_DEBUG
                        // in DEBUG mode returns whether the lock is held
                        // in non-debug mode returns true
                        bool                            IsReadLocked() const;
#endif

private:
                                                                MultiLocker();
                                                                MultiLocker(const MultiLocker& other);
                        MultiLocker&            operator=(const MultiLocker& other);
                                                                        // not implemented

#if !MULTI_LOCKER_DEBUG
                        rw_lock                         fLock;
#else
                        // functions for managing the DEBUG reader array
                        void                            _RegisterThread();
                        void                            _UnregisterThread();

                        sem_id                          fLock;
                        int32*                          fDebugArray;
                        int32                           fMaxThreads;
                        int32                           fWriterNest;
                        thread_id                       fWriterThread;
#endif  // MULTI_LOCKER_DEBUG

                        status_t                        fInit;

#if MULTI_LOCKER_TIMING
                        uint32                          rl_count;
                        bigtime_t                       rl_time;
                        uint32                          ru_count;
                        bigtime_t                       ru_time;
                        uint32                          wl_count;
                        bigtime_t                       wl_time;
                        uint32                          wu_count;
                        bigtime_t                       wu_time;
                        uint32                          islock_count;
                        bigtime_t                       islock_time;
#endif
};


class AutoWriteLocker {
public:
        AutoWriteLocker(MultiLocker* lock)
                :
                fLock(*lock)
        {
                fLocked = fLock.WriteLock();
        }

        AutoWriteLocker(MultiLocker& lock)
                :
                fLock(lock)
        {
                fLocked = fLock.WriteLock();
        }

        ~AutoWriteLocker()
        {
                if (fLocked)
                        fLock.WriteUnlock();
        }

        bool IsLocked() const
        {
                return fLock.IsWriteLocked();
        }

        void Unlock()
        {
                if (fLocked) {
                        fLock.WriteUnlock();
                        fLocked = false;
                }
        }

private:
        MultiLocker&    fLock;
        bool                    fLocked;
};


class AutoReadLocker {
public:
        AutoReadLocker(MultiLocker* lock)
                :
                fLock(*lock)
        {
                fLocked = fLock.ReadLock();
        }

        AutoReadLocker(MultiLocker& lock)
                :
                fLock(lock)
        {
                fLocked = fLock.ReadLock();
        }

        ~AutoReadLocker()
        {
                Unlock();
        }

        void Unlock()
        {
                if (fLocked) {
                        fLock.ReadUnlock();
                        fLocked = false;
                }
        }

private:
        MultiLocker&    fLock;
        bool                    fLocked;
};

#endif  // MULTI_LOCKER_H