[Haskell-cafe] A simple telnet client using Conduit

Felipe Almeida Lessa felipe.lessa at gmail.com
Thu Jan 12 01:41:51 CET 2012

On Wed, Jan 11, 2012 at 10:28 PM, Erik de Castro Lopo
<mle+hs at mega-nerd.com> wrote:
> Thanks for the input Felipe.
> Felipe Almeida Lessa wrote:
>> On line 29, instead of
>>   liftIO $ do
>>     mapM_ ...
>>     runResourceT $ do
> Well that was because that whole block needs to run in IO.

My point is that the second runResourceT is not necessary.

>> Regarding threads, you should use resourceForkIO [1] which has a quite
>> nicer interface.
> I did read about resourceForkIO and it says:
>    Introduce a reference-counting scheme to allow a resource context to
>    be shared by multiple threads. Once the last thread exits, all
>    remaining resources will be released.
> In my case, I don't have any resources that are shared between threads.
> All I have is the actual ThreadId returned by forkIO. Since that ThreadId
> actually isn't used explicitly anywhere (but is implicitly passed to
> killThread when "release releaseThread" is called).

Actually, hsock is shared between both threads.  With your original
code, it gets closed regardless of what the other thread is doing.
Which in this case is what you want, but you need to be careful.

> The other thing about your solution is the question of what happens to
> the ThreadId returned by resourceForkIO. Rewriting your solution to
> explicity handle the ThreadId I get:
>    telnet :: String -> Int -> IO ()
>    telnet host port = runResourceT $ do
>        (releaseSock, hsock) <- with (connectTo host $ PortNumber $ fromIntegral port) hClose
>        liftIO $ mapM_ (\h -> hSetBuffering h LineBuffering) [ stdin, stdout, hsock ]
>        tid <- resourceForkIO $ sourceHandle stdin $$ sinkHandle hsock
>        sourceHandle hsock $$ sinkHandle stdout
>        liftIO $ killThread tid
>        release releaseSock
> The problem here is that I am not sure if the explicit killThread is
> actually needed [...]

What about inverting which thread gets to do what?

  _ <- resourceForkIO $ sourceHandle hsock $$ sinkHandle stdout
  sourceHandle stdin $$ sinkHandle hsock
  release releaseSock

My reasoning is that:

  - 'sourceHandle stdin' will close after the user presses ^D, thereby
reaching 'release releaseSock'.

  - After the socket is closed, 'sourceHandle hsock' will close as
well, killing the thread.

Note that without 'release releaseSock' the socket would not be closed
until the other thread died.

> [...] and it is, I think my solution, where killThread happens
> automatically is actually better. If what happens within the outer call
> to resourceT is a long running process, your solution (in the absence of
> the explicit killThread) could leave threads lying around that would
> have been cleaned up much earlier in my soltuion.

Actually, I'm not sure if my solution is better or worse than yours.
The question is "how long does it take for the thread to die after
hsock gets closed?"  If the answer is "right away", then my solution
is simpler =).  Otherwise, you solution is less resource-hungry.



More information about the Haskell-Cafe mailing list