{- |
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 '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 (MIN_VERSION_haskell_gi_overloading(1,0,0) \
       && !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.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 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) => OL.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