J Fritsch
J Fritsch

Reputation: 3358

loop not terminating

The function below wants to either receive and ack or wait until its duetime has come and return.

Now, it works when it receives and ack. It works correctly when no ack is received and waits until duetime.

When the duetime is reached it freezes. It seems that it is not exiting my self constructed loop correctly. I have also tried with if-then-else, but same result. I do not want to use whileM.

How do I correctly exit the loop?

import Network.Socket hiding (send, sendTo, recv, recvFrom)
import Network.Socket.ByteString


waitAck s duetime' = do
      print ("in")
      (a, _) <- recvFrom s 4711
      now' <- getPOSIXTime
      unless (B.unpack a == "ack") (when (now' < duetime') (waitAck s duetime'))
      print (B.unpack a)
      return ()

Upvotes: 1

Views: 261

Answers (2)

hammar
hammar

Reputation: 139840

That's not an iterative loop. You don't place any conditions on the stuff after the recursive call, so when the conditions finally fail, the whole thing will unwind, printing once for every recursive call. I suspect that might be enough to make it appear frozen.

Try something like this:

waitAck s duetime' = do
    print ("in")
    (a, _) <- recvFrom s 4711
    now' <- getPOSIXTime
    if B.unpack a == "ack" || now' >= duetime'
        then print (B.unpack a)
        else waitAck s duetime'

Upvotes: 1

Daniel Wagner
Daniel Wagner

Reputation: 152707

The correct solution is to race two threads, one that waits for the ack, and one that waits for the time. Kill the one that loses the race. Perhaps this (untested) code will give you a hint about how:

import Control.Concurrency.MVar

withTimeout :: Int -> IO a -> IO (Maybe a)
withTimeout n io = do
    mvar    <- newEmptyMVar
    timeout <- forkIO (threadDelay n >> putMVar mvar Nothing)
    action  <- forkIO (io >>= putMVar mvar . Just)
    result  <- takeMVar mvar
    killThread timeout
    killThread action
    return result

waitAck s timeout = withTimeout timeout go where
    go = do
        (a, _) <- recvFrom s 4711
        if B.unpack a == "ack" then print (B.unpack a) else go

edit: It seems that base provides System.Timeout.timeout for exactly this purpose. Its implementation is more likely to be correct than this one, too.

Upvotes: 4

Related Questions