11
11
module Network.Socket.Types (
12
12
-- * Socket type
13
13
Socket
14
- , fdSocket
15
14
, withFdSocket
15
+ , unsafeFdSocket
16
+ , touchSocket
17
+ , fdSocket
16
18
, mkSocket
17
19
, invalidateSocket
18
20
, close
@@ -94,13 +96,18 @@ instance Show Socket where
94
96
instance Eq Socket where
95
97
Socket ref1 _ == Socket ref2 _ = ref1 == ref2
96
98
99
+ {-# DEPRECATED fdSocket "Use withFdSocket or unsafeFdSocket instead" #-}
100
+ -- | Currently, this is an alias of `unsafeFdSocket`.
101
+ fdSocket :: Socket -> IO CInt
102
+ fdSocket = unsafeFdSocket
103
+
97
104
-- | Getting a file descriptor from a socket.
98
105
--
99
106
-- If a 'Socket' is shared with multiple threads and
100
- -- one uses 'fdSocket ', unexpected issues may happen.
107
+ -- one uses 'unsafeFdSocket ', unexpected issues may happen.
101
108
-- Consider the following scenario:
102
109
--
103
- -- 1) Thread A acquires a 'Fd' from 'Socket' by 'fdSocket '.
110
+ -- 1) Thread A acquires a 'Fd' from 'Socket' by 'unsafeFdSocket '.
104
111
--
105
112
-- 2) Thread B close the 'Socket'.
106
113
--
@@ -109,39 +116,52 @@ instance Eq Socket where
109
116
--
110
117
-- In this case, it is safer for Thread A to clone 'Fd' by
111
118
-- 'System.Posix.IO.dup'. But this would still suffer from
112
- -- a race condition between 'fdSocket' and 'close'.
119
+ -- a race condition between 'unsafeFdSocket' and 'close'.
120
+ --
121
+ -- If you use this function, you need to guarantee that the 'Socket' does not
122
+ -- get garbage-collected until after you finish using the file descriptor.
123
+ -- 'touchSocket' can be used for this purpose.
113
124
--
114
125
-- A safer option is to use 'withFdSocket' instead.
115
- {-# DEPRECATED fdSocket "Use withFdSocket instead" #-}
116
- fdSocket :: Socket -> IO CInt
117
- fdSocket (Socket ref _) = readIORef ref
126
+ unsafeFdSocket :: Socket -> IO CInt
127
+ unsafeFdSocket (Socket ref _) = readIORef ref
128
+
129
+ -- | Ensure that the given 'Socket' stays alive (i.e. not garbage-collected)
130
+ -- at the given place in the sequence of IO actions. This function can be
131
+ -- used in conjunction with 'unsafeFdSocket' to guarantee that the file
132
+ -- descriptor is not prematurely freed.
133
+ touchSocket :: Socket -> IO ()
134
+ touchSocket (Socket ref _) = touch ref
135
+
136
+ touch :: IORef a -> IO ()
137
+ touch (IORef (STRef mutVar)) =
138
+ -- Thanks to a GHC issue, this touch# may not be quite guaranteed
139
+ -- to work. There's talk of replacing the touch# primop with one
140
+ -- that works better with the optimizer. But this seems to be the
141
+ -- "right" way to do it for now.
142
+ IO $ \ s -> (## touch## mutVar s, () ## )
118
143
119
144
-- | Get a file descriptor from a 'Socket'. The socket will never
120
145
-- be closed automatically before @withFdSocket@ completes, but
121
146
-- it may still be closed by an explicit call to 'close' or `close'`,
122
147
-- either before or during the call.
123
148
--
124
- -- The file descriptor must not be used after @withFdSocket@ returns;
125
- -- see the documentation for 'fdSocket' to see why that is.
149
+ -- The file descriptor must not be used after @withFdSocket@ returns, because
150
+ -- the 'Socket' may have been garbage-collected, invalidating the file
151
+ -- descriptor.
126
152
--
127
153
-- Since: 3.1.0.0
128
154
withFdSocket :: Socket -> (CInt -> IO r ) -> IO r
129
- withFdSocket (Socket ref@ ( IORef ( STRef ref ## )) _) f = do
155
+ withFdSocket (Socket ref _) f = do
130
156
fd <- readIORef ref
131
157
-- Should we throw an exception if the socket is already invalid?
132
158
-- That will catch some mistakes but certainly not all.
133
159
134
160
r <- f fd
135
161
136
- -- Thanks to a GHC issue, this touch# may not be quite guaranteed
137
- -- to work. There's talk of replacing the touch# primop with one
138
- -- that works better with the optimizer. But this seems to be the
139
- -- "right" way to do it for now.
140
-
141
- IO $ \ s -> (## touch## ref## s, () ## )
162
+ touch ref
142
163
return r
143
164
144
-
145
165
-- | Creating a socket from a file descriptor.
146
166
mkSocket :: CInt -> IO Socket
147
167
mkSocket fd = do
@@ -171,9 +191,9 @@ invalidateSocket (Socket ref _) errorAction normalAction = do
171
191
-- | Close the socket. This function does not throw exceptions even if
172
192
-- the underlying system call returns errors.
173
193
--
174
- -- If multiple threads use the same socket and one uses 'fdSocket ' and
194
+ -- If multiple threads use the same socket and one uses 'unsafeFdSocket ' and
175
195
-- the other use 'close', unexpected behavior may happen.
176
- -- For more information, please refer to the documentation of 'fdSocket '.
196
+ -- For more information, please refer to the documentation of 'unsafeFdSocket '.
177
197
close :: Socket -> IO ()
178
198
close s = invalidateSocket s (\ _ -> return () ) $ \ oldfd -> do
179
199
-- closeFdWith avoids the deadlock of IO manager.
0 commit comments