/** @file
 * IPRT - Critical Sections.
 */

/*
 * Copyright (C) 2006-2015 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
 * VirtualBox OSE distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 */

#ifndef ___iprt_critsect_h
#define ___iprt_critsect_h

#include <iprt/cdefs.h>
#include <iprt/types.h>
#include <iprt/assert.h>
#if defined(IN_RING3) || defined(IN_RING0)
# include <iprt/thread.h>
#endif
#ifdef RT_LOCK_STRICT_ORDER
# include <iprt/lockvalidator.h>
#endif

RT_C_DECLS_BEGIN

/** @defgroup grp_rt_critsect       RTCritSect - Critical Sections
 *
 * "Critical section" synchronization primitives can be used to
 * protect a section of code or data to which access must be exclusive;
 * only one thread can hold access to a critical section at one time.
 *
 * A critical section is a fast recursive write lock; if the critical
 * section is not acquired, then entering it is fast (requires no system
 * call). IPRT uses the Windows terminology here; on other platform, this
 * might be called a "futex" or a "fast mutex". As opposed to IPRT
 * "fast mutexes" (see @ref grp_rt_sems_fast_mutex ), critical sections
 * are recursive.
 *
 * Use RTCritSectInit to initialize a critical section; use RTCritSectEnter
 * and RTCritSectLeave to acquire and release access.
 *
 * For an overview of all types of synchronization primitives provided
 * by IPRT (event, mutex/fast mutex/read-write mutex semaphores), see
 * @ref grp_rt_sems .
 *
 * @ingroup grp_rt
 * @{
 */

/**
 * Critical section.
 */
typedef struct RTCRITSECT
{
    /** Magic used to validate the section state.
     * RTCRITSECT_MAGIC is the value of an initialized & operational section. */
    volatile uint32_t                   u32Magic;
    /** Number of lockers.
     * -1 if the section is free. */
    volatile int32_t                    cLockers;
    /** The owner thread. */
    volatile RTNATIVETHREAD             NativeThreadOwner;
    /** Number of nested enter operations performed.
     * Greater or equal to 1 if owned, 0 when free.
     */
    volatile int32_t                    cNestings;
    /** Section flags - the RTCRITSECT_FLAGS_* \#defines. */
    uint32_t                            fFlags;
    /** The semaphore to block on. */
    RTSEMEVENT                          EventSem;
    /** Lock validator record.  Only used in strict builds. */
    R3R0PTRTYPE(PRTLOCKVALRECEXCL)      pValidatorRec;
    /** Alignmnet padding. */
    RTHCPTR                             Alignment;
} RTCRITSECT;
AssertCompileSize(RTCRITSECT, HC_ARCH_BITS == 32 ? 32 : 48);

/** RTCRITSECT::u32Magic value. (Hiromi Uehara) */
#define RTCRITSECT_MAGIC                UINT32_C(0x19790326)

/** @name RTCritSectInitEx flags / RTCRITSECT::fFlags
 * @{ */
/** If set, nesting(/recursion) is not allowed. */
#define RTCRITSECT_FLAGS_NO_NESTING     UINT32_C(0x00000001)
/** Disables lock validation. */
#define RTCRITSECT_FLAGS_NO_LOCK_VAL    UINT32_C(0x00000002)
/** Bootstrap hack for use with certain memory allocator locks only! */
#define RTCRITSECT_FLAGS_BOOTSTRAP_HACK UINT32_C(0x00000004)
/** If set, the critical section becomes a dummy that doesn't serialize any
 * threads.  This flag can only be set at creation time.
 *
 * The intended use is avoiding lots of conditional code where some component
 * might or might not require entering a critical section before access. */
#define RTCRITSECT_FLAGS_NOP            UINT32_C(0x00000008)
/** Indicates that this is a ring-0 critical section. */
#define RTCRITSECT_FLAGS_RING0          UINT32_C(0x00000010)
/** @} */


#if defined(IN_RING3) || defined(IN_RING0)

/**
 * Initialize a critical section.
 */
RTDECL(int) RTCritSectInit(PRTCRITSECT pCritSect);

