[plt-scheme] letrec and threads

From: Robby Findler (robby at cs.uchicago.edu)
Date: Mon Feb 19 11:10:40 EST 2007

That's really a property of letrec -- the threads only really serve to
make things more complex (and non-deterministically working/not
working).

Try this program:

(letrec ([x (begin (printf "y ~s\n" y) 2)]
         [y 3])
  1)

and you'll see that `y' is undefined, just like your foo2 below.

The issue is that letrec is much like a let that initializes all of
the variables to undefined and then a series of assignments that fill
in the right-hand sides (that's how the recursion is achieved).

You sometimes get the right behavior depending on the order in which
the threads run.

So, in your case, you may want to communicate the threads to each
other via the channels, or you can use a semaphore to wait for the
letrec to finish initialization before the calls to foo, something
like this:

  (letrec ([s (make-semaphore 0)]
           [foo1 (spawn (lambda () (semaphore-wait s) (foo foo2 ...)))]
           [foo2 (spawn (lambda () (semaphore-wait s) (foo foo2 ...)))])
    (semaphore-post s)
    (semaphore-post s)
    ....)

hth,
Robby

(apologies for any typos above -- that is untested code)

On 2/19/07, Dimitris Vyzovitis <vyzo at media.mit.edu> wrote:
> Hi,
>
> I am observing inconsistent behavior in letrec with
> threads with a slightly convoluted scenario.
>
> First, the simple case that works as expected:
> (define (spawn thunk)
>   (thread thunk))
>
> (define (ok)
>   (define (foo other ch)
>     (channel-put ch other))
>   (letrec ((ch1 (make-channel))
>            (ch2 (make-channel))
>            (foo1 (spawn (lambda () (foo foo2 ch1))))
>            (foo2 (spawn (lambda () (foo foo1 ch2)))))
>       (values (thread? (sync ch1)) (thread? (sync ch2)))))
> > (ok)
> #t
> #t
>
> And now the less simple case that results in undefined values:
> (define sch (make-channel))
> (define spawner
>   (thread
>    (lambda ()
>      (let lp ((thunk (channel-get sch)))
>           (thread thunk)
>           (lp (channel-get sch))))))
>
> (define (spawn thunk)
>   (channel-put sch thunk))
>
> (define (oops)
>   (define (foo other ch)
>     (channel-put ch other))
>   (letrec ((ch1 (make-channel))
>            (ch2 (make-channel))
>            (foo1 (spawn (lambda () (foo foo2 ch1))))
>            (foo2 (spawn (lambda () (foo foo1 ch2)))))
>       (values (thread? (sync ch1)) (thread? (sync ch2)))))
>
> > (oops)
> #f
> #f
>
> This is happening consistently with 369.8 in my gnu box. Not that
> letrec semantics with threads are clear, but still there is marked change in
> behavior when adding the extra thread.
>
> -- vyzo
>
> _________________________________________________
>   For list-related administrative tasks:
>   http://list.cs.brown.edu/mailman/listinfo/plt-scheme
>


Posted on the users mailing list.