    {- |
    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 'GI.GLib.Unions.Mutex.Mutex' struct is an opaque data structure to represent a mutex
    (mutual exclusion). It can be used to protect data against shared
    access.

    Take for example the following function:

    === /C code/
    >
    >  int
    >  give_me_next_number (void)
    >  {
    >    static int current_number = 0;
    >
    >    // now do a very complicated calculation to calculate the new
    >    // number, this might for example be a random number generator
    >    current_number = calc_next_number (current_number);
    >
    >    return current_number;
    >  }

    It is easy to see that this won\'t work in a multi-threaded
    application. There current_number must be protected against shared
    access. A 'GI.GLib.Unions.Mutex.Mutex' can be used as a solution to this problem:

    === /C code/
    >
    >  int
    >  give_me_next_number (void)
    >  {
    >    static GMutex mutex;
    >    static int current_number = 0;
    >    int ret_val;
    >
    >    g_mutex_lock (&mutex);
    >    ret_val = current_number = calc_next_number (current_number);
    >    g_mutex_unlock (&mutex);
    >
    >    return ret_val;
    >  }

    Notice that the 'GI.GLib.Unions.Mutex.Mutex' is not initialised to any particular value.
    Its placement in static storage ensures that it will be initialised
    to all-zeros, which is appropriate.

    If a 'GI.GLib.Unions.Mutex.Mutex' is placed in other contexts (eg: embedded in a struct)
    then it must be explicitly initialised using 'GI.GLib.Unions.Mutex.mutexInit'.

    A 'GI.GLib.Unions.Mutex.Mutex' should only be accessed via g_mutex_ functions.
    -}

    #define ENABLE_OVERLOADING \
           (!defined(__HADDOCK_VERSION__))

    module GI.GLib.Unions.Mutex
        (

    -- * Exported types
        Mutex(..)                               ,
        newZeroMutex                            ,
        noMutex                                 ,

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

    #if ENABLE_OVERLOADING
        MutexClearMethodInfo                    ,
    #endif
        mutexClear                              ,

    -- ** init #method:init#

    #if ENABLE_OVERLOADING
        MutexInitMethodInfo                     ,
    #endif
        mutexInit                               ,

    -- ** lock #method:lock#

    #if ENABLE_OVERLOADING
        MutexLockMethodInfo                     ,
    #endif
        mutexLock                               ,

    -- ** trylock #method:trylock#

    #if ENABLE_OVERLOADING
        MutexTrylockMethodInfo                  ,
    #endif
        mutexTrylock                            ,

    -- ** unlock #method:unlock#

    #if ENABLE_OVERLOADING
        MutexUnlockMethodInfo                   ,
    #endif
        mutexUnlock                             ,

        ) 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 Mutex = Mutex (ManagedPtr Mutex)
    instance WrappedPtr Mutex where
        wrappedPtrCalloc = callocBytes 8
        wrappedPtrCopy = \p -> withManagedPtr p (copyBytes 8 >=> wrapPtr Mutex)
        wrappedPtrFree = Just ptr_to_g_free

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

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

    -- | A convenience alias for `Nothing` :: `Maybe` `Mutex`.
    noMutex :: Maybe Mutex
    noMutex = Nothing

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

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

    foreign import ccall "g_mutex_clear" g_mutex_clear ::
        Ptr Mutex ->                            -- mutex : TInterface (Name {namespace = "GLib", name = "Mutex"})
        IO ()

    {- |
    Frees the resources allocated to a mutex with 'GI.GLib.Unions.Mutex.mutexInit'.

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

    Calling 'GI.GLib.Unions.Mutex.mutexClear' on a locked mutex leads to undefined
    behaviour.

    Sine: 2.32
    -}
    mutexClear ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        Mutex
        {- ^ /@mutex@/: an initialized 'GI.GLib.Unions.Mutex.Mutex' -}
        -> m ()
    mutexClear mutex = liftIO $ do
        mutex' <- unsafeManagedPtrGetPtr mutex
        g_mutex_clear mutex'
        touchManagedPtr mutex
        return ()

    #if ENABLE_OVERLOADING
    data MutexClearMethodInfo
    instance (signature ~ (m ()), MonadIO m) => O.MethodInfo MutexClearMethodInfo Mutex signature where
        overloadedMethod _ = mutexClear

    #endif

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

    foreign import ccall "g_mutex_init" g_mutex_init ::
        Ptr Mutex ->                            -- mutex : TInterface (Name {namespace = "GLib", name = "Mutex"})
        IO ()

    {- |
    Initializes a 'GI.GLib.Unions.Mutex.Mutex' so that it can be used.

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

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

    To undo the effect of 'GI.GLib.Unions.Mutex.mutexInit' when a mutex is no longer
    needed, use 'GI.GLib.Unions.Mutex.mutexClear'.

    Calling 'GI.GLib.Unions.Mutex.mutexInit' on an already initialized 'GI.GLib.Unions.Mutex.Mutex' leads
    to undefined behaviour.

    /Since: 2.32/
    -}
    mutexInit ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        Mutex
        {- ^ /@mutex@/: an uninitialized 'GI.GLib.Unions.Mutex.Mutex' -}
        -> m ()
    mutexInit mutex = liftIO $ do
        mutex' <- unsafeManagedPtrGetPtr mutex
        g_mutex_init mutex'
        touchManagedPtr mutex
        return ()

    #if ENABLE_OVERLOADING
    data MutexInitMethodInfo
    instance (signature ~ (m ()), MonadIO m) => O.MethodInfo MutexInitMethodInfo Mutex signature where
        overloadedMethod _ = mutexInit

    #endif

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

    foreign import ccall "g_mutex_lock" g_mutex_lock ::
        Ptr Mutex ->                            -- mutex : TInterface (Name {namespace = "GLib", name = "Mutex"})
        IO ()

    {- |
    Locks /@mutex@/. If /@mutex@/ is already locked by another thread, the
    current thread will block until /@mutex@/ is unlocked by the other
    thread.

    'GI.GLib.Unions.Mutex.Mutex' is neither guaranteed to be recursive nor to be
    non-recursive.  As such, calling 'GI.GLib.Unions.Mutex.mutexLock' on a 'GI.GLib.Unions.Mutex.Mutex' that has
    already been locked by the same thread results in undefined behaviour
    (including but not limited to deadlocks).
    -}
    mutexLock ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        Mutex
        {- ^ /@mutex@/: a 'GI.GLib.Unions.Mutex.Mutex' -}
        -> m ()
    mutexLock mutex = liftIO $ do
        mutex' <- unsafeManagedPtrGetPtr mutex
        g_mutex_lock mutex'
        touchManagedPtr mutex
        return ()

    #if ENABLE_OVERLOADING
    data MutexLockMethodInfo
    instance (signature ~ (m ()), MonadIO m) => O.MethodInfo MutexLockMethodInfo Mutex signature where
        overloadedMethod _ = mutexLock

    #endif

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

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

    'GI.GLib.Unions.Mutex.Mutex' is neither guaranteed to be recursive nor to be
    non-recursive.  As such, calling 'GI.GLib.Unions.Mutex.mutexLock' on a 'GI.GLib.Unions.Mutex.Mutex' that has
    already been locked by the same thread results in undefined behaviour
    (including but not limited to deadlocks or arbitrary return values).
    -}
    mutexTrylock ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        Mutex
        {- ^ /@mutex@/: a 'GI.GLib.Unions.Mutex.Mutex' -}
        -> m Bool
        {- ^ __Returns:__ 'True' if /@mutex@/ could be locked -}
    mutexTrylock mutex = liftIO $ do
        mutex' <- unsafeManagedPtrGetPtr mutex
        result <- g_mutex_trylock mutex'
        let result' = (/= 0) result
        touchManagedPtr mutex
        return result'

    #if ENABLE_OVERLOADING
    data MutexTrylockMethodInfo
    instance (signature ~ (m Bool), MonadIO m) => O.MethodInfo MutexTrylockMethodInfo Mutex signature where
        overloadedMethod _ = mutexTrylock

    #endif

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

    foreign import ccall "g_mutex_unlock" g_mutex_unlock ::
        Ptr Mutex ->                            -- mutex : TInterface (Name {namespace = "GLib", name = "Mutex"})
        IO ()

    {- |
    Unlocks /@mutex@/. If another thread is blocked in a 'GI.GLib.Unions.Mutex.mutexLock'
    call for /@mutex@/, it will become unblocked and can lock /@mutex@/ itself.

    Calling 'GI.GLib.Unions.Mutex.mutexUnlock' on a mutex that is not locked by the
    current thread leads to undefined behaviour.
    -}
    mutexUnlock ::
        (B.CallStack.HasCallStack, MonadIO m) =>
        Mutex
        {- ^ /@mutex@/: a 'GI.GLib.Unions.Mutex.Mutex' -}
        -> m ()
    mutexUnlock mutex = liftIO $ do
        mutex' <- unsafeManagedPtrGetPtr mutex
        g_mutex_unlock mutex'
        touchManagedPtr mutex
        return ()

    #if ENABLE_OVERLOADING
    data MutexUnlockMethodInfo
    instance (signature ~ (m ()), MonadIO m) => O.MethodInfo MutexUnlockMethodInfo Mutex signature where
        overloadedMethod _ = mutexUnlock

    #endif

    #if ENABLE_OVERLOADING
    type family ResolveMutexMethod (t :: Symbol) (o :: *) :: * where
        ResolveMutexMethod "clear" o = MutexClearMethodInfo
        ResolveMutexMethod "init" o = MutexInitMethodInfo
        ResolveMutexMethod "lock" o = MutexLockMethodInfo
        ResolveMutexMethod "trylock" o = MutexTrylockMethodInfo
        ResolveMutexMethod "unlock" o = MutexUnlockMethodInfo
        ResolveMutexMethod l o = O.MethodResolutionFailed l o

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

    #if MIN_VERSION_base(4,9,0)
    instance (info ~ ResolveMutexMethod t Mutex, O.MethodInfo info Mutex p) => O.IsLabel t (Mutex -> 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