source: project/wiki/eggref/4/gochan @ 36506

Last change on this file since 36506 was 36506, checked in by mario, 11 months ago

eggref/4/gochan: fix markup for `go'

File size: 7.9 KB
Line 
1== chicken-gochan
2[[toc:]]
3[[http://golang.org/|Go]]-inspired channels for [[http://call-cc.org/|Chicken Scheme]] ([[http://synthcode.com/wiki/chibi-scheme|Chibi Scheme]] might work
4too). Essentially thread-safe fifo queues that are useful for thread
5communication and synchronization.
6
7
8=== Dependencies
9* [[wiki.call-cc.org/eggref/4/matchable|matchable]]
10
11
12=== Development Status
13Currently supported:
14
15* receive and send switch ({{gochan-select}})
16* buffered channels
17* timeouts as ordinary receive on a channel
18* closable channels with close-reason (aka {{fail-flag}})
19* load-balancing when multiple channels have data ready
20
21Source code can be found [[https://github.com/Adellica/chicken-gochan|here]].
22
23
24=== Comparison to real Go Channels
25The API and behaviour largely follows [[http://golang.org/|Go]]'s channel API, with some
26exceptions:
27
28* channels don't have any type information
29* sending to a channel that gets closed does not panic, it unblocks
30  all senders immediately with the {{fail}} flag set to non-{{#f}}.
31* closing an already closed channel does not result in error.
32* {{nil}}-channels aren't supported, create new forever-blocking {{(gochan 0)}} instead.
33* Unlike in [[http://golang.org/|Go]], you can choose what channels to select on at runtime with {{gochan-select*}}
34
35
36=== Comparison to [[https://github.com/clojure/core.async|core.async]]
37Honestly, I wish I had stolen the [[https://github.com/clojure/core.async|core.async]] API instead of the [[http://golang.org/|Go]] channel API
38since that's already a LISP, but here is what we have for now:
39
40* {{alt!}} is {{gochan-select}}
41* {{alts!}} is {{gochan-select*}}
42* {{<!}} is {{gochan-recv}}
43* {{>!}} is {{gochan-send}}
44* There is no distinction between {{park}} and {{block}} because CHICKEN
45  doesn't have native threads.
46* The operations don't need to be called inside {{(go ...)}} blocks.
47
48
49=== API
50<procedure> (gochan capacity)</procedure>
51
52Construct a channel with a maximum buffer-size of {{capacity}}. If
53{{capacity}} is {{0}}, the channel is unbuffered and all its operations
54will block until a remote end sends/receives.
55
56<syntax> (gochan-select ((chan <-|-> msg [ fail ]) body ...) ... [(else body ...])</syntax>
57
58This is a channel switch that will send or receive on a single
59channel, picking whichever clause is able to complete soonest. If no
60clause is ready, {{gochan-select}} will block until one does, unless
61{{else}} is specified which will by execute its body instead of
62blocking. Multiple send and receive clauses can be specified
63interchangeably. Note that only one clause will be served.
64
65Here's an example:
66
67<enscript highlight="scheme">    
68(gochan-select
69  ((chan1 -> msg fail) (if fail (print "chan1 closed!") (print "chan1 says " msg)))
70  ((chan2 -> msg fail) (if fail (print "chan2 closed!") (print "chan2 says " msg))))
71</enscript>
72
73Receive clauses, {{((chan -> msg [fail]) body ...)}}, execute {{body}}
74with {{msg}} bound to the message object and {{fail}} bound to a flag
75indicating failure. Receiving from a closed channel immediately
76completes with this {{fail}} flag set to non-{{#f}}.
77
78Send clauses, {{((chan <- msg [fail]) body ...)}}, execute {{body}} after
79{{msg}} has been sent to a receiver, successfully buffered onto the
80channel, or if channel was closed. Sending to a closed channel
81immediately completes with the {{fail}} flag set to {{#f}}.
82
83A send or receive clause on a closed channel with no {{fail}}-flag
84binding specified will immediately return {{(void)}} without executing
85{{body}}. This can be combined with recursion like this:
86
87<enscript highlight="scheme">    
88;; loop forever until either chan1 or chan2 closes
89(let loop ()
90   (gochan-select
91    ((chan1 -> msg) (print "chan1 says " msg) (loop))
92    ((chan2 <- 123) (print "chan2 got  " 123) (loop))))
93</enscript>
94
95Or like this:
96
97<enscript highlight="scheme">    
98;; loop forever until chan1 closes. replacing chan2 is important to avoid busy-wait!
99(let loop ((chan2 chan2))
100  (gochan-select
101   ((chan1 -> msg)      (print "chan1 says " msg) (loop chan2))
102   ((chan2 -> msg fail) (if fail
103                            (begin (print "chan2 closed, keep going")
104                                   ;; create new forever-blocking channel
105                                   (loop (gochan 0)))
106                            (begin (print "chan2 says " msg)
107                                   (loop chan2))))))
108</enscript>
109
110{{gochan-select}} returns the return-value of the executed clause's
111body.
112
113To do a non-blocking receive, you can do the following:
114
115<enscript highlight="scheme">    
116(gochan-select ((chan1 -> msg fail) (if fail #!eof msg))
117               (else 'eagain))
118
119</enscript>
120
121<procedure> (gochan-send chan msg)</procedure>
122
123
124This is short for {{(gochan-select ((chan <- msg fail) (values  #f fail #t)))}}.
125
126<procedure> (gochan-recv chan)</procedure>
127
128This is short for {{(gochan-select ((chan -> msg fail) (values msg fail #t)))}}.
129
130<procedure> (gochan-close chan [fail-flag])</procedure>
131
132Close the channel. Note that this will unblock existing receivers and
133senders waiting for an operation on {{chan}} with the {{fail-flag}} set to
134a non-{{#f}} value. All ''future'' receivers and senders will also
135immdiately unblock in this way, so watch out for busy-loops.
136
137The optional {{fail-flag}} can be used to specify an alternative to the
138default {{#t}}. As this value is given to all receivers and senders of
139{{chan}}, the {{fail-flag}} can be used as a "broadcast"
140mechanism. {{fail-flag}} cannot be {{#f}} as that would indicate a
141successful message transaction.
142
143Closing an already closed channel will results in its {{fail-flag}}
144being updated.
145
146<procedure> (gochan-after duration/ms)</procedure>
147
148Returns a {{gochan}} that will "send" a single message after
149{{duration/ms}} milliseconds of its creation. The message is the
150{{(current-milliseconds)}} value at the time of the timeout (not when
151the message was received). Receiving more than once on an
152{{gochan-after}} channel will block indefinitely or deadlock the second
153time.
154
155<enscript highlight="scheme">    
156(gochan-select
157 ((chan1 -> msg)                (print "chan1 says " msg))
158 (((gochan-after 1000) -> when) (print "chan1 took too long")))
159</enscript>
160
161You cannot send to or close a timer channel. These are special records
162that contain information about when the next timer will
163trigger. Creating timers is a relatively cheap operation, and
164unlike [[https://golang.org/pkg/time/#After|golang.time.After]], may be
165garbage-collected before the timer triggers. Creating a timer does not
166spawn a new thread.
167
168<procedure> (gochan-tick duration/ms)</procedure>
169
170Return a {{gochan}} that will "send" a message every {{duration/ms}}
171milliseconds. The message is the {{(current-milliseconds)}}
172value at the time of the tick (not when it was received).
173
174See [[tests/worker-pool.scm|{{tests/worker-pool.scm}}]] for
175an example of its use.
176
177<syntax> (go body ...)</syntax>
178
179Starts and returns a new srfi-18 thread. Short for {{(thread-start!
180(lambda () body ...))}}.
181
182
183=== Samples
184* See [[tests/worker-pool.scm|{{tests/worker-pool.scm}}]] for a port of
185  [[https://gobyexample.com/worker-pools|this Go example]].
186* See [[tests/secret.scm|{{tests/secret.scm}}]] for a port of
187  [[https://blog.jayway.com/2014/09/16/comparing-core-async-and-rx-by-example/|this]]
188  [[https://github.com/clojure/core.async|core.async]]/[[https://github.com/Reactive-Extensions/RxJS|rxjs]] challenge.
189
190
191=== TODO
192* Perhaps rename the API to [[https://github.com/clojure/core.async|core.async]]'s?
193* Add {{go-map}}, {{go-fold}} and friends (hopefully simple because we can also do [[http://clojure.github.io/core.async/#clojure.core.async/map|this]])
194* Support customizing buffering behaviour, like [[https://github.com/clojure/core.async|core.async]]'s [[http://clojure.github.io/core.async/#clojure.core.async/dropping-buffer|{{dropping-buffer}}]] and [[http://clojure.github.io/core.async/#clojure.core.async/sliding-buffer|{{sliding-buffer}}]] (harder!)
195* Add a priority option to {{gochan-select*}}?
196* Support cancelling timers
197
Note: See TracBrowser for help on using the repository browser.