    {- |
    Copyright  : Will Thompson, Iñaki García Etxebarria and Jonas Platte
    License    : LGPL-2.1
    Maintainer : Iñaki García Etxebarria (garetxe@gmail.com)

    The GRWLock struct is an opaque data structure to represent a
    reader-writer lock. It is similar to a 'GI.GLib.Unions.Mutex.Mutex' in that it allows
    multiple threads to coordinate access to a shared resource.

    The difference to a mutex is that a reader-writer lock discriminates
    between read-only (\'reader\') and full (\'writer\') access. While only
    one thread at a time is allowed write access (by holding the \'writer\'
    lock via 'GI.GLib.Structs.RWLock.rWLockWriterLock'), multiple threads can gain
    simultaneous read-only access (by holding the \'reader\' lock via
    'GI.GLib.Structs.RWLock.rWLockReaderLock').

    It is unspecified whether readers or writers have priority in acquiring the
    lock when a reader already holds the lock and a writer is queued to acquire
    it.

    Here is an example for an array with access functions:

    === /C code/
    >
    >  GRWLock lock;
    >  GPtrArray *array;
    >
    >  gpointer
    >  my_array_get (guint index)
    >  {
    >    gpointer retval = NULL;
    >
    >    if (!array)
    >      return NULL;
    >
    >    g_rw_lock_reader_lock (&lock);
    >    if (index < array->len)
    >      retval = g_ptr_array_index (array, index);
    >    g_rw_lock_reader_unlock (&lock);
    >
    >    return retval;
    >  }
    >
    >  void
    >  my_array_set (guint index, gpointer data)
    >  {
    >    g_rw_lock_writer_lock (&lock);
    >
    >    if (!array)
    >      array = g_ptr_array_new ();
    >
    >    if (index >= array->len)
    >      g_ptr_array_set_size (array, index+1);
    >    g_ptr_array_index (array, index) = data;
    >
    >    g_rw_lock_writer_unlock (&lock);
    >  }
    >

    This example shows an array which can be accessed by many readers
    (the @/my_array_get()/@ function) simultaneously, whereas the writers
    (the @/my_array_set()/@ function) will only be allowed one at a time
    and only if no readers currently access the array. This is because
    of the potentially dangerous resizing of the array. Using these
    functions is fully multi-thread safe now.

    If a 'GI.GLib.Structs.RWLock.RWLock' is allocated in static storage then it can be used
    without initialisation.  Otherwise, you should call
    'GI.GLib.Structs.RWLock.rWLockInit' on it and 'GI.GLib.Structs.RWLock.rWLockClear' when done.

    A GRWLock should only be accessed with the g_rw_lock_ functions.

    /Since: 2.32/
    -}

    #define ENABLE_OVERLOADING \
           (!defined(__HADDOCK_VERSION__))

    module GI.GLib.Structs.RWLock
        (

    -- * Exported types
        RWLock(..)                              ,
        newZeroRWLock                           ,
        noRWLock                                ,

     -- * Methods
    -- ** clear #method:clear#

    #if ENABLE_OVERLOADING
        RWLockClearMethodInfo                   ,
    #endif
        rWLockClear                             ,

    -- ** init #method:init#

    #if ENABLE_OVERLOADING
        RWLockInitMethodInfo                    ,
    #endif
        rWLockInit                              ,

    -- ** readerLock #method:readerLock#

    #if ENABLE_OVERLOADING
        RWLockReaderLockMethodInfo              ,
    #endif
        rWLockReaderLock                        ,

    -- ** readerTrylock #method:readerTrylock#

    #if ENABLE_OVERLOADING
        RWLockReaderTrylockMethodInfo           ,
    #endif
        rWLockReaderTrylock                     ,

    -- ** readerUnlock #method:readerUnlock#

    #if ENABLE_OVERLOADING
        RWLockReaderUnlockMethodInfo            ,
    #endif
        rWLockReaderUnlock                      ,

    -- ** writerLock #method:writerLock#

    #if ENABLE_OVERLOADING
        RWLockWriterLockMethodInfo              ,
    #endif
        rWLockWriterLock                        ,

    -- ** writerTrylock #method:writerTrylock#

    #if ENABLE_OVERLOADING
        RWLockWriterTrylockMethodInfo           ,
    #endif
        rWLockWriterTrylock                     ,

    -- ** writerUnlock #method:writerUnlock#

    #if ENABLE_OVERLOADING
        RWLockWriterUnlockMethodInfo            ,
    #endif
        rWLockWriterUnlock                      ,

        ) where

    import Data.GI.Base.ShortPrelude
    import qualified Data.GI.Base.ShortPrelude as SP
    import qualified Data.GI.Base.Overloading as O
    import qualified Prelude as P

    import qualified Data.GI.Base.Attributes as GI.Attributes
    import qualified Data.GI.Base.ManagedPtr as B.ManagedPtr
    import qualified Data.GI.Base.GError as B.GError
    import qualified Data.GI.Base.GVariant as B.GVariant
    import qualified Data.GI.Base.GValue as B.GValue
    import qualified Data.GI.Base.GParamSpec as B.GParamSpec
    import qualified Data.GI.Base.CallStack as B.CallStack
    import qualified Data.Text as T
    import qualified Data.ByteString.Char8 as B
    import qualified Data.Map as Map
    import qualified Foreign.Ptr as FP

    -- | Memory-managed wrapper type.
    newtype RWLock = RWLock (ManagedPtr RWLock)
    instance WrappedPtr RWLock where
        wrappedPtrCalloc = callocBytes 16
        wrappedPtrCopy = \p -> withManagedPtr p (copyBytes 16 >=> wrapPtr RWLock)
        wrappedPtrFree = Just ptr_to_g_free

    -- | Construct a `RWLock` struct initialized to zero.
    newZeroRWLock :: MonadIO m => m RWLock
    newZeroRWLock = liftIO $ wrappedPtrCalloc >>= wrapPtr RWLock

    instance tag ~ 'AttrSet => Constructible RWLock tag where
        new _ attrs = do
            o <- newZeroRWLock
            GI.Attributes.set o attrs
            return o

    -- | A convenience alias for `Nothing` :: `Maybe` `RWLock`.
    noRWLock :: Maybe RWLock
    noRWLock = Nothing

    #if ENABLE_OVERLOADING
    instance O.HasAttributeList RWLock
    type instance O.AttributeList RWLock = RWLockAttributeList
    type RWLockAttributeList = ('[ ] :: [(Symbol, *)])
    #endif

    -- method RWLock::clear
    -- method type : OrdinaryMethod
    -- Args : [Arg {argCName = "rw_lock", argType = TInterface (Name {namespace = "GLib", name = "RWLock"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "an initialized #GRWLock", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
    -- Lengths : []
    -- returnType : Nothing
    -- throws : False
    -- Skip return : False

    foreign import ccall "g_rw_lock_clear" g_rw_lock_clear ::
        Ptr RWLock ->                           -- rw_lock : TInterface (Name {namespace = "GLib", name = "RWLock"})
        IO ()

    {- |
    Frees the resources allocated to a lock with 'GI.GLib.Structs.RWLock.rWLockInit'.

    This function should not be used with a 'GI.GLib.Structs.RWLock.RWLock' that has been
    statically allocated.

    Calling 'GI.GLib.Structs.RWLock.rWLockClear' when any thread holds the lock
    leads to undefined behaviour.

    Sine: 2.32
    -}
    rWLockClear ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        RWLock
        {- ^ /@rwLock@/: an initialized 'GI.GLib.Structs.RWLock.RWLock' -}
        -> m ()
    rWLockClear rwLock = liftIO $ do
        rwLock' <- unsafeManagedPtrGetPtr rwLock
        g_rw_lock_clear rwLock'
        touchManagedPtr rwLock
        return ()

    #if ENABLE_OVERLOADING
    data RWLockClearMethodInfo
    instance (signature ~ (m ()), MonadIO m) => O.MethodInfo RWLockClearMethodInfo RWLock signature where
        overloadedMethod _ = rWLockClear

    #endif

    -- method RWLock::init
    -- method type : OrdinaryMethod
    -- Args : [Arg {argCName = "rw_lock", argType = TInterface (Name {namespace = "GLib", name = "RWLock"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "an uninitialized #GRWLock", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
    -- Lengths : []
    -- returnType : Nothing
    -- throws : False
    -- Skip return : False

    foreign import ccall "g_rw_lock_init" g_rw_lock_init ::
        Ptr RWLock ->                           -- rw_lock : TInterface (Name {namespace = "GLib", name = "RWLock"})
        IO ()

    {- |
    Initializes a 'GI.GLib.Structs.RWLock.RWLock' so that it can be used.

    This function is useful to initialize a lock that has been
    allocated on the stack, or as part of a larger structure.  It is not
    necessary to initialise a reader-writer lock that has been statically
    allocated.

    === /C code/
    >
    >  typedef struct {
    >    GRWLock l;
    >    ...
    >  } Blob;
    >
    >Blob *b;
    >
    >b = g_new (Blob, 1);
    >g_rw_lock_init (&b->l);

    To undo the effect of 'GI.GLib.Structs.RWLock.rWLockInit' when a lock is no longer
    needed, use 'GI.GLib.Structs.RWLock.rWLockClear'.

    Calling 'GI.GLib.Structs.RWLock.rWLockInit' on an already initialized 'GI.GLib.Structs.RWLock.RWLock' leads
    to undefined behaviour.

    /Since: 2.32/
    -}
    rWLockInit ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        RWLock
        {- ^ /@rwLock@/: an uninitialized 'GI.GLib.Structs.RWLock.RWLock' -}
        -> m ()
    rWLockInit rwLock = liftIO $ do
        rwLock' <- unsafeManagedPtrGetPtr rwLock
        g_rw_lock_init rwLock'
        touchManagedPtr rwLock
        return ()

    #if ENABLE_OVERLOADING
    data RWLockInitMethodInfo
    instance (signature ~ (m ()), MonadIO m) => O.MethodInfo RWLockInitMethodInfo RWLock signature where
        overloadedMethod _ = rWLockInit

    #endif

    -- method RWLock::reader_lock
    -- method type : OrdinaryMethod
    -- Args : [Arg {argCName = "rw_lock", argType = TInterface (Name {namespace = "GLib", name = "RWLock"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GRWLock", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
    -- Lengths : []
    -- returnType : Nothing
    -- throws : False
    -- Skip return : False

    foreign import ccall "g_rw_lock_reader_lock" g_rw_lock_reader_lock ::
        Ptr RWLock ->                           -- rw_lock : TInterface (Name {namespace = "GLib", name = "RWLock"})
        IO ()

    {- |
    Obtain a read lock on /@rwLock@/. If another thread currently holds
    the write lock on /@rwLock@/ or blocks waiting for it, the current
    thread will block. Read locks can be taken recursively.

    It is implementation-defined how many threads are allowed to
    hold read locks on the same lock simultaneously. If the limit is hit,
    or if a deadlock is detected, a critical warning will be emitted.

    /Since: 2.32/
    -}
    rWLockReaderLock ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        RWLock
        {- ^ /@rwLock@/: a 'GI.GLib.Structs.RWLock.RWLock' -}
        -> m ()
    rWLockReaderLock rwLock = liftIO $ do
        rwLock' <- unsafeManagedPtrGetPtr rwLock
        g_rw_lock_reader_lock rwLock'
        touchManagedPtr rwLock
        return ()

    #if ENABLE_OVERLOADING
    data RWLockReaderLockMethodInfo
    instance (signature ~ (m ()), MonadIO m) => O.MethodInfo RWLockReaderLockMethodInfo RWLock signature where
        overloadedMethod _ = rWLockReaderLock

    #endif

    -- method RWLock::reader_trylock
    -- method type : OrdinaryMethod
    -- Args : [Arg {argCName = "rw_lock", argType = TInterface (Name {namespace = "GLib", name = "RWLock"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GRWLock", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
    -- Lengths : []
    -- returnType : Just (TBasicType TBoolean)
    -- throws : False
    -- Skip return : False

    foreign import ccall "g_rw_lock_reader_trylock" g_rw_lock_reader_trylock ::
        Ptr RWLock ->                           -- rw_lock : TInterface (Name {namespace = "GLib", name = "RWLock"})
        IO CInt

    {- |
    Tries to obtain a read lock on /@rwLock@/ and returns 'True' if
    the read lock was successfully obtained. Otherwise it
    returns 'False'.

    /Since: 2.32/
    -}
    rWLockReaderTrylock ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        RWLock
        {- ^ /@rwLock@/: a 'GI.GLib.Structs.RWLock.RWLock' -}
        -> m Bool
        {- ^ __Returns:__ 'True' if /@rwLock@/ could be locked -}
    rWLockReaderTrylock rwLock = liftIO $ do
        rwLock' <- unsafeManagedPtrGetPtr rwLock
        result <- g_rw_lock_reader_trylock rwLock'
        let result' = (/= 0) result
        touchManagedPtr rwLock
        return result'

    #if ENABLE_OVERLOADING
    data RWLockReaderTrylockMethodInfo
    instance (signature ~ (m Bool), MonadIO m) => O.MethodInfo RWLockReaderTrylockMethodInfo RWLock signature where
        overloadedMethod _ = rWLockReaderTrylock

    #endif

    -- method RWLock::reader_unlock
    -- method type : OrdinaryMethod
    -- Args : [Arg {argCName = "rw_lock", argType = TInterface (Name {namespace = "GLib", name = "RWLock"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GRWLock", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
    -- Lengths : []
    -- returnType : Nothing
    -- throws : False
    -- Skip return : False

    foreign import ccall "g_rw_lock_reader_unlock" g_rw_lock_reader_unlock ::
        Ptr RWLock ->                           -- rw_lock : TInterface (Name {namespace = "GLib", name = "RWLock"})
        IO ()

    {- |
    Release a read lock on /@rwLock@/.

    Calling 'GI.GLib.Structs.RWLock.rWLockReaderUnlock' on a lock that is not held
    by the current thread leads to undefined behaviour.

    /Since: 2.32/
    -}
    rWLockReaderUnlock ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        RWLock
        {- ^ /@rwLock@/: a 'GI.GLib.Structs.RWLock.RWLock' -}
        -> m ()
    rWLockReaderUnlock rwLock = liftIO $ do
        rwLock' <- unsafeManagedPtrGetPtr rwLock
        g_rw_lock_reader_unlock rwLock'
        touchManagedPtr rwLock
        return ()

    #if ENABLE_OVERLOADING
    data RWLockReaderUnlockMethodInfo
    instance (signature ~ (m ()), MonadIO m) => O.MethodInfo RWLockReaderUnlockMethodInfo RWLock signature where
        overloadedMethod _ = rWLockReaderUnlock

    #endif

    -- method RWLock::writer_lock
    -- method type : OrdinaryMethod
    -- Args : [Arg {argCName = "rw_lock", argType = TInterface (Name {namespace = "GLib", name = "RWLock"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GRWLock", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
    -- Lengths : []
    -- returnType : Nothing
    -- throws : False
    -- Skip return : False

    foreign import ccall "g_rw_lock_writer_lock" g_rw_lock_writer_lock ::
        Ptr RWLock ->                           -- rw_lock : TInterface (Name {namespace = "GLib", name = "RWLock"})
        IO ()

    {- |
    Obtain a write lock on /@rwLock@/. If any thread already holds
    a read or write lock on /@rwLock@/, the current thread will block
    until all other threads have dropped their locks on /@rwLock@/.

    /Since: 2.32/
    -}
    rWLockWriterLock ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        RWLock
        {- ^ /@rwLock@/: a 'GI.GLib.Structs.RWLock.RWLock' -}
        -> m ()
    rWLockWriterLock rwLock = liftIO $ do
        rwLock' <- unsafeManagedPtrGetPtr rwLock
        g_rw_lock_writer_lock rwLock'
        touchManagedPtr rwLock
        return ()

    #if ENABLE_OVERLOADING
    data RWLockWriterLockMethodInfo
    instance (signature ~ (m ()), MonadIO m) => O.MethodInfo RWLockWriterLockMethodInfo RWLock signature where
        overloadedMethod _ = rWLockWriterLock

    #endif

    -- method RWLock::writer_trylock
    -- method type : OrdinaryMethod
    -- Args : [Arg {argCName = "rw_lock", argType = TInterface (Name {namespace = "GLib", name = "RWLock"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GRWLock", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
    -- Lengths : []
    -- returnType : Just (TBasicType TBoolean)
    -- throws : False
    -- Skip return : False

    foreign import ccall "g_rw_lock_writer_trylock" g_rw_lock_writer_trylock ::
        Ptr RWLock ->                           -- rw_lock : TInterface (Name {namespace = "GLib", name = "RWLock"})
        IO CInt

    {- |
    Tries to obtain a write lock on /@rwLock@/. If any other thread holds
    a read or write lock on /@rwLock@/, it immediately returns 'False'.
    Otherwise it locks /@rwLock@/ and returns 'True'.

    /Since: 2.32/
    -}
    rWLockWriterTrylock ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        RWLock
        {- ^ /@rwLock@/: a 'GI.GLib.Structs.RWLock.RWLock' -}
        -> m Bool
        {- ^ __Returns:__ 'True' if /@rwLock@/ could be locked -}
    rWLockWriterTrylock rwLock = liftIO $ do
        rwLock' <- unsafeManagedPtrGetPtr rwLock
        result <- g_rw_lock_writer_trylock rwLock'
        let result' = (/= 0) result
        touchManagedPtr rwLock
        return result'

    #if ENABLE_OVERLOADING
    data RWLockWriterTrylockMethodInfo
    instance (signature ~ (m Bool), MonadIO m) => O.MethodInfo RWLockWriterTrylockMethodInfo RWLock signature where
        overloadedMethod _ = rWLockWriterTrylock

    #endif

    -- method RWLock::writer_unlock
    -- method type : OrdinaryMethod
    -- Args : [Arg {argCName = "rw_lock", argType = TInterface (Name {namespace = "GLib", name = "RWLock"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GRWLock", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
    -- Lengths : []
    -- returnType : Nothing
    -- throws : False
    -- Skip return : False

    foreign import ccall "g_rw_lock_writer_unlock" g_rw_lock_writer_unlock ::
        Ptr RWLock ->                           -- rw_lock : TInterface (Name {namespace = "GLib", name = "RWLock"})
        IO ()

    {- |
    Release a write lock on /@rwLock@/.

    Calling 'GI.GLib.Structs.RWLock.rWLockWriterUnlock' on a lock that is not held
    by the current thread leads to undefined behaviour.

    /Since: 2.32/
    -}
    rWLockWriterUnlock ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        RWLock
        {- ^ /@rwLock@/: a 'GI.GLib.Structs.RWLock.RWLock' -}
        -> m ()
    rWLockWriterUnlock rwLock = liftIO $ do
        rwLock' <- unsafeManagedPtrGetPtr rwLock
        g_rw_lock_writer_unlock rwLock'
        touchManagedPtr rwLock
        return ()

    #if ENABLE_OVERLOADING
    data RWLockWriterUnlockMethodInfo
    instance (signature ~ (m ()), MonadIO m) => O.MethodInfo RWLockWriterUnlockMethodInfo RWLock signature where
        overloadedMethod _ = rWLockWriterUnlock

    #endif

    #if ENABLE_OVERLOADING
    type family ResolveRWLockMethod (t :: Symbol) (o :: *) :: * where
        ResolveRWLockMethod "clear" o = RWLockClearMethodInfo
        ResolveRWLockMethod "init" o = RWLockInitMethodInfo
        ResolveRWLockMethod "readerLock" o = RWLockReaderLockMethodInfo
        ResolveRWLockMethod "readerTrylock" o = RWLockReaderTrylockMethodInfo
        ResolveRWLockMethod "readerUnlock" o = RWLockReaderUnlockMethodInfo
        ResolveRWLockMethod "writerLock" o = RWLockWriterLockMethodInfo
        ResolveRWLockMethod "writerTrylock" o = RWLockWriterTrylockMethodInfo
        ResolveRWLockMethod "writerUnlock" o = RWLockWriterUnlockMethodInfo
        ResolveRWLockMethod l o = O.MethodResolutionFailed l o

    instance (info ~ ResolveRWLockMethod t RWLock, O.MethodInfo info RWLock p) => O.IsLabelProxy t (RWLock -> p) where
        fromLabelProxy _ = O.overloadedMethod (O.MethodProxy :: O.MethodProxy info)

    #if MIN_VERSION_base(4,9,0)
    instance (info ~ ResolveRWLockMethod t RWLock, O.MethodInfo info RWLock p) => O.IsLabel t (RWLock -> p) where
    #if MIN_VERSION_base(4,10,0)
        fromLabel = O.overloadedMethod (O.MethodProxy :: O.MethodProxy info)
    #else
        fromLabel _ = O.overloadedMethod (O.MethodProxy :: O.MethodProxy info)
    #endif
    #endif

    #endif