Guest User

Esteban U C Castro

a guest
Aug 23rd, 2007
294
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scheme 9.24 KB | None | 0 0
  1. ;;;
  2. ;;; Coroutine library, inspired by Stackless Python's API.
  3. ;;; (www.stackless.com)
  4. ;;;
  5. ;;; This has been tested in Gambit-C 4.0 beta 20.  AFAIK it should run on any
  6. ;;; R5RS compliant Scheme, or maybe earlier, since it doesn't use macros, and
  7. ;;; it has no external dependencies.  Please bear with the verbosity.
  8. ;;;
  9. ;;; (c) 2007 Esteban U. Caama�o Castro
  10. ;;;
  11. ;;; This code is in the public domain.
  12. ;;;
  13.  
  14. ;;; Exported symbols (these are set! below, in the implementation scope).
  15.  
  16. @@; (add-next-task! thunk) -> <undefined>
  17. ;
  18. @@; Schedule a new task to be run right after the current one yields or is
  19. @@; blocked.
  20. ;
  21. ; If you'd rather switch to that one immediately, just (yield) right after
  22. ; calling this.
  23. ;
  24. ; If you'd rather schedule it after other waiting tasks, just (yield)
  25. ; first thing in the thunk.
  26. (define add-next-task! #f)
  27.  
  28. @@; (last-task?) -> bool
  29. ;
  30. @@; Whether this one is the only nonblocked task.
  31. (define last-task? #f)
  32.  
  33. @@; (yield) -> <undefined>
  34. ;
  35. @@; Pause the current task and resume the next scheduled one.
  36. ;
  37. ; This task will be resumed after all the other scheduled tasks have had a
  38. ; chance to run.
  39. (define yield #f)
  40.  
  41. @@; (make-channel) -> channel object
  42. ;
  43. ; channel constructor.
  44. ;
  45. @@; A channel is the facility to communicate and synchronize between tasks.
  46. (define make-channel #f)
  47.  
  48. @@; (send-to-channel! channel value) -> <undefined>
  49. ;
  50. ; If there are any tasks waiting to receive data from this channel, this call
  51. ; returns immediately, and the oldest of the waiting tasks will be resumed
  52. ; next after the current task is paused or blocked.  
  53. ;
  54. ; If there are no such waiting tasks, the current task will be blocked until
  55. ; other task calls receive-from-channel! on this channel.
  56. ;
  57. @@; In either case, the value passed to this procedure is the value that the
  58. @@; receiving task will get.
  59. (define send-to-channel! #f)
  60.  
  61. @@; (receive-from-channel! channel) -> some value
  62. ;
  63. ; If there are any tasks waiting to send data to this channel, this call
  64. ; returns immediately, and the oldest of such tasks will be scheduled to be
  65. ; resumed right after the current task is paused or blocked.
  66. ;
  67. ; If there are no such waiting tasks, the current task will be blocked until
  68. ; other task calls send-to-channel! on this channel.
  69. ;
  70. @@; In either case this procedure returns the value passed to the corresponding
  71. @@; send-to-channel! call.
  72. (define receive-from-channel! #f)
  73.  
  74. @@; (channel-sending? channel) -> bool
  75. ;
  76. @@; Whether there are any tasks waiting to send data through this channel.
  77. (define channel-sending? #f)
  78.  
  79. @@; (channel-receiving? channel) -> bool
  80. ;
  81. @@; Whether there are any tasks waiting to receive data from this channel.
  82. (define channel-receiving? #f)
  83.  
  84. ;;; Utility.
  85.  
  86. ; (run-tasks) -> <undefined>
  87. ;
  88. ; This just yields until there are no other non-blocked tasks.
  89. ;
  90. ; This is usually done in the main task: you set up initial tasks, let them
  91. ; run, and when they all are done this call returns and you clean up.
  92. (define (run-tasks)
  93.   (if (last-task?)
  94.     'done
  95.     (begin
  96.       (yield)
  97.       (run-tasks))))
  98.  
  99. ;;; Implementation.
  100. ;;;
  101. @@;;; Ignore this on first reading; just search forward for
  102. @@;;;     "Implementation of exported symbols."
  103.  
  104. (let ()
  105.  
  106.   (define (make-task thunk)
  107.     (list thunk #f #f))
  108.  
  109.   (define (task-continuation t)
  110.     (car t))
  111.  
  112.   (define (set-task-continuation! t k)
  113.     (set-car! t k))
  114.  
  115.   (define (prev-task t)
  116.     (cadr t))
  117.  
  118.   (define (set-prev-task! t u)
  119.     (set-car! (cdr t) u))
  120.  
  121.   (define (next-task t)
  122.     (caddr t))
  123.  
  124.   (define (set-next-task! t u)
  125.     (set-car! (cddr t) u))
  126.  
  127.   (define (remove-task! t)
  128.     (set-prev-task! (next-task t) (prev-task t))
  129.     (set-next-task! (prev-task t) (next-task t)))
  130.  
  131.   (define (resume-next!)
  132.     (set! current (next-task current))
  133.     ((task-continuation current))
  134.     ; The task has exited normally.
  135.     ; If it was the last task, we just exit,
  136.     ; otherwise, we remove this one and continue with the next.
  137.     (cond
  138.       ((last-task?)
  139.         'bye)
  140.       (else
  141.         (remove-task! current)
  142.         (resume-next!))))
  143.  
  144.   (define (channel-state ch)
  145.     (car ch))
  146.  
  147.   (define (channel-queue ch)
  148.     (cdr ch))
  149.  
  150.   (define (set-channel-state! ch state)
  151.     (set-car! ch state))
  152.  
  153.   (define (add-channel! ch op x)
  154.     (set-channel-state! ch op)
  155.     (queue-push! (channel-queue ch) x))
  156.  
  157.   (define (push-channel-sender! ch sender)
  158.     ; XXX: assert I'm idle or sending.
  159.     (add-channel! ch 'sending sender))
  160.  
  161.   (define (push-channel-receiver! ch receiver)
  162.     ; XXX: assert I'm idle or receiving.
  163.     (add-channel! ch 'receiving receiver))
  164.  
  165.   (define (pop-from-channel! ch)
  166.     (let ((q (channel-queue ch)))
  167.       (let ((ret (queue-pop! q)))
  168.         (if (queue-empty? q)
  169.           (set-channel-state! ch 'idle)
  170.           #f)
  171.         ret)))
  172.  
  173.   (define (pop-channel-sender! ch)
  174.     ; XXX: assert I'm sending.
  175.     (pop-from-channel! ch))
  176.  
  177.   (define (pop-channel-receiver! ch)
  178.     ; XXX: assert I'm receiving.
  179.     (pop-from-channel! ch))
  180.  
  181.   (define (make-sender k val)
  182.     (cons k val))
  183.  
  184.   (define (sender-continuation sender)
  185.     (car sender))
  186.  
  187.   (define (sender-value sender)
  188.     (cdr sender))
  189.  
  190.   (define (block!)
  191.     (if (last-task?)
  192.       (error "I can't block the last remaining task! Who will wake it up?")
  193.       (begin
  194.         (remove-task! current)
  195.         (resume-next!))))
  196.  
  197.   ;;; FIFO Queue.
  198.   ;;;
  199.   ;;; I should really use libraries or put this in a separate module, but I
  200.   ;;; wanted this to be self contained so it's more convenient to give it a
  201.   ;;; try.
  202.  
  203.   (define (make-queue)
  204.     (cons '() '()))
  205.  
  206.   (define (queue-empty? q)
  207.     (null? (queue-first q)))
  208.  
  209.   (define (queue-first q)
  210.     (car q))
  211.  
  212.   (define (queue-last q)
  213.     (cdr q))
  214.  
  215.   (define (set-queue-first! q el)
  216.     (set-car! q el))
  217.  
  218.   (define (set-queue-last! q el)
  219.     (set-cdr! q el))
  220.  
  221.   (define (make-queue-el val prev next)
  222.     (list val prev next))
  223.  
  224.   (define (queue-el-value e)
  225.     (car e))
  226.  
  227.   (define (queue-el-prev e)
  228.     (cadr e))
  229.  
  230.   (define (set-queue-el-prev! el prev)
  231.     (set-car! (cdr el) prev))
  232.  
  233.   (define (queue-el-next e)
  234.     (caddr e))
  235.  
  236.   (define (set-queue-el-next! el next)
  237.     (set-car! (cddr el) next))
  238.  
  239.   (define (queue-push! q val)
  240.     (let ((el (make-queue-el val '() (queue-first q))))
  241.         (if (queue-empty? q)
  242.           (set-queue-last! q el)
  243.           (set-queue-el-prev! (queue-first q) el))
  244.         (set-queue-first! q el)))
  245.  
  246.   (define (queue-pop! q)
  247.     (let ((last-el (queue-last q)))
  248.       (set-queue-last! q (queue-el-prev last-el))
  249.       (if (null? (queue-last q))
  250.         (set-queue-first! q '())
  251.         (begin
  252.           ; Break references to let old items be garbage collected.
  253.           (set-queue-el-next! (queue-last q) '())
  254.           (set-queue-el-prev! last-el '())))
  255.       (queue-el-value last-el)))
  256.  
  257.   ;;; Main task.
  258.  
  259.   (define current #f)
  260.   (set! current (make-task #f))
  261.   (set-prev-task! current current)
  262.   (set-next-task! current current)
  263.  
  264.   ;;; Implementation of exported symbols.
  265.  
  266. @@  (set! add-next-task!
  267. @@    (lambda (thunk)
  268. @@      (let ((t (make-task thunk)))
  269. @@        (set-prev-task! t current)
  270. @@        (set-next-task! t (next-task current))
  271. @@        (set-prev-task! (next-task current) t)
  272. @@        (set-next-task! current t))))
  273.  
  274.   (set! make-channel
  275.     (lambda ()
  276.       (cons 'idle (make-queue))))
  277.  
  278. @@  (set! yield
  279. @@    (lambda ()
  280. @@      (call-with-current-continuation
  281. @@        (lambda (k)
  282. @@          (set-task-continuation! current k)
  283. @@          (resume-next!)))))
  284.  
  285.   (set! last-task?
  286.     (lambda ()
  287.       (eq? (next-task current) current)))
  288.  
  289. @@  (set! send-to-channel!
  290. @@    (lambda (ch val)
  291. @@      (cond
  292. @@        ((channel-receiving? ch)
  293. @@          (let ((receiver-continuation (pop-channel-receiver! ch)))
  294. @@            (add-next-task! (lambda () (receiver-continuation val)))))
  295. @@        (else
  296. @@          (call-with-current-continuation
  297. @@            (lambda (k)
  298. @@              (push-channel-sender! ch (make-sender k val))
  299. @@              (block!)))))))
  300.  
  301. @@  (set! receive-from-channel!
  302. @@    (lambda (ch)
  303. @@      (cond
  304. @@        ((channel-sending? ch)
  305. @@          (let ((sender (pop-channel-sender! ch)))
  306. @@            (add-next-task! (sender-continuation sender))
  307. @@            (sender-value sender)))
  308. @@        (else
  309. @@          (call-with-current-continuation
  310. @@            (lambda (k)
  311. @@              (push-channel-receiver! ch k)
  312. @@              (block!)))))))
  313.  
  314.   (set! channel-sending?
  315.     (lambda (ch)
  316.       (eq? (channel-state ch) 'sending)))
  317.  
  318.   (set! channel-receiving?
  319.     (lambda (ch)
  320.       (eq? (channel-state ch) 'receiving))))
  321.  
  322.  
  323. ;;; Debug.
  324.  
  325. (define (test-task)
  326.   (let ((ch (make-channel)))
  327.     (add-next-task!
  328.       (lambda ()
  329.         (display "yielding")
  330.         (newline)
  331.         (yield)
  332.         (display "sending A")
  333.         (newline)
  334.         (send-to-channel! ch 42)
  335.         (display "done sending A")
  336.         (newline)))
  337.     (add-next-task!
  338.       (lambda ()
  339.         (display "receiving X")
  340.         (newline)
  341.         (display (receive-from-channel! ch))
  342.         (newline)))
  343.     (add-next-task!
  344.       (lambda ()
  345.         (display "sending B")
  346.         (newline)
  347.         (send-to-channel! ch 'muac)
  348.         (display "sent B")
  349.         (newline)))
  350.     ch))
Advertisement
Add Comment
Please, Sign In to add comment