forked from haskell/cabal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathIPC.hs
236 lines (187 loc) · 7.87 KB
/
IPC.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# OPTIONS_HADDOCK hide #-}
-----------------------------------------------------------------------------
-- |
-- Module : Distribution.Compat.IPC
--
-- Maintainer : cabal-devel@haskell.org
-- Portability : portable
--
module Distribution.Compat.IPC (
Semaphore(semaphoreName)
-- * Low-level API.
, createSemaphore, deleteSemaphore
, openSemaphore, closeSemaphore
, waitSemaphore, tryWaitSemaphore, releaseSemaphore
-- * High-level API.
,withNewSemaphore, withSemaphore
,withWaitSemaphore, withWaitGreedySemaphore )
where
import Control.Exception (bracket, bracket_)
#ifdef mingw32_HOST_OS
import Data.Bits ((.|.))
import Data.Maybe (fromMaybe)
import Foreign.Ptr (Ptr)
import System.Win32.File (LPSECURITY_ATTRIBUTES, closeHandle)
import System.Win32.Process (iNFINITE)
import System.Win32.Types (BOOL, DWORD, HANDLE, LONG, LPCTSTR,
errorWin, failIf, failIfFalse_, failIfNull,
nullPtr, withTString)
data Semaphore = Semaphore {
semaphoreName :: FilePath,
semaphoreHandle :: HANDLE
}
foreign import stdcall "windows.h CreateSemaphoreW"
c_CreateSemaphore :: LPSECURITY_ATTRIBUTES -> LONG -> LONG -> LPCTSTR
-> IO HANDLE
-- Can't use the name createSemaphore because it collides with our usage.
w32CreateSemaphore :: Maybe LPSECURITY_ATTRIBUTES -> LONG -> LONG -> String
-> IO HANDLE
w32CreateSemaphore mbLpSemaphoreAttributes lInitialCount lMaximumCount name =
withTString name $ \c_name -> failIfNull "CreateSemaphore" $
c_CreateSemaphore (fromMaybe nullPtr mbLpSemaphoreAttributes)
lInitialCount lMaximumCount c_name
foreign import stdcall "windows.h OpenSemaphoreW"
c_OpenSemaphore :: DWORD -> BOOL -> LPCTSTR -> IO HANDLE
sYNCHRONIZE :: DWORD
sYNCHRONIZE = 0x00100000
sEMAPHORE_MODIFY_STATE :: DWORD
sEMAPHORE_MODIFY_STATE = 0x0002
w32OpenSemaphore :: String -> IO HANDLE
w32OpenSemaphore name =
withTString name $ \c_name -> failIfNull "OpenSemaphore" $
c_OpenSemaphore (sEMAPHORE_MODIFY_STATE .|. sYNCHRONIZE) False c_name
type LPLONG = Ptr LONG
foreign import stdcall "windows.h ReleaseSemaphore"
c_ReleaseSemaphore :: HANDLE -> LONG -> LPLONG -> IO BOOL
w32ReleaseSemaphore :: HANDLE -> LONG -> IO ()
w32ReleaseSemaphore hSemaphore lReleaseCount =
failIfFalse_ "ReleaseSemaphore" $
c_ReleaseSemaphore hSemaphore lReleaseCount nullPtr
foreign import stdcall "windows.h WaitForSingleObject"
c_WaitForSingleObject :: HANDLE -> DWORD -> IO DWORD
wAIT_OBJECT_0 :: DWORD
wAIT_OBJECT_0 = 0x00000000
wAIT_FAILED :: DWORD
wAIT_FAILED = 0xFFFFFFFF
w32WaitForSingleObject :: HANDLE -> DWORD -> IO DWORD
w32WaitForSingleObject handle dwMilliseconds =
failIf (== wAIT_FAILED) "WaitForSingleObject" $
c_WaitForSingleObject handle dwMilliseconds
foreign import stdcall "windows.h GetCurrentProcessId"
c_GetCurrentProcessId :: IO DWORD
getCurrentProcessId :: IO DWORD
getCurrentProcessId = c_GetCurrentProcessId
-- | Create a new semaphore with a given initial value.
createSemaphore :: Int -> IO Semaphore
createSemaphore val = do
pid <- getCurrentProcessId
let name = "cabal-sem-" ++ (show pid)
handle <- w32CreateSemaphore Nothing (toEnum val) (toEnum val) name
return (Semaphore name handle)
-- | Delete a given semaphore.
deleteSemaphore :: Semaphore -> IO ()
deleteSemaphore =
-- On Windows, a semaphore gets deleted automatically when all open handles
-- to it are closed.
closeHandle . semaphoreHandle
-- | Open an existing semaphore.
openSemaphore :: FilePath -> IO Semaphore
openSemaphore semName = do handle <- w32OpenSemaphore semName
return (Semaphore semName handle)
-- | Close an open semaphore handle. This does not delete the semaphore.
closeSemaphore :: Semaphore -> IO ()
closeSemaphore = closeHandle . semaphoreHandle
-- | Wait for a semaphore to become available, blocking if needed.
waitSemaphore :: Semaphore -> IO ()
waitSemaphore = wait . semaphoreHandle
where
wait handle = do dwResult <- w32WaitForSingleObject handle iNFINITE
if dwResult == wAIT_OBJECT_0
then return ()
else errorWin "WaitForSingleObject"
-- | Non-blocking variant of 'waitSemaphore'.
tryWaitSemaphore :: Semaphore -> IO Bool
tryWaitSemaphore = tryWait . semaphoreHandle
where
tryWait handle = do dwResult <- w32WaitForSingleObject handle 0
if dwResult == wAIT_OBJECT_0
then return True
else return False
-- | Increment the semaphore counter's value (previously decremented by
-- 'waitSemaphore' or 'tryWaitSemaphore').
releaseSemaphore :: Semaphore -> IO ()
releaseSemaphore = release . semaphoreHandle
where
release handle = do w32ReleaseSemaphore handle 1
closeHandle handle
#else
import System.Posix.Process (getProcessID)
import System.Posix.Semaphore (OpenSemFlags(..),
semOpen, semUnlink, semPost, semWait, semTryWait)
import qualified System.Posix.Semaphore as Sem (Semaphore)
data Semaphore = Semaphore {
semaphoreName :: FilePath,
semaphoreHandle :: Sem.Semaphore
}
-- | Create a new semaphore with a given initial value.
createSemaphore :: Int -> IO Semaphore
createSemaphore val = do
pid <- getProcessID
let name = "/cabal-sem-" ++ (show pid)
flags = OpenSemFlags { semCreate = True, semExclusive = True }
perms = 0o755
sem <- semOpen name flags perms val
return (Semaphore name sem)
-- | Delete a given semaphore.
deleteSemaphore :: Semaphore -> IO ()
deleteSemaphore = semUnlink . semaphoreName
-- | Open an existing semaphore.
openSemaphore :: FilePath -> IO Semaphore
openSemaphore semName = do
let flags = OpenSemFlags { semCreate = False, semExclusive = False}
ignored0 = 0o755
ignored1 = 0
handle <- semOpen semName flags ignored0 ignored1
return (Semaphore semName handle)
-- | Close an open semaphore handle. This does not delete the semaphore.
closeSemaphore :: Semaphore -> IO ()
closeSemaphore _ = return ()
-- | Wait for a semaphore to become available, blocking if needed.
waitSemaphore :: Semaphore -> IO ()
waitSemaphore = semWait . semaphoreHandle
-- | Non-blocking variant of 'waitSemaphore'.
tryWaitSemaphore :: Semaphore -> IO Bool
tryWaitSemaphore = semTryWait . semaphoreHandle
-- | Increment the semaphore counter's value (previously decremented by
-- 'waitSemaphore' or 'tryWaitSemaphore').
releaseSemaphore :: Semaphore -> IO ()
releaseSemaphore = semPost . semaphoreHandle
#endif
-- High-level API.
-- | Create a new semaphore, run the provided action, delete the semaphore.
withNewSemaphore :: Int -> (Semaphore -> IO a) -> IO a
withNewSemaphore n = bracket (createSemaphore n) (deleteSemaphore)
-- | Open a handle to an existing semaphore, run the provided action, close the
-- handle.
withSemaphore :: FilePath -> (Semaphore -> IO a) -> IO a
withSemaphore semName = bracket (openSemaphore semName) (closeSemaphore)
-- | Acquire the semaphore, run the provided action, release the semaphore.
withWaitSemaphore :: Semaphore -> IO a -> IO a
withWaitSemaphore sem = bracket_ (waitSemaphore sem) (releaseSemaphore sem)
-- | Acquire the maximum possible value from the semaphore (always at least 1),
-- run the provided action, release the semaphore.
withWaitGreedySemaphore :: Semaphore -> (Int -> IO a) -> IO a
withWaitGreedySemaphore sem = bracket (acquire) (release)
where
acquire = waitSemaphore sem >> go 1
where
go n = do b <- tryWaitSemaphore sem
if b then go (n+1) else return n
#ifdef mingw32_HOST_OS
release n = w32ReleaseSemaphore (semaphoreHandle sem) (toEnum n)
#else
release n | n <= 0 = return ()
| otherwise = releaseSemaphore sem >> release (n-1)
#endif