/**
 * Initialize a critical section.
 *
 * @returns iprt status code.
 * @param   pCritSect       Pointer to the critical section structure.
 * @param   fFlags          Flags, any combination of the RTCRITSECT_FLAGS
 *                          \#defines.
 * @param   hClass          The class (no reference consumed).  If NIL, no lock
 *                          order validation will be performed on this lock.
 * @param   uSubClass       The sub-class.  This is used to define lock order
 *                          within a class.  RTLOCKVAL_SUB_CLASS_NONE is the
 *                          recommended value here.
 * @param   pszNameFmt      Name format string for the lock validator, optional
 *                          (NULL).  Max length is 32 bytes.
 * @param   ...             Format string arguments.
 */
RTDECL(int) RTCritSectInitEx(PRTCRITSECT pCritSect, uint32_t fFlags, RTLOCKVALCLASS hClass, uint32_t uSubClass,
                             const char *pszNameFmt, ...) RT_IPRT_FORMAT_ATTR_MAYBE_NULL(5, 6);

/**
 * Changes the lock validator sub-class of the critical section.
 *
 * It is recommended to try make sure that nobody is using this critical section
 * while changing the value.
 *
 * @returns The old sub-class.  RTLOCKVAL_SUB_CLASS_INVALID is returns if the
 *          lock validator isn't compiled in or either of the parameters are
 *          invalid.
 * @param   pCritSect       The critical section.
 * @param   uSubClass       The new sub-class value.
 */
RTDECL(uint32_t) RTCritSectSetSubClass(PRTCRITSECT pCritSect, uint32_t uSubClass);

/**
 * Enter a critical section.
 *
 * @returns VINF_SUCCESS on success.
 * @returns VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 * @param   pCritSect       The critical section.
 */
RTDECL(int) RTCritSectEnter(PRTCRITSECT pCritSect);

/**
 * Enter a critical section.
 *
 * @returns IPRT status code.
 * @retval  VINF_SUCCESS on success.
 * @retval  VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 *
 * @param   pCritSect       The critical section.
 * @param   uId             Where we're entering the section.
 * @param   SRC_POS         The source position where call is being made from.
 *                          Use RT_SRC_POS when possible.  Optional.
 */
RTDECL(int) RTCritSectEnterDebug(PRTCRITSECT pCritSect, RTHCUINTPTR uId, RT_SRC_POS_DECL);

/**
 * Try enter a critical section.
 *
 * @retval  VINF_SUCCESS on success.
 * @retval  VERR_SEM_BUSY if the critsect was owned.
 * @retval  VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 *
 * @param   pCritSect   The critical section.
 */
RTDECL(int) RTCritSectTryEnter(PRTCRITSECT pCritSect);

/**
 * Try enter a critical section.
 *
 * @retval  VINF_SUCCESS on success.
 * @retval  VERR_SEM_BUSY if the critsect was owned.
 * @retval  VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 *
 * @param   pCritSect       The critical section.
 * @param   uId             Where we're entering the section.
 * @param   SRC_POS         The source position where call is being made from.
 *                          Use RT_SRC_POS when possible.  Optional.
 */
RTDECL(int) RTCritSectTryEnterDebug(PRTCRITSECT pCritSect, RTHCUINTPTR uId, RT_SRC_POS_DECL);

# ifdef IN_RING3 /* Crazy APIs: ring-3 only. */

/**
 * Enter multiple critical sections.
 *
 * This function will enter ALL the specified critical sections before returning.
 *
 * @returns VINF_SUCCESS on success.
 * @returns VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 * @param   cCritSects      Number of critical sections in the array.
 * @param   papCritSects    Array of critical section pointers.
 *
 * @remark  Please note that this function will not necessarily come out favourable in a
 *          fight with other threads which are using the normal RTCritSectEnter() function.
 *          Therefore, avoid having to enter multiple critical sections!
 */
RTDECL(int) RTCritSectEnterMultiple(size_t cCritSects, PRTCRITSECT *papCritSects);

/**
 * Enter multiple critical sections.
 *
 * This function will enter ALL the specified critical sections before returning.
 *
 * @returns VINF_SUCCESS on success.
 * @returns VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 *
 * @param   cCritSects      Number of critical sections in the array.
 * @param   papCritSects    Array of critical section pointers.
 * @param   uId             Where we're entering the section.
 * @param   SRC_POS         The source position where call is being made from.
 *                          Use RT_SRC_POS when possible.  Optional.
 *
 * @remark  See RTCritSectEnterMultiple().
 */
