Advertisement
Guest User

Esteban U C Castro

a guest
Aug 23rd, 2007
255
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scheme 9.02 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. (let ()
  102.  
  103.   (define (make-task thunk)
  104.     (list thunk #f #f))
  105.  
  106.   (define (task-continuation t)
  107.     (car t))
  108.  
  109.   (define (set-task-continuation! t k)
  110.     (set-car! t k))
  111.  
  112.   (define (prev-task t)
  113.     (cadr t))
  114.  
  115.   (define (set-prev-task! t u)
  116.     (set-car! (cdr t) u))
  117.  
  118.   (define (next-task t)
  119.     (caddr t))
  120.  
  121.   (define (set-next-task! t u)
  122.     (set-car! (cddr t) u))
  123.  
  124.   (define (remove-task! t)
  125.     (set-prev-task! (next-task t) (prev-task t))
  126.     (set-next-task! (prev-task t) (next-task t)))
  127.  
  128.   (define (resume-next!)
  129.     (set! current (next-task current))
  130.     ((task-continuation current))
  131.     ; The task has exited normally.
  132.     ; If it was the last task, we just exit,
  133.     ; otherwise, we remove this one and continue with the next.
  134.     (cond
  135.       ((last-task?)
  136.         'bye)
  137.       (else
  138.         (remove-task! current)
  139.         (resume-next!))))
  140.  
  141.   (define (channel-state ch)
  142.     (car ch))
  143.  
  144.   (define (channel-queue ch)
  145.     (cdr ch))
  146.  
  147.   (define (set-channel-state! ch state)
  148.     (set-car! ch state))
  149.  
  150.   (define (add-channel! ch op x)
  151.     (set-channel-state! ch op)
  152.     (queue-push! (channel-queue ch) x))
  153.  
  154.   (define (push-channel-sender! ch sender)
  155.     ; XXX: assert I'm idle or sending.
  156.     (add-channel! ch 'sending sender))
  157.  
  158.   (define (push-channel-receiver! ch receiver)
  159.     ; XXX: assert I'm idle or receiving.
  160.     (add-channel! ch 'receiving receiver))
  161.  
  162.   (define (pop-from-channel! ch)
  163.     (let ((q (channel-queue ch)))
  164.       (let ((ret (queue-pop! q)))
  165.         (if (queue-empty? q)
  166.           (set-channel-state! ch 'idle)
  167.           #f)
  168.         ret)))
  169.  
  170.   (define (pop-channel-sender! ch)
  171.     ; XXX: assert I'm sending.
  172.     (pop-from-channel! ch))
  173.  
  174.   (define (pop-channel-receiver! ch)
  175.     ; XXX: assert I'm receiving.
  176.     (pop-from-channel! ch))
  177.  
  178.   (define (make-sender k val)
  179.     (cons k val))
  180.  
  181.   (define (sender-continuation sender)
  182.     (car sender))
  183.  
  184.   (define (sender-value sender)
  185.     (cdr sender))
  186.  
  187.   (define (block!)
  188.     (if (last-task?)
  189.       (error "I can't block the last remaining task! Who will wake it up?")
  190.       (begin
  191.         (remove-task! current)
  192.         (resume-next!))))
  193.  
  194.   ;;; FIFO Queue.
  195.   ;;;
  196.   ;;; I should really use libraries or put this in a separate module, but I
  197.   ;;; wanted this to be self contained so it's more convenient to give it a
  198.   ;;; try.
  199.  
  200.   (define (make-queue)
  201.     (cons '() '()))
  202.  
  203.   (define (queue-empty? q)
  204.     (null? (queue-first q)))
  205.  
  206.   (define (queue-first q)
  207.     (car q))
  208.  
  209.   (define (queue-last q)
  210.     (cdr q))
  211.  
  212.   (define (set-queue-first! q el)
  213.     (set-car! q el))
  214.  
  215.   (define (set-queue-last! q el)
  216.     (set-cdr! q el))
  217.  
  218.   (define (make-queue-el val prev next)
  219.     (list val prev next))
  220.  
  221.   (define (queue-el-value e)
  222.     (car e))
  223.  
  224.   (define (queue-el-prev e)
  225.     (cadr e))
  226.  
  227.   (define (set-queue-el-prev! el prev)
  228.     (set-car! (cdr el) prev))
  229.  
  230.   (define (queue-el-next e)
  231.     (caddr e))
  232.  
  233.   (define (set-queue-el-next! el next)
  234.     (set-car! (cddr el) next))
  235.  
  236.   (define (queue-push! q val)
  237.     (let ((el (make-queue-el val '() (queue-first q))))
  238.         (if (queue-empty? q)
  239.           (set-queue-last! q el)
  240.           (set-queue-el-prev! (queue-first q) el))
  241.         (set-queue-first! q el)))
  242.  
  243.   (define (queue-pop! q)
  244.     (let ((last-el (queue-last q)))
  245.       (set-queue-last! q (queue-el-prev last-el))
  246.       (if (null? (queue-last q))
  247.         (set-queue-first! q '())
  248.         (begin
  249.           ; Break references to let old items be garbage collected.
  250.           (set-queue-el-next! (queue-last q) '())
  251.           (set-queue-el-prev! last-el '())))
  252.       (queue-el-value last-el)))
  253.  
  254.   ;;; Main task.
  255.  
  256.   (define current #f)
  257.   (set! current (make-task #f))
  258.   (set-prev-task! current current)
  259.   (set-next-task! current current)
  260.  
  261.   ;;; Implementation of exported symbols.
  262.  
  263.   (set! add-next-task!
  264.     (lambda (thunk)
  265.       (let ((t (make-task thunk)))
  266.         (set-prev-task! t current)
  267.         (set-next-task! t (next-task current))
  268.         (set-prev-task! (next-task current) t)
  269.         (set-next-task! current t))))
  270.  
  271.   (set! make-channel
  272.     (lambda ()
  273.       (cons 'idle (make-queue))))
  274.  
  275.   (set! yield
  276.     (lambda ()
  277.       (call-with-current-continuation
  278.         (lambda (k)
  279.           (set-task-continuation! current k)
  280.           (resume-next!)))))
  281.  
  282.   (set! last-task?
  283.     (lambda ()
  284.       (eq? (next-task current) current)))
  285.  
  286.   (set! send-to-channel!
  287.     (lambda (ch val)
  288.       (cond
  289.         ((channel-receiving? ch)
  290.           (let ((receiver-continuation (pop-channel-receiver! ch)))
  291.             (add-next-task! (lambda () (receiver-continuation val)))))
  292.         (else
  293.           (call-with-current-continuation
  294.             (lambda (k)
  295.               (push-channel-sender! ch (make-sender k val))
  296.               (block!)))))))
  297.  
  298.   (set! receive-from-channel!
  299.     (lambda (ch)
  300.       (cond
  301.         ((channel-sending? ch)
  302.           (let ((sender (pop-channel-sender! ch)))
  303.             (add-next-task! (sender-continuation sender))
  304.             (sender-value sender)))
  305.         (else
  306.           (call-with-current-continuation
  307.             (lambda (k)
  308.               (push-channel-receiver! ch k)
  309.               (block!)))))))
  310.  
  311.   (set! channel-sending?
  312.     (lambda (ch)
  313.       (eq? (channel-state ch) 'sending)))
  314.  
  315.   (set! channel-receiving?
  316.     (lambda (ch)
  317.       (eq? (channel-state ch) 'receiving))))
  318.  
  319.  
  320. ;;; Debug.
  321.  
  322. (define (test-task)
  323.   (let ((ch (make-channel)))
  324.     (add-next-task!
  325.       (lambda ()
  326.         (display "yielding")
  327.         (newline)
  328.         (yield)
  329.         (display "sending A")
  330.         (newline)
  331.         (send-to-channel! ch 42)
  332.         (display "done sending A")
  333.         (newline)))
  334.     (add-next-task!
  335.       (lambda ()
  336.         (display "receiving X")
  337.         (newline)
  338.         (display (receive-from-channel! ch))
  339.         (newline)))
  340.     (add-next-task!
  341.       (lambda ()
  342.         (display "sending B")
  343.         (newline)
  344.         (send-to-channel! ch 'muac)
  345.         (display "sent B")
  346.         (newline)))
  347.     ch))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement