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
|
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE GeneralizedNewtypeDeriving
, NoImplicitPrelude
, BangPatterns
#-}
module GHC.Event.Poll
(
new
, available
) where
#include "EventConfig.h"
#if !defined(HAVE_POLL_H)
import GHC.Base
import qualified GHC.Event.Internal as E
new :: IO E.Backend
new = errorWithoutStackTrace "Poll back end not implemented for this platform"
available :: Bool
available = False
{-# INLINE available #-}
#else
#include <poll.h>
import Control.Concurrent.MVar (MVar, newMVar, swapMVar)
import Data.Bits (Bits, FiniteBits, (.|.), (.&.))
import Foreign.C.Types (CInt(..), CShort(..))
import Foreign.Ptr (Ptr)
import Foreign.Storable (Storable(..))
import GHC.Base
import GHC.Conc.Sync (withMVar)
import GHC.Enum (maxBound)
import GHC.Num (Num(..))
import GHC.Real (fromIntegral, div)
import GHC.Show (Show)
import System.Posix.Types (Fd(..), CNfds(..))
import qualified GHC.Event.Array as A
import qualified GHC.Event.Internal as E
available :: Bool
available = True
{-# INLINE available #-}
data Poll = Poll {
pollChanges :: {-# UNPACK #-} !(MVar (A.Array PollFd))
, pollFd :: {-# UNPACK #-} !(A.Array PollFd)
}
new :: IO E.Backend
new = E.backend poll modifyFd modifyFdOnce (\_ -> return ()) `liftM`
liftM2 Poll (newMVar =<< A.empty) A.empty
modifyFd :: Poll -> Fd -> E.Event -> E.Event -> IO Bool
modifyFd p fd oevt nevt =
withMVar (pollChanges p) $ \ary -> do
A.snoc ary $ PollFd fd (fromEvent nevt) (fromEvent oevt)
return True
modifyFdOnce :: Poll -> Fd -> E.Event -> IO Bool
modifyFdOnce = errorWithoutStackTrace "modifyFdOnce not supported in Poll backend"
reworkFd :: Poll -> PollFd -> IO ()
reworkFd p (PollFd fd npevt opevt) = do
let ary = pollFd p
if opevt == 0
then A.snoc ary $ PollFd fd npevt 0
else do
found <- A.findIndex ((== fd) . pfdFd) ary
case found of
Nothing -> errorWithoutStackTrace "reworkFd: event not found"
Just (i,_)
| npevt /= 0 -> A.unsafeWrite ary i $ PollFd fd npevt 0
| otherwise -> A.removeAt ary i
poll :: Poll
-> Maybe E.Timeout
-> (Fd -> E.Event -> IO ())
-> IO Int
poll p mtout f = do
let a = pollFd p
mods <- swapMVar (pollChanges p) =<< A.empty
A.forM_ mods (reworkFd p)
n <- A.useAsPtr a $ \ptr len ->
E.throwErrnoIfMinus1NoRetry "c_poll" $
case mtout of
Just tout ->
c_pollLoop ptr (fromIntegral len) (fromTimeout tout)
Nothing ->
c_poll_unsafe ptr (fromIntegral len) 0
when (n /= 0) $ do
A.loop a 0 $ \i e -> do
let r = pfdRevents e
if r /= 0
then do f (pfdFd e) (toEvent r)
let i' = i + 1
return (i', i' == n)
else return (i, True)
return (fromIntegral n)
where
-- The poll timeout is specified as an Int, but c_poll takes a CInt. These
-- can't be safely coerced as on many systems (e.g. x86_64) CInt has a
-- maxBound of (2^32 - 1), even though Int may have a significantly higher
-- bound.
--
-- This function deals with timeouts greater than maxBound :: CInt, by
-- looping until c_poll returns a non-zero value (0 indicates timeout
-- expired) OR the full timeout has passed.
c_pollLoop :: Ptr PollFd -> CNfds -> Int -> IO CInt
c_pollLoop ptr len tout
| isShortTimeout = c_poll ptr len (fromIntegral tout)
| otherwise = do
result <- c_poll ptr len (fromIntegral maxPollTimeout)
if result == 0
then c_pollLoop ptr len (fromIntegral (tout - maxPollTimeout))
else return result
where
-- maxPollTimeout is smaller than 0 IFF Int is smaller than CInt.
-- This means any possible Int input to poll can be safely directly
-- converted to CInt.
isShortTimeout = tout <= maxPollTimeout || maxPollTimeout < 0
-- We need to account for 3 cases:
-- 1. Int and CInt are of equal size.
-- 2. Int is larger than CInt
-- 3. Int is smaller than CInt
--
-- In case 1, the value of maxPollTimeout will be the maxBound of Int.
--
-- In case 2, the value of maxPollTimeout will be the maxBound of CInt,
-- which is the largest value accepted by c_poll. This will result in
-- c_pollLoop recursing if the provided timeout is larger.
--
-- In case 3, "fromIntegral (maxBound :: CInt) :: Int" will result in a
-- negative Int. This will cause isShortTimeout to be true and result in
-- the timeout being directly converted to a CInt.
maxPollTimeout :: Int
maxPollTimeout = fromIntegral (maxBound :: CInt)
fromTimeout :: E.Timeout -> Int
fromTimeout E.Forever = -1
fromTimeout (E.Timeout s) = fromIntegral $ s `divRoundUp` 1000000
where
divRoundUp num denom = (num + denom - 1) `div` denom
data PollFd = PollFd {
pfdFd :: {-# UNPACK #-} !Fd
, pfdEvents :: {-# UNPACK #-} !Event
, pfdRevents :: {-# UNPACK #-} !Event
} deriving Show -- ^ @since 4.4.0.0
newtype Event = Event CShort
deriving ( Eq -- ^ @since 4.4.0.0
, Show -- ^ @since 4.4.0.0
, Num -- ^ @since 4.4.0.0
, Storable -- ^ @since 4.4.0.0
, Bits -- ^ @since 4.4.0.0
, FiniteBits -- ^ @since 4.7.0.0
)
#{enum Event, Event
, pollIn = POLLIN
, pollOut = POLLOUT
, pollErr = POLLERR
, pollHup = POLLHUP
}
fromEvent :: E.Event -> Event
fromEvent e = remap E.evtRead pollIn .|.
remap E.evtWrite pollOut
where remap evt to
| e `E.eventIs` evt = to
| otherwise = 0
toEvent :: Event -> E.Event
toEvent e = remap (pollIn .|. pollErr .|. pollHup) E.evtRead `mappend`
remap (pollOut .|. pollErr .|. pollHup) E.evtWrite
where remap evt to
| e .&. evt /= 0 = to
| otherwise = mempty
-- | @since 4.3.1.0
instance Storable PollFd where
sizeOf _ = #size struct pollfd
alignment _ = alignment (undefined :: CInt)
peek ptr = do
fd <- #{peek struct pollfd, fd} ptr
events <- #{peek struct pollfd, events} ptr
revents <- #{peek struct pollfd, revents} ptr
let !pollFd' = PollFd fd events revents
return pollFd'
poke ptr p = do
#{poke struct pollfd, fd} ptr (pfdFd p)
#{poke struct pollfd, events} ptr (pfdEvents p)
#{poke struct pollfd, revents} ptr (pfdRevents p)
foreign import ccall safe "poll.h poll"
c_poll :: Ptr PollFd -> CNfds -> CInt -> IO CInt
foreign import ccall unsafe "poll.h poll"
c_poll_unsafe :: Ptr PollFd -> CNfds -> CInt -> IO CInt
#endif /* defined(HAVE_POLL_H) */
|