RTDECL(int) RTCritSectEnterMultipleDebug(size_t cCritSects, PRTCRITSECT *papCritSects, RTUINTPTR uId, RT_SRC_POS_DECL);

# endif /* IN_RING3 */

/**
 * Leave a critical section.
 *
 * @returns VINF_SUCCESS.
 * @param   pCritSect       The critical section.
 */
RTDECL(int) RTCritSectLeave(PRTCRITSECT pCritSect);

/**
 * Leave multiple critical sections.
 *
 * @returns VINF_SUCCESS.
 * @param   cCritSects      Number of critical sections in the array.
 * @param   papCritSects    Array of critical section pointers.
 */
RTDECL(int) RTCritSectLeaveMultiple(size_t cCritSects, PRTCRITSECT *papCritSects);

/**
 * Deletes a critical section.
 *
 * @returns VINF_SUCCESS.
 * @param   pCritSect       The critical section.
 */
RTDECL(int) RTCritSectDelete(PRTCRITSECT pCritSect);

/**
 * Checks the caller is the owner of the critical section.
 *
 * @returns true if owner.
 * @returns false if not owner.
 * @param   pCritSect       The critical section.
 */
DECLINLINE(bool) RTCritSectIsOwner(PCRTCRITSECT pCritSect)
{
    return pCritSect->NativeThreadOwner == RTThreadNativeSelf();
}

#endif /* IN_RING3 || IN_RING0 */

/**
 * Checks the section is owned by anyone.
 *
 * @returns true if owned.
 * @returns false if not owned.
 * @param   pCritSect       The critical section.
 */
DECLINLINE(bool) RTCritSectIsOwned(PCRTCRITSECT pCritSect)
{
    return pCritSect->NativeThreadOwner != NIL_RTNATIVETHREAD;
}

/**
 * Gets the thread id of the critical section owner.
 *
 * @returns Thread id of the owner thread if owned.
 * @returns NIL_RTNATIVETHREAD is not owned.
 * @param   pCritSect       The critical section.
 */
DECLINLINE(RTNATIVETHREAD) RTCritSectGetOwner(PCRTCRITSECT pCritSect)
{
    return pCritSect->NativeThreadOwner;
}

/**
 * Checks if a critical section is initialized or not.
 *
 * @returns true if initialized.
 * @returns false if not initialized.
 * @param   pCritSect       The critical section.
 */
DECLINLINE(bool) RTCritSectIsInitialized(PCRTCRITSECT pCritSect)
{
    return pCritSect->u32Magic == RTCRITSECT_MAGIC;
}

/**
 * Gets the recursion depth.
 *
 * @returns The recursion depth.
 * @param   pCritSect       The Critical section
 */
DECLINLINE(uint32_t) RTCritSectGetRecursion(PCRTCRITSECT pCritSect)
{
    return pCritSect->cNestings;
}

/**
 * Gets the waiter count
 *
 * @returns The waiter count
 * @param   pCritSect       The Critical section
 */
DECLINLINE(int32_t) RTCritSectGetWaiters(PCRTCRITSECT pCritSect)
{
    return pCritSect->cLockers;
}

/* Lock strict build: Remap the three enter calls to the debug versions. */
#if defined(RT_LOCK_STRICT) && !defined(RTCRITSECT_WITHOUT_REMAPPING) && !defined(RT_WITH_MANGLING)
# ifdef ___iprt_asm_h
#  define RTCritSectEnter(pCritSect)                        RTCritSectEnterDebug(pCritSect, (uintptr_t)ASMReturnAddress(), RT_SRC_POS)
#  define RTCritSectTryEnter(pCritSect)                     RTCritSectTryEnterDebug(pCritSect, (uintptr_t)ASMReturnAddress(), RT_SRC_POS)
#  define RTCritSectEnterMultiple(cCritSects, pCritSect)    RTCritSectEnterMultipleDebug((cCritSects), (pCritSect), (uintptr_t)ASMReturnAddress(), RT_SRC_POS)
# else
#  define RTCritSectEnter(pCritSect)                        RTCritSectEnterDebug(pCritSect, 0, RT_SRC_POS)
#  define RTCritSectTryEnter(pCritSect)                     RTCritSectTryEnterDebug(pCritSect, 0, RT_SRC_POS)
#  define RTCritSectEnterMultiple(cCritSects, pCritSect)    RTCritSectEnterMultipleDebug((cCritSects), (pCritSect), 0, RT_SRC_POS)
# endif
#endif

