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

The GRecMutex struct is an opaque data structure to represent a
recursive mutex. It is similar to a 'GI.GLib.Unions.Mutex.Mutex' with the difference
that it is possible to lock a GRecMutex multiple times in the same
thread without deadlock. When doing so, care has to be taken to
unlock the recursive mutex as often as it has been locked.

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

A GRecMutex should only be accessed with the
g_rec_mutex_ functions.

/Since: 2.32/
-}

#define ENABLE_OVERLOADING (MIN_VERSION_haskell_gi_overloading(1,0,0) \
       && !defined(__HADDOCK_VERSION__))

module GI.GLib.Structs.RecMutex
    (

-- * Exported types
    RecMutex(..)                            ,
    newZeroRecMutex                         ,
    noRecMutex                              ,


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

#if ENABLE_OVERLOADING
    RecMutexClearMethodInfo                 ,
#endif
    recMutexClear                           ,


-- ** init #method:init#

#if ENABLE_OVERLOADING
    RecMutexInitMethodInfo                  ,
#endif
    recMutexInit                            ,


-- ** lock #method:lock#

#if ENABLE_OVERLOADING
    RecMutexLockMethodInfo                  ,
#endif
    recMutexLock                            ,


-- ** trylock #method:trylock#

#if ENABLE_OVERLOADING
    RecMutexTrylockMethodInfo               ,
#endif
    recMutexTrylock                         ,


-- ** unlock #method:unlock#

#if ENABLE_OVERLOADING
    RecMutexUnlockMethodInfo                ,
#endif
    recMutexUnlock                          ,




    ) 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.GClosure as B.GClosure
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.GI.Base.Properties as B.Properties
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
import qualified GHC.OverloadedLabels as OL


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

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

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


-- | A convenience alias for `Nothing` :: `Maybe` `RecMutex`.
noRecMutex :: Maybe RecMutex
noRecMutex = Nothing


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

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

foreign import ccall "g_rec_mutex_clear" g_rec_mutex_clear ::
    Ptr RecMutex ->                         -- rec_mutex : TInterface (Name {namespace = "GLib", name = "RecMutex"})
    IO ()

{- |
Frees the resources allocated to a recursive mutex with
'GI.GLib.Structs.RecMutex.recMutexInit'.

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

Calling 'GI.GLib.Structs.RecMutex.recMutexClear' on a locked recursive mutex leads
to undefined behaviour.

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

#if ENABLE_OVERLOADING
data RecMutexClearMethodInfo
instance (signature ~ (m ()), MonadIO m) => O.MethodInfo RecMutexClearMethodInfo RecMutex signature where
    overloadedMethod _ = recMutexClear

#endif

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

foreign import ccall "g_rec_mutex_init" g_rec_mutex_init ::
    Ptr RecMutex ->                         -- rec_mutex : TInterface (Name {namespace = "GLib", name = "RecMutex"})
    IO ()

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

This function is useful to initialize a recursive mutex
that has been allocated on the stack, or as part of a larger
structure.

It is not necessary to initialise a recursive mutex that has been
statically allocated.


=== /C code/
>
>  typedef struct {
>    GRecMutex m;
>    ...
>  } Blob;
>
>Blob *b;
>
>b = g_new (Blob, 1);
>g_rec_mutex_init (&b->m);


Calling 'GI.GLib.Structs.RecMutex.recMutexInit' on an already initialized 'GI.GLib.Structs.RecMutex.RecMutex'
leads to undefined behaviour.

To undo the effect of 'GI.GLib.Structs.RecMutex.recMutexInit' when a recursive mutex
is no longer needed, use 'GI.GLib.Structs.RecMutex.recMutexClear'.

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

#if ENABLE_OVERLOADING
data RecMutexInitMethodInfo
instance (signature ~ (m ()), MonadIO m) => O.MethodInfo RecMutexInitMethodInfo RecMutex signature where
    overloadedMethod _ = recMutexInit

#endif

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

foreign import ccall "g_rec_mutex_lock" g_rec_mutex_lock ::
    Ptr RecMutex ->                         -- rec_mutex : TInterface (Name {namespace = "GLib", name = "RecMutex"})
    IO ()

{- |
Locks /@recMutex@/. If /@recMutex@/ is already locked by another
thread, the current thread will block until /@recMutex@/ is
unlocked by the other thread. If /@recMutex@/ is already locked
by the current thread, the \'lock count\' of /@recMutex@/ is increased.
The mutex will only become available again when it is unlocked
as many times as it has been locked.

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

#if ENABLE_OVERLOADING
data RecMutexLockMethodInfo
instance (signature ~ (m ()), MonadIO m) => O.MethodInfo RecMutexLockMethodInfo RecMutex signature where
    overloadedMethod _ = recMutexLock

#endif

-- method RecMutex::trylock
-- method type : OrdinaryMethod
-- Args : [Arg {argCName = "rec_mutex", argType = TInterface (Name {namespace = "GLib", name = "RecMutex"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GRecMutex", 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_rec_mutex_trylock" g_rec_mutex_trylock ::
    Ptr RecMutex ->                         -- rec_mutex : TInterface (Name {namespace = "GLib", name = "RecMutex"})
    IO CInt

{- |
Tries to lock /@recMutex@/. If /@recMutex@/ is already locked
by another thread, it immediately returns 'False'. Otherwise
it locks /@recMutex@/ and returns 'True'.

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

#if ENABLE_OVERLOADING
data RecMutexTrylockMethodInfo
instance (signature ~ (m Bool), MonadIO m) => O.MethodInfo RecMutexTrylockMethodInfo RecMutex signature where
    overloadedMethod _ = recMutexTrylock

#endif

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

foreign import ccall "g_rec_mutex_unlock" g_rec_mutex_unlock ::
    Ptr RecMutex ->                         -- rec_mutex : TInterface (Name {namespace = "GLib", name = "RecMutex"})
    IO ()

{- |
Unlocks /@recMutex@/. If another thread is blocked in a
'GI.GLib.Structs.RecMutex.recMutexLock' call for /@recMutex@/, it will become unblocked
and can lock /@recMutex@/ itself.

Calling 'GI.GLib.Structs.RecMutex.recMutexUnlock' on a recursive mutex that is not
locked by the current thread leads to undefined behaviour.

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

#if ENABLE_OVERLOADING
data RecMutexUnlockMethodInfo
instance (signature ~ (m ()), MonadIO m) => O.MethodInfo RecMutexUnlockMethodInfo RecMutex signature where
    overloadedMethod _ = recMutexUnlock

#endif

#if ENABLE_OVERLOADING
type family ResolveRecMutexMethod (t :: Symbol) (o :: *) :: * where
    ResolveRecMutexMethod "clear" o = RecMutexClearMethodInfo
    ResolveRecMutexMethod "init" o = RecMutexInitMethodInfo
    ResolveRecMutexMethod "lock" o = RecMutexLockMethodInfo
    ResolveRecMutexMethod "trylock" o = RecMutexTrylockMethodInfo
    ResolveRecMutexMethod "unlock" o = RecMutexUnlockMethodInfo
    ResolveRecMutexMethod l o = O.MethodResolutionFailed l o

instance (info ~ ResolveRecMutexMethod t RecMutex, O.MethodInfo info RecMutex p) => OL.IsLabel t (RecMutex -> 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