On Mon, Jul 17, 2006 at 07:29:35PM +0100, Duncan Rose wrote:

> I was more thinking along the lines of:
> 
> (let ((closure-var 0))        #<...> )
> (spawn-thread #'some-fun)
> (some-other-fun)

Funnily enough, I had got sidetracked into this shortly before Marco
posted.  As far as I can make out (i.e. I haven't read the docs
properly or used the source, I just wrote some code), sb-threads
shares lexicals without implicit locks.

You can make strange things happen.  Details below.


> ... how do these threads interact with closure-var? Is there any 
> protection? Should there be?

Looks like there is none.  A warning might be helpful, but that sounds
tricky to do.

Calculating reachability for an arbitrarily large fraction of the code
in the running image, given a function to execute?  A conservative
"might possibly be accessed without locks" is probably going to make
lots of noise.

Here's a spec idea: could one declare a thread to be an outcast,
banned from touching any specials or shared lexicals that don't meet
some standard locking mechanism?  This probably means no access to
vast tracts of CL that read or modify global stuff, but it would
partition the problem of what might need to be locked.


The details...

The snippet of test code below gives plenty of output, but for one
group of three threads sharing an argument in a defun starts like
this,

  [EMAIL PROTECTED]:~/cvswork-toy/lisp$ ./test.lisp 1
  #<SB-THREAD:THREAD "thread_0A" {AB74559}>  
........................................     0
  #<SB-THREAD:THREAD "thread_0B" {AB776A1}>  
........................................     0
  WARNING: wait for 4 threads...
  #<SB-THREAD:THREAD "thread_0C" {AB786D1}>  
........................................     0
  #<SB-THREAD:THREAD "thread_0A" {AB74559}>  
........................................     1
  #<SB-THREAD:THREAD "thread_0A" {AB74559}>  
........................................     1
  #<SB-THREAD:THREAD "thread_0B" {AB776A1}>  
........................................     2
  #<SB-THREAD:THREAD "thread_0C" {AB786D1}>  
........................................     3
  #<SB-THREAD:THREAD "thread_0A" {AB74559}>  
........................................     4
  #<SB-THREAD:THREAD "thread_0B" {AB776A1}>  
........................................     5
  #<SB-THREAD:THREAD "thread_0C" {AB786D1}>  
........................................     6
  #<SB-THREAD:THREAD "thread_0A" {AB74559}>  
........................................     7
[...]

My interpretation: threads 0A,0B,0C all read n=0 and print it, then do
(incf n)

When 0A reads one, prints it, sleeps, then (incf n), the writing of 0C
has not yet happened.  0C writes while 0A sleeps, then 0A gets another
chance and does the same again - it sees its view of `n' go backwards.

After that the threads desynchronise because their sleep time is a
multiple of n, and the counter increases steadily.

Ooh and "./test.lisp 15" goes pop.  Maybe I need a newer sbcl.


> (I suppose the usual fall-back for this kind of thing would be to
> say it's up to the programmer... [...]

And we know how reliable that is.  *g*


Matthew  #8-)
-- 
#! /usr/bin/env shell-sbcl
; -*- lisp -*-

;; shebang handler: 
http://www.t8o.org/~mca1001/cgi/viewcvs/*checkout*/lisp/shell-sbcl.pdf?rev=HEAD

#-sb-thread (warn "uh oh, this doesn't look good")

(defun start-threads (n)
  "start threads flavoured a b c, for `n'"
  ;; `n' is shared, apparently without locks
  (let ((init-n n)
        (gap (make-string (* 3 n) :initial-element #\Space))
        ;; we love printf, we do
        (inv-gap (make-string (- 40 (* 3 n))
                              :initial-element
                              ;; (mostly)
                              (if (= 0 (mod n 3)) #\. #\Space))))
    (dolist (x '(a b c))
      (sb-thread:make-thread
       (lambda ()
         (flet ((chatter ()
                  (format t "~a~s  ~a   ~s~%" gap sb-thread:*current-thread* 
inv-gap n)
                  (force-output)))
           (dotimes (i 10)
             (chatter)
             (sleep (* 0.1 n))
             (incf n))))
       :name (format nil "thread_~s~s" init-n x)))))

(unwind-protect
     (progn
       (dotimes (i (parse-integer (or (car shebang:+script-argv+)
                                      "5")))
         (start-threads i))

       ;; wait for all threads but self, in a probably-untidy way
       (do ((left (length (sb-thread:list-all-threads))
                  (length (sb-thread:list-all-threads))))
           ((< left 2))
         (warn "wait for ~a threads..." left)
         (sleep 2)))
  (warn "cleanup in ~s" sb-thread:*current-thread*))
_______________________________________________
Gardeners mailing list
[email protected]
http://www.lispniks.com/mailman/listinfo/gardeners

Reply via email to