Hello,

I use addFinalizer from System.Mem.Weak for performing some actions on
data just before that is reclaimed by GC. Registered in this way
finalizer is called surprisingly early for me, much earlier the data is
tend to be collected.

I've attached source code of simple two-threaded program. Source code
also can be found at [1]. It is pretty simple and should be readable
enough.

Program is intentioned to pass messages through transactional variable
(TVar) for printing by worker thread. Messages consist of formatted to
String Int values from 1 to 1000000. So the output should look like
this:

> 1
> 2
> ...
> 1000000

But running the program gives something like that:

> 1
> 2
> ...
> 283
> finalizing
> Test: thread blocked indefinitely in an STM transaction

As far as I see, finalizer is called too early. If someone could point
me out how to do the right thing, that would be helpful.

Operaton system: Debian Squeeze
Compiler: GHC 6.12.1
Compiling options: none, just "ghc --make Test.hs"

Thanks,
balodja

[1] http://hpaste.org/44648

-- Simple example of message passing through TVar from base thread
-- to worker. Worker thread waits for messages on TVar and prints them out,
-- while base thread sends messages with 10ms delay.

module Main where

import System.Mem.Weak (addFinalizer)
import Control.Concurrent (ThreadId, forkIO, threadDelay, killThread)
import Control.Concurrent.STM (TVar, newTVarIO, readTVar, writeTVar, atomically, retry)
import Control.Monad (when, forever, forM_)

-- Data type for holding transaction variable and information about worker thread
data Socket = Socket ThreadId (TVar String)

-- "Open socket" means:
--   * create TVar for interaction between base and worker threads
--   * launch worker thread
--   * set finalizer on socket, that kills worker thread when the socket
--     is garbage collected

open :: IO Socket
open = do
  var <- newTVarIO ""
  threadId <- forkIO (loop var) -- launch worker thread
  let sock = Socket threadId var
  addFinalizer sock $ do
    putStrLn "finalizing"
    killThread threadId
  return sock
  where
    loop var = forever $ do
      x <- atomically $ extract var -- wait on TVar, read it
      putStrLn x -- then print it, then repeat
    extract var = do
      x <- readTVar var
      when (x == "") retry -- when no pending message, just wait
      writeTVar var ""
      return x

-- Send message. When there is already any message for delivery,
-- just wait and retry.

send :: Socket -> String -> IO ()
send sock@(Socket _ var) msg = do
  atomically $ do
    x <- readTVar var
    when (x /= "") retry
    writeTVar var msg

main :: IO ()
main = do
  socket <- open
  forM_ [1..1000000] $ \x -> do
    send socket (show x)
    threadDelay (10*1000)

_______________________________________________
Haskell-Cafe mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to