{- |
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').

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.
-}

module GI.GLib.Structs.RWLock
    ( 

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


 -- * Methods
-- ** clear #method:clear#
#ifdef ENABLE_OVERLOADING
    RWLockClearMethodInfo                   ,
#endif
    rWLockClear                             ,


-- ** init #method:init#
#ifdef ENABLE_OVERLOADING
    RWLockInitMethodInfo                    ,
#endif
    rWLockInit                              ,


-- ** readerLock #method:readerLock#
#ifdef ENABLE_OVERLOADING
    RWLockReaderLockMethodInfo              ,
#endif
    rWLockReaderLock                        ,


-- ** readerTrylock #method:readerTrylock#
#ifdef ENABLE_OVERLOADING
    RWLockReaderTrylockMethodInfo           ,
#endif
    rWLockReaderTrylock                     ,


-- ** readerUnlock #method:readerUnlock#
#ifdef ENABLE_OVERLOADING
    RWLockReaderUnlockMethodInfo            ,
#endif
    rWLockReaderUnlock                      ,


-- ** writerLock #method:writerLock#
#ifdef ENABLE_OVERLOADING
    RWLockWriterLockMethodInfo              ,
#endif
    rWLockWriterLock                        ,


-- ** writerTrylock #method:writerTrylock#
#ifdef ENABLE_OVERLOADING
    RWLockWriterTrylockMethodInfo           ,
#endif
    rWLockWriterTrylock                     ,


-- ** writerUnlock #method:writerUnlock#
#ifdef 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.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


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


noRWLock :: Maybe RWLock
noRWLock = Nothing


#ifdef 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 ()

#ifdef 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 ()

#ifdef 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.

@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 ()

#ifdef 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'

#ifdef 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 ()

#ifdef 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 ()

#ifdef 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'

#ifdef 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 ()

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

#endif

#ifdef 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