{-# LANGUAGE TypeApplications #-} -- | Copyright : Will Thompson, Iñaki García Etxebarria and Jonas Platte -- License : LGPL-2.1 -- Maintainer : Iñaki García Etxebarria -- -- The GRWLock struct is an opaque data structure to represent a -- reader-writer lock. It is similar to a t'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'). -- -- It is unspecified whether readers or writers have priority in acquiring the -- lock when a reader already holds the lock and a writer is queued to acquire -- it. -- -- 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 t'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. -- -- /Since: 2.32/ #if (MIN_VERSION_haskell_gi_overloading(1,0,0) && !defined(__HADDOCK_VERSION__)) #define ENABLE_OVERLOADING #endif module GI.GLib.Structs.RWLock ( -- * Exported types RWLock(..) , newZeroRWLock , noRWLock , -- * Methods -- ** Overloaded methods #method:Overloaded methods# #if defined(ENABLE_OVERLOADING) ResolveRWLockMethod , #endif -- ** clear #method:clear# #if defined(ENABLE_OVERLOADING) RWLockClearMethodInfo , #endif rWLockClear , -- ** init #method:init# #if defined(ENABLE_OVERLOADING) RWLockInitMethodInfo , #endif rWLockInit , -- ** readerLock #method:readerLock# #if defined(ENABLE_OVERLOADING) RWLockReaderLockMethodInfo , #endif rWLockReaderLock , -- ** readerTrylock #method:readerTrylock# #if defined(ENABLE_OVERLOADING) RWLockReaderTrylockMethodInfo , #endif rWLockReaderTrylock , -- ** readerUnlock #method:readerUnlock# #if defined(ENABLE_OVERLOADING) RWLockReaderUnlockMethodInfo , #endif rWLockReaderUnlock , -- ** writerLock #method:writerLock# #if defined(ENABLE_OVERLOADING) RWLockWriterLockMethodInfo , #endif rWLockWriterLock , -- ** writerTrylock #method:writerTrylock# #if defined(ENABLE_OVERLOADING) RWLockWriterTrylockMethodInfo , #endif rWLockWriterTrylock , -- ** writerUnlock #method:writerUnlock# #if defined(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.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.GI.Base.Signals as B.Signals 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 RWLock = RWLock (ManagedPtr RWLock) deriving (Eq) 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 -- | A convenience alias for `Nothing` :: `Maybe` `RWLock`. noRWLock :: Maybe RWLock noRWLock = Nothing #if defined(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 t'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 t'GI.GLib.Structs.RWLock.RWLock' -> m () rWLockClear rwLock = liftIO $ do rwLock' <- unsafeManagedPtrGetPtr rwLock g_rw_lock_clear rwLock' touchManagedPtr rwLock return () #if defined(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 t'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 t'GI.GLib.Structs.RWLock.RWLock' leads -- to undefined behaviour. -- -- /Since: 2.32/ rWLockInit :: (B.CallStack.HasCallStack, MonadIO m) => RWLock -- ^ /@rwLock@/: an uninitialized t'GI.GLib.Structs.RWLock.RWLock' -> m () rWLockInit rwLock = liftIO $ do rwLock' <- unsafeManagedPtrGetPtr rwLock g_rw_lock_init rwLock' touchManagedPtr rwLock return () #if defined(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@/, the current thread will block. If another thread -- does not hold the write lock, but is waiting for it, it is implementation -- defined whether the reader or writer 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. If the limit is hit, -- or if a deadlock is detected, a critical warning will be emitted. -- -- /Since: 2.32/ rWLockReaderLock :: (B.CallStack.HasCallStack, MonadIO m) => RWLock -- ^ /@rwLock@/: a t'GI.GLib.Structs.RWLock.RWLock' -> m () rWLockReaderLock rwLock = liftIO $ do rwLock' <- unsafeManagedPtrGetPtr rwLock g_rw_lock_reader_lock rwLock' touchManagedPtr rwLock return () #if defined(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 'P.True' if -- the read lock was successfully obtained. Otherwise it -- returns 'P.False'. -- -- /Since: 2.32/ rWLockReaderTrylock :: (B.CallStack.HasCallStack, MonadIO m) => RWLock -- ^ /@rwLock@/: a t'GI.GLib.Structs.RWLock.RWLock' -> m Bool -- ^ __Returns:__ 'P.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' #if defined(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 t'GI.GLib.Structs.RWLock.RWLock' -> m () rWLockReaderUnlock rwLock = liftIO $ do rwLock' <- unsafeManagedPtrGetPtr rwLock g_rw_lock_reader_unlock rwLock' touchManagedPtr rwLock return () #if defined(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 t'GI.GLib.Structs.RWLock.RWLock' -> m () rWLockWriterLock rwLock = liftIO $ do rwLock' <- unsafeManagedPtrGetPtr rwLock g_rw_lock_writer_lock rwLock' touchManagedPtr rwLock return () #if defined(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 'P.False'. -- Otherwise it locks /@rwLock@/ and returns 'P.True'. -- -- /Since: 2.32/ rWLockWriterTrylock :: (B.CallStack.HasCallStack, MonadIO m) => RWLock -- ^ /@rwLock@/: a t'GI.GLib.Structs.RWLock.RWLock' -> m Bool -- ^ __Returns:__ 'P.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' #if defined(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 t'GI.GLib.Structs.RWLock.RWLock' -> m () rWLockWriterUnlock rwLock = liftIO $ do rwLock' <- unsafeManagedPtrGetPtr rwLock g_rw_lock_writer_unlock rwLock' touchManagedPtr rwLock return () #if defined(ENABLE_OVERLOADING) data RWLockWriterUnlockMethodInfo instance (signature ~ (m ()), MonadIO m) => O.MethodInfo RWLockWriterUnlockMethodInfo RWLock signature where overloadedMethod = rWLockWriterUnlock #endif #if defined(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) => OL.IsLabel t (RWLock -> p) where #if MIN_VERSION_base(4,10,0) fromLabel = O.overloadedMethod @info #else fromLabel _ = O.overloadedMethod @info #endif #endif