[Haskell-cafe] Help with "shootout"

Chris Kuklewicz haskell at list.mightyreason.com
Tue Jan 3 06:46:25 EST 2006

[ Deeply nested replies are starting to look similar to runListT $
runStateT $ runWriter .... ]

matth at mindspring.com wrote:
> On Tue, Jan 03, 2006 at 12:07:43AM +0000, Joel Reymont wrote:
>>On Jan 2, 2006, at 9:20 PM, Chris Kuklewicz wrote:
>>> This makes me ponder one of the things that Joel was trying to do:
>>>efficiently pass data to a logging thread.  It may be that a custom
>>>channel would be helpful for that as well.
>>I have not taken the time to analyze the Chameneos code but need to  
>>point out that my problem was not with efficiently passing data to  
>>the logging thread. The issue was with data accumulating in the  
>>channel and the logger thread not reading it out fast enough.
>>The TChan implementation is a single-linked list implemented on top  
>>of TVar's. That would seem pretty efficient to me.
> It's simple and efficient but does nothing to prevent the channel from
> growing out of control.  A slightly modified (custom) channel based on
> TChan, but enforcing a maximum size (blocking on insert if the channel
> is too full), probably would have solved the problem.
> I assume that Erlang either does that or increases the priority of
> threads with large event queues, or both.
> Thanks,
> Matt Harden

Given that actually controlling priorities is not an option, adding
blocking like that makes sense.  One can make a ring buffer instead of a
singly linked list very easily.  In fact, I have that code lying around
(now attached).  It has not been speed optimized, but I did like being
able to express:

> type Node a = [TMVar a]
> make :: (Integral k) => k -> STM (Node a)
> make k = liftM cycle $ sequence $ genericReplicate k newEmptyTMVar

It has the usual operations, but you need to pass a fixed size to
new/newEmpty and you also have an isFull test.  It has no operations to
resize the ring buffer created by "make".
-------------- next part --------------
module ProdCons (PC,new,newEmpty, put,ProdCons.take,ProdCons.read,
                   tryPut,tryTake,tryRead, isEmpty,isFull) where

{-  Fixed bounded-buffer size solution of producer/consumer problem.

    Acts like a FIFO TMVar, blocking when capacity is reached.  So a
    capacity of 1 behaves like a TMVar.

    For arbitrary capacity just use a TChan. -}

import Control.Concurrent.STM
import Control.Concurrent
import Control.Monad.Fix
import Control.Monad
import Data.List(cycle,genericReplicate)

type Node a = [TMVar a]
newtype PC a = PC (TVar (Node a),TVar (Node a))

newEmpty :: (Integral k) => k -> IO (PC a)
newEmpty k | k <=0 = error "Need capacity > 0"
           | otherwise = do 
  node <- atomically $ make k
  atomically $ do
    tv1 <- newTVar node
    tv2 <- newTVar node
    return (PC (tv1,tv2))

new :: (Integral k) => k -> a -> IO (PC a)
new k v | k <=0 = error "Need capacity > 0"
      v | otherwise = do 
  pc <- newEmpty k 
  atomically $ put pc v 
  return pc

put ::PC a -> a -> STM ()
put (PC (tvar,_)) value = do 
  (tmvar:next) <- readTVar tvar
  putTMVar tmvar value
  writeTVar tvar next

take :: PC a -> STM a
take (PC (_,tvar)) = do
  (tmvar:next) <- readTVar tvar
  value <- takeTMVar tmvar
  writeTVar tvar next
  return value

read :: PC a -> STM a
read (PC (_,tvar)) = do 
  (tmvar:_) <- readTVar tvar
  readTMVar tmvar

tryTake :: PC a -> STM (Maybe a)
tryTake pc = (ProdCons.take pc >>= return.Just) `orElse` (return Nothing)

tryRead :: PC a -> STM (Maybe a)
tryRead pc = (ProdCons.read pc >>= return.Just) `orElse` (return Nothing)

tryPut :: PC a -> a -> STM Bool
tryPut pc v = (put pc v >> return True) `orElse` (return False)

isEmpty :: PC a -> STM Bool
isEmpty (PC (_,tvar)) = do
   (tmvar:_) <- readTVar tvar
   isEmptyTMVar tmvar

isFull (PC (tvar,_)) = do
   (tmvar:_) <- readTVar tvar
   empty <- isEmptyTMVar tmvar
   return (not empty)

-- -- -- Internal -- -- --

make :: (Integral k) => k -> STM (Node a)
make k = liftM cycle $ sequence $ genericReplicate k newEmptyTMVar

More information about the Haskell-Cafe mailing list