/* Strict lock order: Automatically classify locks by init location. */
#if defined(RT_LOCK_STRICT_ORDER) && defined(IN_RING3) && !defined(RTCRITSECT_WITHOUT_REMAPPING) && !defined(RT_WITH_MANGLING)
# define RTCritSectInit(pCritSect) \
    RTCritSectInitEx((pCritSect), 0 /*fFlags*/, \
                     RTLockValidatorClassForSrcPos(RT_SRC_POS, NULL), \
                     RTLOCKVAL_SUB_CLASS_NONE, NULL)
#endif

/** @}  */



/** @defgroup grp_rt_critsectrw     RTCritSectRw - Read/Write Critical Sections
 * @ingroup grp_rt
 * @{
 */

/**
 * Read/write critical section.
 */
typedef struct RTCRITSECTRW
{
    /** Magic used to validate the section state.
     * RTCRITSECTRW_MAGIC is the value of an initialized & operational section. */
    volatile uint32_t                   u32Magic;

    /** Indicates whether hEvtRead needs resetting. */
    bool volatile                       fNeedReset;
    /** Explicit alignment padding. */
    bool volatile                       afPadding[1];
    /** Section flags - the RTCRITSECT_FLAGS_* \#defines. */
    uint16_t                            fFlags;

    /** The state variable.
     * All accesses are atomic and it bits are defined like this:
     *      Bits 0..14  - cReads.
     *      Bit 15      - Unused.
     *      Bits 16..31 - cWrites. - doesn't make sense here
     *      Bit 31      - fDirection; 0=Read, 1=Write.
     *      Bits 32..46 - cWaitingReads
     *      Bit 47      - Unused.
     *      Bits 48..62 - cWaitingWrites
     *      Bit 63      - Unused.
     */
    uint64_t volatile                   u64State;
    /** The write owner. */
    RTNATIVETHREAD volatile             hNativeWriter;
    /** The number of reads made by the current writer. */
    uint32_t volatile                   cWriterReads;
    /** The number of recursions made by the current writer. (The initial grabbing
     *  of the lock counts as the first one.) */
    uint32_t volatile                   cWriteRecursions;

    /** What the writer threads are blocking on. */
    RTSEMEVENT                          hEvtWrite;
    /** What the read threads are blocking on when waiting for the writer to
     * finish. */
    RTSEMEVENTMULTI                     hEvtRead;

    /** The validator record for the writer. */
    R3R0PTRTYPE(PRTLOCKVALRECEXCL)      pValidatorWrite;
    /** The validator record for the readers. */
    R3R0PTRTYPE(PRTLOCKVALRECSHRD)      pValidatorRead;
#if HC_ARCH_BITS == 32
    /** Size padding.  */
    RTHCPTR                             HCPtrPadding;
#endif
} RTCRITSECTRW;
AssertCompileSize(RTCRITSECTRW, HC_ARCH_BITS == 32 ? 48 : 64);

/** RTCRITSECTRW::u32Magic value. (Eric Allan Dolphy, Jr.) */
#define RTCRITSECTRW_MAGIC              UINT32_C(0x19280620)
/** RTCRITSECTRW::u32Magic dead value. */
#define RTCRITSECTRW_MAGIC_DEAD         UINT32_C(0x19640629)

/** @name RTCRITSECTRW::u64State values.
 * @note Using RTCSRW instead of RTCRITSECTRW to save space.
 * @{ */
#define RTCSRW_CNT_BITS            15
#define RTCSRW_CNT_MASK            UINT64_C(0x00007fff)

#define RTCSRW_CNT_RD_SHIFT        0
#define RTCSRW_CNT_RD_MASK         (RTCSRW_CNT_MASK << RTCSRW_CNT_RD_SHIFT)
#define RTCSRW_CNT_WR_SHIFT        16
#define RTCSRW_CNT_WR_MASK         (RTCSRW_CNT_MASK << RTCSRW_CNT_WR_SHIFT)

#define RTCSRW_DIR_SHIFT           31
#define RTCSRW_DIR_MASK            RT_BIT_64(RTCSRW_DIR_SHIFT)
#define RTCSRW_DIR_READ            UINT64_C(0)
#define RTCSRW_DIR_WRITE           UINT64_C(1)

#define RTCSRW_WAIT_CNT_RD_SHIFT   32
#define RTCSRW_WAIT_CNT_RD_MASK    (RTCSRW_CNT_MASK << RTCSRW_WAIT_CNT_RD_SHIFT)
/* #define RTCSRW_WAIT_CNT_WR_SHIFT   48 */
/* #define RTCSRW_WAIT_CNT_WR_MASK    (RTCSRW_CNT_MASK << RTCSRW_WAIT_CNT_WR_SHIFT) */
/** @} */

#if defined(IN_RING3) || defined(IN_RING0)

/**
 * Initialize a critical section.
 */
RTDECL(int) RTCritSectRwInit(PRTCRITSECTRW pThis);

/**
 * Initialize a critical section.
 *
 * @returns IPRT status code.
 * @param   pThis           Pointer to the read/write critical section.
 * @param   fFlags          Flags, any combination of the RTCRITSECT_FLAGS
 *                          \#defines.
 * @param   hClass          The class (no reference consumed).  If NIL, no lock
 *                          order validation will be performed on this lock.
 * @param   uSubClass       The sub-class.  This is used to define lock order
 *                          within a class.  RTLOCKVAL_SUB_CLASS_NONE is the
 *                          recommended value here.
 * @param   pszNameFmt      Name format string for the lock validator, optional
 *                          (NULL).  Max length is 32 bytes.
 * @param   ...             Format string arguments.
 */
RTDECL(int) RTCritSectRwInitEx(PRTCRITSECTRW pThis, uint32_t fFlags, RTLOCKVALCLASS hClass, uint32_t uSubClass,
                               const char *pszNameFmt, ...) RT_IPRT_FORMAT_ATTR_MAYBE_NULL(5, 6);

/**
 * Changes the lock validator sub-class of the critical section.
 *
 * It is recommended to try make sure that nobody is using this critical section
 * while changing the value.
 *
 * @returns The old sub-class.  RTLOCKVAL_SUB_CLASS_INVALID is returns if the
 *          lock validator isn't compiled in or either of the parameters are
 *          invalid.
 * @param   pThis           Pointer to the read/write critical section.
 * @param   uSubClass       The new sub-class value.
 */
RTDECL(uint32_t) RTCritSectRwSetSubClass(PRTCRITSECTRW pThis, uint32_t uSubClass);


/**
 * Enter a critical section with shared (read) access.
 *
 * @returns IPRT status code.
 * @retval  VINF_SUCCESS on success.
 * @retval  VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 * @param   pThis           Pointer to the read/write critical section.
 */
RTDECL(int) RTCritSectRwEnterShared(PRTCRITSECTRW pThis);

/**
 * Enter a critical section with shared (read) access.
 *
 * @returns IPRT status code.
 * @retval  VINF_SUCCESS on success.
 * @retval  VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 *
 * @param   pThis           Pointer to the read/write critical section.
 * @param   uId             Where we're entering the section.
 * @param   SRC_POS         The source position where call is being made from.
 *                          Use RT_SRC_POS when possible.  Optional.
 */
RTDECL(int) RTCritSectRwEnterSharedDebug(PRTCRITSECTRW pThis, RTHCUINTPTR uId, RT_SRC_POS_DECL);

/**
 * Try enter a critical section with shared (read) access.
 *
 * @returns IPRT status code.
 * @retval  VINF_SUCCESS on success.
 * @retval  VERR_SEM_BUSY if the critsect was owned.
 * @retval  VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 *
 * @param   pThis           Pointer to the read/write critical section.
 */
RTDECL(int) RTCritSectRwTryEnterShared(PRTCRITSECTRW pThis);

