System/Win32/Semaphore.hsc
#if __GLASGOW_HASKELL__ >= 709
{-# LANGUAGE Safe #-}
#else
{-# LANGUAGE Trustworthy #-}
#endif
-----------------------------------------------------------------------------
-- |
-- Module : System.Win32.Semaphore
-- Copyright : (c) Sam Derbyshire, 2022
-- License : BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer : Sam Derbyshire
-- Stability : provisional
-- Portability : portable
--
-- Windows Semaphore objects and operations
--
-----------------------------------------------------------------------------
module System.Win32.Semaphore
( -- * Semaphores
Semaphore(..)
-- * Access modes
, AccessMode
, sEMAPHORE_ALL_ACCESS
, sEMAPHORE_MODIFY_STATE
-- * Managing semaphores
, createSemaphore
, openSemaphore
, releaseSemaphore
) where
import System.Win32.File
import System.Win32.Types
import Data.Maybe (fromMaybe)
import Foreign hiding (void)
import Foreign.C (withCAString)
##include "windows_cconv.h"
#include <windows.h>
----------------------------------------------------------------
-- Semaphore access modes
----------------------------------------------------------------
#{enum AccessMode,
, sEMAPHORE_ALL_ACCESS = SEMAPHORE_ALL_ACCESS
, sEMAPHORE_MODIFY_STATE = SEMAPHORE_MODIFY_STATE
}
----------------------------------------------------------------
-- Semaphores
----------------------------------------------------------------
-- | A Windows semaphore.
--
-- To obtain a 'Semaphore', use 'createSemaphore' to create a new one,
-- or 'openSemaphore' to open an existing one.
--
-- To wait on a semaphore, use 'System.Win32.Event.waitForSingleObject'.
--
-- To release resources on a semaphore, use 'releaseSemaphore'.
--
-- To free a semaphore, use 'System.Win32.File.closeHandle'.
-- The semaphore object is destroyed when its last handle has been closed.
-- Closing the handle does not affect the semaphore count; therefore, be sure to call
-- 'releaseSemaphore' before closing the handle or before the process terminates.
-- Otherwise, pending wait operations will either time out or continue indefinitely,
-- depending on whether a time-out value has been specified.
newtype Semaphore = Semaphore { semaphoreHandle :: HANDLE }
-- | Open a 'Semaphore' with the given name, or create a new semaphore
-- if no such semaphore exists, with initial count @i@ and maximum count @m@.
--
-- The counts must satisfy @i >= 0@, @m > 0@ and @i <= m@.
--
-- The returned 'Bool' is 'True' if the function found an existing semaphore
-- with the given name, in which case a handle to that semaphore is returned
-- and the counts are ignored.
--
-- Use 'openSemaphore' if you don't want to create a new semaphore.
createSemaphore :: Maybe SECURITY_ATTRIBUTES
-> LONG -- ^ initial count @i@ with @0 <= i <= m@
-> LONG -- ^ maximum count @m > 0@
-> Maybe String -- ^ (optional) semaphore name
-- (case-sensitive, limited to MAX_PATH characters)
-> IO (Semaphore, Bool)
createSemaphore mb_sec initial_count max_count mb_name =
maybeWith with mb_sec $ \ c_sec -> do
maybeWith withCAString mb_name $ \ c_name -> do
handle <- c_CreateSemaphore c_sec initial_count max_count c_name
err_code <- getLastError
already_exists <-
case err_code of
(# const ERROR_INVALID_HANDLE) ->
errorWin $ "createSemaphore: semaphore name '"
++ fromMaybe "" mb_name
++ "' matches non-semaphore"
(# const ERROR_ALREADY_EXISTS) ->
return True
_ ->
return False
if handle == nullPtr
then errorWin "createSemaphore"
else return (Semaphore handle, already_exists)
foreign import WINDOWS_CCONV unsafe "windows.h CreateSemaphoreA"
c_CreateSemaphore :: LPSECURITY_ATTRIBUTES -> LONG -> LONG -> LPCSTR -> IO HANDLE
-- | Open an existing 'Semaphore'.
openSemaphore :: AccessMode -- ^ desired access mode
-> Bool -- ^ should child processes inherit the handle?
-> String -- ^ name of the semaphore to open (case-sensitive)
-> IO Semaphore
openSemaphore amode inherit name =
withTString name $ \c_name -> do
handle <- failIfNull ("openSemaphore: '" ++ name ++ "'") $
c_OpenSemaphore (fromIntegral amode) inherit c_name
return (Semaphore handle)
foreign import WINDOWS_CCONV unsafe "windows.h OpenSemaphoreW"
c_OpenSemaphore :: DWORD -> BOOL -> LPCWSTR -> IO HANDLE
-- | Increase the count of the 'Semaphore' by the specified amount.
--
-- Returns the count of the semaphore before the increase.
--
-- Throws an error if the count would exceeded the maximum count
-- of the semaphore.
releaseSemaphore :: Semaphore -> LONG -> IO LONG
releaseSemaphore (Semaphore handle) count =
with 0 $ \ ptr_prevCount -> do
failIfFalse_ "releaseSemaphore" $ c_ReleaseSemaphore handle count ptr_prevCount
peek ptr_prevCount
foreign import WINDOWS_CCONV unsafe "windows.h ReleaseSemaphore"
c_ReleaseSemaphore :: HANDLE -> LONG -> Ptr LONG -> IO BOOL
----------------------------------------------------------------
-- End
----------------------------------------------------------------