Skip to content

Commit 47291bd

Browse files
committed
Include example for coroutines.
1 parent 7cbc78d commit 47291bd

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

LispKit.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
CC5E473D20D2D92900F21B03 /* 69.sld in Copy pre-installed SRFI libraries */ = {isa = PBXBuildFile; fileRef = CC5E473C20D2D8B600F21B03 /* 69.sld */; };
9090
CC5E473F20D2DF5400F21B03 /* SRFI69.scm in Resources */ = {isa = PBXBuildFile; fileRef = CC5E473E20D2D95E00F21B03 /* SRFI69.scm */; };
9191
CC5E474120D316AF00F21B03 /* 129.sld in Copy pre-installed SRFI libraries */ = {isa = PBXBuildFile; fileRef = CC5E474020D315B300F21B03 /* 129.sld */; };
92+
CC5E474320D45AE500F21B03 /* Coroutines.scm in Copy examples */ = {isa = PBXBuildFile; fileRef = CC5E474220D45A8900F21B03 /* Coroutines.scm */; };
9293
CC68B3A91FCCCDEE00EB4AF9 /* object.sld in Copy pre-installed LispKit libraries */ = {isa = PBXBuildFile; fileRef = CC68B3A81FCC73C300EB4AF9 /* object.sld */; };
9394
CC6A3B5C1C52E71F00E962E2 /* ListLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC6A3B5B1C52E71F00E962E2 /* ListLibrary.swift */; };
9495
CC6A3B5E1C52E8EF00E962E2 /* VectorLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC6A3B5D1C52E8EF00E962E2 /* VectorLibrary.swift */; };
@@ -255,6 +256,7 @@
255256
dstPath = LispKit/Resources/Examples;
256257
dstSubfolderSpec = 7;
257258
files = (
259+
CC5E474320D45AE500F21B03 /* Coroutines.scm in Copy examples */,
258260
CC4385BE20BB5F3400055289 /* Compiler.scm in Copy examples */,
259261
CC4385BC20BABAA600055289 /* HTTP.scm in Copy examples */,
260262
CC14F4B31F93AE04000FB1E0 /* AvlTrees.scm in Copy examples */,
@@ -485,6 +487,7 @@
485487
CC5E473C20D2D8B600F21B03 /* 69.sld */ = {isa = PBXFileReference; lastKnownFileType = text; path = 69.sld; sourceTree = "<group>"; };
486488
CC5E473E20D2D95E00F21B03 /* SRFI69.scm */ = {isa = PBXFileReference; lastKnownFileType = text; path = SRFI69.scm; sourceTree = "<group>"; };
487489
CC5E474020D315B300F21B03 /* 129.sld */ = {isa = PBXFileReference; lastKnownFileType = text; path = 129.sld; sourceTree = "<group>"; };
490+
CC5E474220D45A8900F21B03 /* Coroutines.scm */ = {isa = PBXFileReference; lastKnownFileType = text; path = Coroutines.scm; sourceTree = "<group>"; };
488491
CC68B3A81FCC73C300EB4AF9 /* object.sld */ = {isa = PBXFileReference; lastKnownFileType = text; path = object.sld; sourceTree = "<group>"; };
489492
CC6A3B5B1C52E71F00E962E2 /* ListLibrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ListLibrary.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
490493
CC6A3B5D1C52E8EF00E962E2 /* VectorLibrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = VectorLibrary.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
@@ -1017,6 +1020,7 @@
10171020
CC0EBFF81ED37A76004510B2 /* Math.scm */,
10181021
CC9EB1141F5C9D4000697511 /* Topological.scm */,
10191022
CC474EB01ED0390200B535E3 /* Queens.scm */,
1023+
CC5E474220D45A8900F21B03 /* Coroutines.scm */,
10201024
CCBDB73A1EDB4AB3001606E2 /* Prolog.scm */,
10211025
CC4385BD20BB5EE200055289 /* Compiler.scm */,
10221026
CC14F4AE1F9385BF000FB1E0 /* AvlTrees.scm */,
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
;;; Coroutines
2+
;;;
3+
;;; This is a simple example explaining how to implement coroutines in Scheme using
4+
;;; call/cc. The code is based on Matt Might's blog post on "Continuations by example:
5+
;;; Exceptions, time-traveling search, generators, threads, and coroutines" at his
6+
;;; site http://matt.might.net/. The article can be found here: https://goo.gl/pyCHCu .
7+
;;;
8+
;;; The code below introduces the following API for implementing simple coroutine-based
9+
;;; applications:
10+
;;;
11+
;;; - (spawn thunk) puts a thread for thunk into the thread queue.
12+
;;; - (quit) kills the current thread and removes it from the thread queue.
13+
;;; - (yield) hands control from the current thread to another thread.
14+
;;; - (start-threads) starts executing threads in the thread queue.
15+
;;; - (halt) exits all threads.
16+
;;;
17+
;;; Author: Matt Might (might@uab.edu)
18+
19+
(import (srfi 31))
20+
21+
; *thread-queue* : list[continuation]
22+
(define *thread-queue* '())
23+
24+
; halt : continuation
25+
(define halt #f)
26+
27+
;; (current-continuation) -> continuation | any value
28+
;; This procedure returns the continuation in which it was evaluated. A conditional
29+
;; pattern can be used to detect whether the continuation was just created, or the
30+
;; continuation has been invoked from some later point:
31+
;; ```
32+
;; (let ((cc (current-continuation)))
33+
;; (cond ((procedure? cc) body)
34+
;; ((future-value? cc) handling-body)
35+
;; (else (error "Contract violation!"))))
36+
;; ```
37+
(define (current-continuation)
38+
(call-with-current-continuation (lambda (cc) (cc cc))))
39+
40+
;; (spawn thunk)
41+
;; Puts a thread for thunk into the thread queue.
42+
(define (spawn thunk)
43+
(let ((cc (current-continuation)))
44+
(if (procedure? cc)
45+
(set! *thread-queue* (append *thread-queue* (list cc)))
46+
(begin (thunk)
47+
(quit)))))
48+
49+
;; (yield)
50+
;; Hands control from the current thread to another thread.
51+
(define (yield)
52+
(let ((cc (current-continuation)))
53+
(if (and (procedure? cc) (pair? *thread-queue*))
54+
(let ((next-thread (car *thread-queue*)))
55+
(set! *thread-queue* (append (cdr *thread-queue*) (list cc)))
56+
(next-thread 'resume)))))
57+
58+
;; (quit)
59+
;; Kills the current thread and removes it from the thread queue.
60+
(define (quit)
61+
(if (pair? *thread-queue*)
62+
(let ((next-thread (car *thread-queue*)))
63+
(set! *thread-queue* (cdr *thread-queue*))
64+
(next-thread 'resume))
65+
(halt)))
66+
67+
;; (start-threads)
68+
;; Starts executing threads in the thread queue. As a side effect, this function
69+
;; initializes `halt`.
70+
(define (start-threads)
71+
(let ((cc (current-continuation)))
72+
(if cc
73+
(begin
74+
(set! halt (lambda () (cc #f)))
75+
(if (pair? *thread-queue*)
76+
(begin
77+
(let ((next-thread (car *thread-queue*)))
78+
(set! *thread-queue* (cdr *thread-queue*))
79+
(next-thread 'resume))))))))
80+
81+
;; Example of a simple program using three coroutines
82+
83+
(define counter 10)
84+
85+
(define (make-coroutine name)
86+
(rec (loop)
87+
(if (< counter 0)
88+
(begin (display "quitting thread ")
89+
(display name)
90+
(newline)
91+
(quit)))
92+
(display "in thread ")
93+
(display name)
94+
(display "; counter = ")
95+
(display counter)
96+
(newline)
97+
(set! counter (- counter 1))
98+
(yield)
99+
(loop)))
100+
101+
(spawn (make-coroutine 'a))
102+
(spawn (make-coroutine 'b))
103+
(spawn (make-coroutine 'c))
104+
105+
(start-threads)
106+

0 commit comments

Comments
 (0)