/**
 * Try enter a critical section with shared (read) access.
 *
 * @returns IPRT status code.
 * @retval  VINF_SUCCESS on success.
 * @retval  VERR_SEM_BUSY if the critsect was owned.
 * @retval  VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 *
 * @param   pThis           Pointer to the read/write critical section.
 * @param   uId             Where we're entering the section.
 * @param   SRC_POS         The source position where call is being made from.
 *                          Use RT_SRC_POS when possible.  Optional.
 */
RTDECL(int) RTCritSectRwTryEnterSharedDebug(PRTCRITSECTRW pThis, RTHCUINTPTR uId, RT_SRC_POS_DECL);

/**
 * Leave a critical section held with shared access.
 *
 * @returns IPRT status code.
 * @param   pThis           Pointer to the read/write critical section.
 */
RTDECL(int) RTCritSectRwLeaveShared(PRTCRITSECTRW pThis);


/**
 * Enter a critical section with exclusive (write) access.
 *
 * @returns IPRT status code.
 * @retval  VINF_SUCCESS on success.
 * @retval  VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 * @param   pThis           Pointer to the read/write critical section.
 */
RTDECL(int) RTCritSectRwEnterExcl(PRTCRITSECTRW pThis);

/**
 * Enter a critical section with exclusive (write) access.
 *
 * @retval  VINF_SUCCESS on success.
 * @retval  VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 *
 * @param   pThis           Pointer to the read/write critical section.
 * @param   uId             Where we're entering the section.
 * @param   SRC_POS         The source position where call is being made from.
 *                          Use RT_SRC_POS when possible.  Optional.
 */
RTDECL(int) RTCritSectRwEnterExclDebug(PRTCRITSECTRW pThis, RTHCUINTPTR uId, RT_SRC_POS_DECL);

/**
 * Try enter a critical section with exclusive (write) access.
 *
 * @returns IPRT status code.
 * @retval  VINF_SUCCESS on success.
 * @retval  VERR_SEM_BUSY if the critsect was owned.
 * @retval  VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 *
 * @param   pThis           Pointer to the read/write critical section.
 */
RTDECL(int) RTCritSectRwTryEnterExcl(PRTCRITSECTRW pThis);

/**
 * Try enter a critical section with exclusive (write) access.
 *
 * @returns IPRT status code.
 * @retval  VINF_SUCCESS on success.
 * @retval  VERR_SEM_BUSY if the critsect was owned.
 * @retval  VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
 * @retval  VERR_SEM_DESTROYED if the critical section is delete before or
 *          during the operation.
 *
 * @param   pThis           Pointer to the read/write critical section.
 * @param   uId             Where we're entering the section.
 * @param   SRC_POS         The source position where call is being made from.
 *                          Use RT_SRC_POS when possible.  Optional.
 */
RTDECL(int) RTCritSectRwTryEnterExclDebug(PRTCRITSECTRW pThis, RTHCUINTPTR uId, RT_SRC_POS_DECL);

/**
 * Leave a critical section held exclusively.
 *
 * @returns IPRT status code; VINF_SUCCESS, VERR_NOT_OWNER, VERR_SEM_DESTROYED,
 *          or VERR_WRONG_ORDER.
 * @param   pThis           Pointer to the read/write critical section.
 */
RTDECL(int) RTCritSectRwLeaveExcl(PRTCRITSECTRW pThis);


/**
 * Deletes a critical section.
 *
 * @returns VINF_SUCCESS.
 * @param   pThis           Pointer to the read/write critical section.
 */
RTDECL(int) RTCritSectRwDelete(PRTCRITSECTRW pThis);

/**
 * Checks the caller is the exclusive (write) owner of the critical section.
 *
 * @retval  true if owner.
 * @retval  false if not owner.
 * @param   pThis           Pointer to the read/write critical section.
 */
RTDECL(bool) RTCritSectRwIsWriteOwner(PRTCRITSECTRW pThis);

/**
 * Checks if the caller is one of the read owners of the critical section.
 *
 * @note    !CAUTION!  This API doesn't work reliably if lock validation isn't
 *          enabled. Meaning, the answer is not trustworhty unless
 *          RT_LOCK_STRICT or RTCRITSECTRW_STRICT was defined at build time.
 *          Also, make sure you do not use RTCRITSECTRW_FLAGS_NO_LOCK_VAL when
 *          creating the semaphore.  And finally, if you used a locking class,
 *          don't disable deadlock detection by setting cMsMinDeadlock to
 *          RT_INDEFINITE_WAIT.
 *
 *          In short, only use this for assertions.
 *
 * @returns @c true if reader, @c false if not.
 * @param   pThis           Pointer to the read/write critical section.
 * @param   fWannaHear      What you'd like to hear when lock validation is not
 *                          available.  (For avoiding asserting all over the
 *                          place.)
 */
RTDECL(bool) RTCritSectRwIsReadOwner(PRTCRITSECTRW pThis, bool fWannaHear);

/**
 * Gets the write recursion count.
 *
 * @returns The write recursion count (0 if bad critsect).
 * @param   pThis           Pointer to the read/write critical section.
 */
RTDECL(uint32_t) RTCritSectRwGetWriteRecursion(PRTCRITSECTRW pThis);

/**
 * Gets the read recursion count of the current writer.
 *
 * @returns The read recursion count (0 if bad critsect).
 * @param   pThis           Pointer to the read/write critical section.
 */
RTDECL(uint32_t) RTCritSectRwGetWriterReadRecursion(PRTCRITSECTRW pThis);

/**
 * Gets the current number of reads.
 *
 * This includes all read recursions, so it might be higher than the number of
 * read owners.  It does not include reads done by the current writer.
 *
 * @returns The read count (0 if bad critsect).
 * @param   pThis           Pointer to the read/write critical section.
 */
RTDECL(uint32_t) RTCritSectRwGetReadCount(PRTCRITSECTRW pThis);

#endif /* IN_RING3 || IN_RING0 */

/**
 * Checks if a critical section is initialized or not.
 *
 * @retval  true if initialized.
 * @retval  false if not initialized.
 * @param   pThis           Pointer to the read/write critical section.
 */
DECLINLINE(bool) RTCritSectRwIsInitialized(PCRTCRITSECTRW pThis)
{
    return pThis->u32Magic == RTCRITSECTRW_MAGIC;
}

/* Lock strict build: Remap the three enter calls to the debug versions. */
#if defined(RT_LOCK_STRICT) && !defined(RTCRITSECTRW_WITHOUT_REMAPPING) && !defined(RT_WITH_MANGLING)
# ifdef ___iprt_asm_h
#  define RTCritSectRwEnterExcl(pThis)      RTCritSectRwEnterExclDebug(pThis, (uintptr_t)ASMReturnAddress(), RT_SRC_POS)
#  define RTCritSectRwTryEnterExcl(pThis)   RTCritSectRwTryEnterExclDebug(pThis, (uintptr_t)ASMReturnAddress(), RT_SRC_POS)
#  define RTCritSectRwEnterShared(pThis)    RTCritSectRwEnterSharedDebug(pThis, (uintptr_t)ASMReturnAddress(), RT_SRC_POS)
#  define RTCritSectRwTryEnterShared(pThis) RTCritSectRwTryEnterSharedDebug(pThis, (uintptr_t)ASMReturnAddress(), RT_SRC_POS)
# else
#  define RTCritSectRwEnterExcl(pThis)      RTCritSectRwEnterExclDebug(pThis, 0, RT_SRC_POS)
#  define RTCritSectRwTryEnterExcl(pThis)   RTCritSectRwTryEnterExclDebug(pThis, 0, RT_SRC_POS)
#  define RTCritSectRwEnterShared(pThis)    RTCritSectRwEnterSharedDebug(pThis, 0, RT_SRC_POS)
#  define RTCritSectRwTryEnterShared(pThis) RTCritSectRwTryEnterSharedDebug(pThis, 0, RT_SRC_POS)
# endif
#endif

/* Strict lock order: Automatically classify locks by init location. */
#if defined(RT_LOCK_STRICT_ORDER) && defined(IN_RING3) && !defined(RTCRITSECTRW_WITHOUT_REMAPPING) && !defined(RT_WITH_MANGLING)
# define RTCritSectRwInit(a_pThis) \
    RTCritSectRwInitEx((a_pThis), 0 /*fFlags*/, \
                       RTLockValidatorClassForSrcPos(RT_SRC_POS, NULL), \
                        RTLOCKVAL_SUB_CLASS_NONE, NULL)
#endif

/** @} */

RT_C_DECLS_END

#endif

