[racket] Destructors in Racket

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Mon Aug 4 01:59:17 EDT 2014

At Sat, 02 Aug 2014 21:12:50 +0400, Roman Klochkov wrote:
>  Here we have a thread per every module, that use such approach. It is 
> better,than a thread per object, but ...
> How many such threads can Racket run without performance degrading?

Many thousands.

Each thread costs about 15 kilobytes by default on a 64-bit platform
(it's possible to set parameters and bring that cost down to about 5k,
I think), and that space will be the main cost for a thread that is
blocked on a will executor. In particular, the thread will not even be
considered by the scheduler until a will is ready to execute.

For something like an output port, which is a fairly limited resource
at the OS level, allocating a thread per port is probably no real cost.

In case you are not already aware: If you are frequently allocating
file ports, beware of relying on GC to reclaim them. GC works fine for
plentiful resources, such as memory, but does not work well for
relatively constrained resources, such as file handles. (That's not a
Racket thing so much as a GC thing.)


> Maybe better make common module for such purpose? Hmm..., I think I should make 
> one...
> 
> 
> Sat, 2 Aug 2014 07:38:08 -0500 от Robby Findler <robby at eecs.northwestern.edu>:
> >Probably you want to do something like this, I guess. It creates a
> >single thread that just waits until anything registered with
> >an-executor is garbage and then runs the executors. They will run
> >asynchronously, but perhaps that's okay for your usecase.
> >
> >Robby
> >
> >#lang racket
> >(define an-executor (make-will-executor))
> >(void
> > (thread
> >  (λ ()
> >    (let loop ()
> >      (will-execute an-executor)
> >      (loop)))))
> >
> >(define a-box (box #f))
> >(will-register an-executor
> >               a-box
> >               (λ (x) (printf "a-box is now garbage\n")))
> >(collect-garbage) (collect-garbage) (collect-garbage)
> >(printf "breaking the link\n")
> >(set! a-box #f)
> >(collect-garbage) (collect-garbage) (collect-garbage)
> >
> >On Sat, Aug 2, 2014 at 7:31 AM, Roman Klochkov < kalimehtar at mail.ru > wrote:
> >> So I can write
> >>
> >> (define (make-obj stream)
> >>    (define res  (obj (open-output-file "out.dat")))
> >>    (define will (make-will-executor))
> >>    (will-register will res (lambda (x) (close-output-port (obj-file x))))
> >>    (thread (lambda () (let loop () (will-try-execute will) (sleep 1)
> >> (loop)))))
> >>
> >> to make my objects? Additional threads don't hinder performance, do they?
> >>
> >> Or should I somehow figure out if the thread is already running, don't run
> >> it and use common `will' for all objects?
> >>
> >> Sat, 2 Aug 2014 07:00:04 -0500 от Robby Findler
> >> < robby at eecs.northwestern.edu >:
> >>
> >> One way is to set up a separate thread to do that.
> >>
> >> The reason they are not called automatically is sometimes running the
> >> procedure passed to will-register needs to access some shared state
> >> and so your program must be allowed to be in control of the timing of
> >> the executor.
> >>
> >> In this case, it sounds like you can run the executor at any time, so
> >> just creating a thread to close the port inside my-obj should work
> >> fine.
> >>
> >> Robby
> >>
> >>
> >> On Sat, Aug 2, 2014 at 6:15 AM, Roman Klochkov < kalimehtar at mail.ru > wrote:
> >>> Then how?
> >>>
> >>> Suppose I have
> >>> (struct obj (file))
> >>> (define my-obj (obj (open-output-file "out.dat")))
> >>>
> >>> What I have to write to close the file, when my-obj will be GC'ed?
> >>>
> >>> I can write
> >>> (define will (make-will-executor))
> >>> (will-register will my-obj (lambda (x) (close-output-port (obj-file x))))
> >>>
> >>> But as far as I understand, it will not be called until `will-execute' or
> >>> `will-try-execute' will be manually called?
> >>>
> >>> Then how to call will-try-execute when GC is collecting my-obj?
> >>>
> >>>
> >>> Sat, 2 Aug 2014 11:57:32 +0100 от Matthew Flatt < mflatt at cs.utah.edu >:
> >>>
> >>> No, use the safe "will executors" API, instead.
> >>>
> >>> The unsafe finalizer API is for low-level, atomic finalization. Closing a
> >>> port can flush buffers and more, and it's not a good idea to do that in an
> >>> unsafe atomic context.
> >>>
> >>>> On Aug 2, 2014, at 11:12 AM, Roman Klochkov < kalimehtar at mail.ru > wrote:
> >>>>
> >>>> I have a structure, that has a filestream inside it. File have to be
> >>>> cosed, when the structure is not used anymore (so gargbage collected).
> >>>>
> >>>> Is the best way to do
> >>>> (require ffi/unsafe)
> >>>> (register-finalizer my-obj
> >>>> (lambda (x) (close-output-port (obj-file x))))
> >>>>
> >>>> ?
> >>>>
> >>>> It seems doing right thing, but why `ffi/unsafe'? Is it OK, when my
> >>>> program actually doesn't use FFI?
> >>>>
> >>>>
> >>>> --
> >>>> Roman Klochkov
> >>>> ____________________
> >>>> Racket Users list:
> >>>>  http://lists.racket-lang.org/users
> >>>
> >>>
> >>>
> >>> --
> >>> Roman Klochkov
> >>>
> >>> ____________________
> >>> Racket Users list:
> >>>  http://lists.racket-lang.org/users
> >>>
> >>
> >>
> >>
> >> --
> >> Roman Klochkov
> 
> 
> -- 
> Roman Klochkov
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users


Posted on the users mailing list.