Newsgroups: comp.lang.scheme
Path: cantaloupe.srv.cs.cmu.edu!das-news2.harvard.edu!news4.ner.bbnplanet.net!news3.near.net!paperboy.wellfleet.com!news-feed-1.peachnet.edu!news.duke.edu!zombie.ncsc.mil!news.mathworks.com!news.kei.com!wang!news
From: holder@discus.technion.ac.il (Ophir Holder)
Subject: Re: desperate call/cc question
Organization: Technion, Israel Institute of Technology
Date: Sun, 9 Jul 1995 12:45:20 GMT
Message-ID: <DBG8rL.244@discus.technion.ac.il>
References: <3tjr44$77u@spool.cs.wisc.edu> <3tk6ef$7sr@agate.berkeley.edu>
Sender: news@wang.com
Lines: 110

From: nweaver@madrone.CS.Berkeley.EDU (Nicholas C. Weaver)
Newsgroups: comp.lang.scheme
Subject: Re: desperate call/cc question
Date: 7 Jul 1995 20:46:07 GMT
Organization: University of California, Berkeley
Message-ID: <3tk6ef$7sr@agate.berkeley.edu>
References: <3tjr44$77u@spool.cs.wisc.edu>

In article <3tjr44$77u@spool.cs.wisc.edu>,
William Annis <wannis@yar.cs.wisc.edu> wrote:
>        Is there any online info on how to work a continuation?  My
>copy of R4RS is downright rhapsodic on the possibilities provided by
>continutations, but I have yet to see an example I understand.  I'm
>not even quite sure what the a call to call/cc does.

	Well, this is my canonical example of just how cool call/cc
can be.  It may not be the clearest example, but with a little thought
it should make sense.  This is simple, cooperative multitasking.
(exit) exits the current thread.  If there is another thread, that
thread is run, otherwise (fork fn arg) creates a new thread
which executes (fn arg) and then terminates when the thread is done.
(yeild) causes the current running thread to give up control.

	It's not the most efficient code right now (eg, the heavy use
of append, and the fact that a naive implementation of call/cc is
rarely efficient).

	It's somewhat long, mostly because of the comments, not the code.

;; well, a (not so) simple call/cc example:  Cooperative multithreading

;; Written off the top of my head.  No guarentees of correctness

;; Using call/cc between these threads is asking for trouble, and
;; will most likely break this system horribly!

;; The list of threads waiting to run.  This is a list of one
;; argument non-returning functions (continuations, mostly)
;; A continuation is a non-returning function, just like (exit),
;; in that it never gives up controll to whoever called it.
(define readyList '())

;; A non-returning function.  If there is any other thread
;; waiting to be run, it causes the next thread to run if there
;; is any left to run, otherwise it calls the origional exit
;; which exits the whole environment.
(define exit
  ;; The origional exit which we override.
  (let ((exit exit))
    ;; The overriding function.
    (lambda ()
      (if (not (null? readyList))
          ;; There is another thread waiting to be run.
          ;; So we run it.
          (let ((cont (car readyList)))
            (set! readyList (cdr readyList))
            ;; Since the readyList is only non-returning
            ;; functions, this will not return.
            (cont '()))
          ;; Nothing left to run.
          ;; The origional (exit) is a non returning function,
          ;; so this is a non-returhning function.
          (exit)))))

;; Takes a one argument function with a given
;; argument and forks it off.  The forked function's new
;; thread will exit if/when the function ever exits.
(define (fork fn arg)
  (set! readyList
        (append readyList
                ;; This function added to the
                ;; readyList is non-returning,
                ;; since exit is non returning.
                (cons
                 (lambda (x)
                   (fn arg)
                   (exit)) '()))))

;; Gives up controll for the next thread waiting to be run.
;; Although it will eventually return, it gives up controll
;; and will only regain it when the continuation is called.
(define (yeild)
  (call-with-current-continuation
   ;; Capture the continuation representing THIS call to yeild
   (lambda (thisCont)
     ;; Stick it on the ready list
     (set! readyList
           (append readyList
                   (cons thisCont '())))
     ;; Get the next thread, and start it running.
     (let ((cont (car readyList)))
       (set! readyList (cdr readyList))
       ;; Run it.
       (cont '())))))


;; Test case, at the prompt
;; > (load "multitask.scm")
;; > (fork (lambda (x) (display x) (newline)) 'foo)
;; > (fork (lambda (x) (display x) (newline)) 'bar)
;; > (yeild)
;; > (yeild)
;; > (fork (lambda (x) (display x) (newline)) 'baz)
;; > (exit)
--
Nicholas C. Weaver			    nweaver@madrone.cs.berkeley.edu
It is a tale, told by an idiot, full of sound and fury, .signifying nothing
		Ash C++ durbatuluk, ash C++ gimbatul,
	    ash C++ thrakatuluk agh burzum-ishi krimpatul!

