{- | 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