
As the result of a conversation on haskell-gui, I have tried to implement the disallocation of resources when a stream is garbage collected. To explain myself: I have a function f :: IO [a] which returns a lazy stream after allocating some resource to feed it (say installing a callback). I wish that the resource could be disallocated when it's no longer used. I did the obvious implementation with Weak.addFinalizer; results are encouraging but not completely satisfying; the scheme I used is: f = do allocateResource l <- makeTheStream addFinalizer l (disallocateResource) return l The problem is that if no memory is allocated, no garbage collection happens; of course finalization is not guaranteed, as the manual states. Another alternative is to make f return an esplicit "close stream" action: f :: IO ([a],IO ()) Is anyone willing to explain me other alternatives if there are, or to tell me that there aren't? Thanks for attention Vincenzo

Nick Name
[lots of context deleted] f :: IO [a] f = do allocateResource l <- makeTheStream addFinalizer l (disallocateResource) return l [snip]
Another alternative is to make f return an esplicit "close stream" action:
f :: IO ([a],IO ())
Is anyone willing to explain me other alternatives if there are, or to tell me that there aren't?
The second form (explicit release of resources) is better when: 1) It is important to release resources promptly (because they are scarce or expensive). 2) It is easy to identify the last use of the resource and to call the finalizer explicitly. This usually requires that your code be strict and that you are in the IO monad. 3) If there is any chance that you could release the resource too early, you have a good way of detecting the problem and propagating an appropriate error value or exception. Detection is usually easy to arrange using a Haskell proxy which records whether the finalizer has been called. Reporting the problem can usually be done using the Maybe type, Haskell 98 style IOErrors or Asynchronous Exceptions. The first form is better when any of these do not hold. The best example where automatic finalization is appropriate is the hGetContents function (which your function 'f' seems to resemble). Laziness makes it hard to predict the lifetime of the stream. The code consuming the stream tends to be pure so there's no obvious place to call the finalizer from. And, finally, the code consuming the stream is unlikely to respond well to an exception being raised because, more than likely, it was written for use in a context where exceptions meant complete failure not failure of an IO operation. Nick name says that a problem with the first is:
The problem is that if no memory is allocated, no garbage collection happens; of course finalization is not guaranteed, as the manual states.
Haskell code tends to consume memory at a fairly constant rate so, as long as your program is not blocked waiting for input, you should be consuming memory. You then need to tweak the configuration of the garbage collector (+RTS -h... ...-RTS in GHC) to make the GC trigger at the desired frequency. You should also use the garbageCollect function (part of the FFI specification) to let you explicitly invoke the garbage collector. You might call this immediately before any blocking IO operations. Calling the GC too often can be expensive. In an image processing system at Yale, we built a tiny 'model' of the resource usage to try to call the GC only when it might plausibly release resources (or when we were very short of resources). The idea was to track how many resources were allocated and to try to estimate (based on past behaviour) how many of those are likely to be released if we were to call GC now. We'd then only call the GC if the overhead (ratio of collectable resources to required resources) exceeded some threshold. Hope this is of some help -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/

On Mon, 10 Mar 2003 11:30:01 +0000
Alastair Reid
Nick name says that a problem with the first is:
The problem is that if no memory is allocated, no garbage collection happens; of course finalization is not guaranteed, as the manual states.
Haskell code tends to consume memory at a fairly constant rate so, as long as your program is not blocked waiting for input, you should be consuming memory. You then need to tweak the configuration of the garbage collector (+RTS -h... ...-RTS in GHC) to make the GC trigger at the desired frequency.
Ok, today the combination of compilers/libraries I use have *decided* I won't do tests any longer :( === Talking about the "f :: IO [a]" approach On my first test, if I had the program waiting there for events, I didn't see the finalizer at work, even if calling "performGC" ***inside the event handler*** (handling a mouse motion event). I saw the mouse motion handler print and print debug information, but never the finalizer printing that "callback uninstalled". However, calling a "print fib 100" somewhere in my main function shows that finalizers really work sometimes. I see you (the FFI team) have had a very long discussion on finalizers, but I have not the skills to understand it fully. I guess the main problem here is that I am giving up the control of disallocating a resource (the callback) to an entity (the garbage collector) wich cares about a *different* resource, with *different* cost (the memory); however, when the programs is at work, garbage IS collected and so I guess the solution is pretty satisfying in general. === The fourth child ??? Now, I think, if one had a resource wich he really cares about more than memory and threads or timers, and if he was still wishing to give a stream produced with this resource around to threads and pure functions, he could... ... produce a stream with some "unsafeInterleaveIO" operations; each of these operations: 1. checks for the existence of the resource 2. if necessary allocates it 3. anyway, retrigger a timer wich, at timeout, deallocates the resource Step 2 can be replaced with 2'. if the resource doesn't exist, then it's time to return the infamous empty list What do you think of this approach? Have you ever tried it? (of course, with this formulation, in general I guess timeouts are standard practice with scarce resources). And, the timeout can be replaced with "another one wants my resource". And, is it time to reopen that operating systems book? :) Vincenzo

Nick Name
As the result of a conversation on haskell-gui, I have tried to implement the disallocation of resources when a stream is garbage collected.
To explain myself:
I have a function
f :: IO [a]
which returns a lazy stream after allocating some resource to feed it (say installing a callback).
I wish that the resource could be disallocated when it's no longer used. I did the obvious implementation with Weak.addFinalizer; results are encouraging but not completely satisfying; the scheme I used is:
f = do allocateResource l <- makeTheStream addFinalizer l (disallocateResource) return l
The problem is that if no memory is allocated, no garbage collection happens; of course finalization is not guaranteed, as the manual states.
You may want to have a look at how ports are closed by finalizers in the Haskell Ports Library: http://www.cse.unsw.edu.au/~chak/haskell/ports/ It seems that the set up there is similar to yours. Cheers, Manuel

another possibility is a withStream type function which explicitly finalizes. withStream :: Stream a -> ([a] -> IO b) -> IO b the idea being the stream is freed when the IO b finishes. this can easily be built on your explicit 'close stream' version below, but may not be flexable enough for what people want to do with streams. John On Mon, Mar 10, 2003 at 12:59:00AM +0100, Nick Name wrote:
As the result of a conversation on haskell-gui, I have tried to implement the disallocation of resources when a stream is garbage collected.
To explain myself:
I have a function
f :: IO [a]
which returns a lazy stream after allocating some resource to feed it (say installing a callback).
I wish that the resource could be disallocated when it's no longer used. I did the obvious implementation with Weak.addFinalizer; results are encouraging but not completely satisfying; the scheme I used is:
f = do allocateResource l <- makeTheStream addFinalizer l (disallocateResource) return l
The problem is that if no memory is allocated, no garbage collection happens; of course finalization is not guaranteed, as the manual states.
Another alternative is to make f return an esplicit "close stream" action:
f :: IO ([a],IO ())
Is anyone willing to explain me other alternatives if there are, or to tell me that there aren't?
Thanks for attention
Vincenzo _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- --------------------------------------------------------------------------- John Meacham - California Institute of Technology, Alum. - john@foo.net ---------------------------------------------------------------------------
participants (4)
-
Alastair Reid
-
John Meacham
-
Manuel M T Chakravarty
-
Nick Name