Skip to content

Commit 19e2579

Browse files
committed
Merge PR #423
2 parents 91fe934 + 713aa97 commit 19e2579

File tree

3 files changed

+43
-21
lines changed

3 files changed

+43
-21
lines changed

Network/Socket.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,10 @@ module Network.Socket
136136
-- * Socket
137137
, Socket
138138
, socket
139-
, fdSocket
140139
, withFdSocket
140+
, unsafeFdSocket
141+
, touchSocket
142+
, fdSocket
141143
, mkSocket
142144
, socketToHandle
143145
-- ** Types of Socket

Network/Socket/Buffer.hsc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ sendBufTo s ptr nbytes sa =
5151
#if defined(mingw32_HOST_OS)
5252
socket2FD :: Socket -> IO FD
5353
socket2FD s = do
54-
fd <- fdSocket s
54+
fd <- unsafeFdSocket s
5555
-- HACK, 1 means True
5656
return $ FD{ fdFD = fd, fdIsSocket_ = 1 }
5757
#endif

Network/Socket/Types.hsc

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
module Network.Socket.Types (
1212
-- * Socket type
1313
Socket
14-
, fdSocket
1514
, withFdSocket
15+
, unsafeFdSocket
16+
, touchSocket
17+
, fdSocket
1618
, mkSocket
1719
, invalidateSocket
1820
, close
@@ -94,13 +96,18 @@ instance Show Socket where
9496
instance Eq Socket where
9597
Socket ref1 _ == Socket ref2 _ = ref1 == ref2
9698

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+
97104
-- | Getting a file descriptor from a socket.
98105
--
99106
-- 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.
101108
-- Consider the following scenario:
102109
--
103-
-- 1) Thread A acquires a 'Fd' from 'Socket' by 'fdSocket'.
110+
-- 1) Thread A acquires a 'Fd' from 'Socket' by 'unsafeFdSocket'.
104111
--
105112
-- 2) Thread B close the 'Socket'.
106113
--
@@ -109,39 +116,52 @@ instance Eq Socket where
109116
--
110117
-- In this case, it is safer for Thread A to clone 'Fd' by
111118
-- '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.
113124
--
114125
-- 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, () ##)
118143

119144
-- | Get a file descriptor from a 'Socket'. The socket will never
120145
-- be closed automatically before @withFdSocket@ completes, but
121146
-- it may still be closed by an explicit call to 'close' or `close'`,
122147
-- either before or during the call.
123148
--
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.
126152
--
127153
-- Since: 3.1.0.0
128154
withFdSocket :: Socket -> (CInt -> IO r) -> IO r
129-
withFdSocket (Socket ref@(IORef (STRef ref##)) _) f = do
155+
withFdSocket (Socket ref _) f = do
130156
fd <- readIORef ref
131157
-- Should we throw an exception if the socket is already invalid?
132158
-- That will catch some mistakes but certainly not all.
133159

134160
r <- f fd
135161

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
142163
return r
143164

144-
145165
-- | Creating a socket from a file descriptor.
146166
mkSocket :: CInt -> IO Socket
147167
mkSocket fd = do
@@ -171,9 +191,9 @@ invalidateSocket (Socket ref _) errorAction normalAction = do
171191
-- | Close the socket. This function does not throw exceptions even if
172192
-- the underlying system call returns errors.
173193
--
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
175195
-- 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'.
177197
close :: Socket -> IO ()
178198
close s = invalidateSocket s (\_ -> return ()) $ \oldfd -> do
179199
-- closeFdWith avoids the deadlock of IO manager.

0 commit comments

Comments
 (0)