Skip to content

Commit b199e73

Browse files
committed
feat(capy): add deque and channel libraries
1 parent 8c0ce26 commit b199e73

File tree

2 files changed

+328
-0
lines changed

2 files changed

+328
-0
lines changed

lib/capy/channel.sls

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
(library (capy channel)
2+
(export
3+
closed-channel-violation?
4+
channel-close
5+
channel-send
6+
channel-recv
7+
channel-closed?
8+
make-channel
9+
10+
)
11+
(import (rnrs)
12+
(capy deque)
13+
(core threading))
14+
15+
(define-record-type slot
16+
(fields
17+
value))
18+
19+
(define-condition-type &closed-channel &violation
20+
make-closed-channel-violation
21+
closed-channel-violation?)
22+
23+
(define (channel-closed-violation who msg)
24+
(let ((c (condition
25+
(make-closed-channel-violation)
26+
(make-message-condition msg))))
27+
(raise
28+
(if who
29+
(condition c (make-who-condition who))
30+
c))))
31+
32+
;; A Go-like channel.
33+
;; - When `cap` is zero: unbuffered
34+
;; - When `cap` is positive: buffered
35+
;; Close semantics:
36+
;; - close() forbids further sends (raises <closed-channel-condition> on send)
37+
;; - recv() returns value, `channel-closed` singleton when closed and drained, blocks otherwise.
38+
(define-record-type channel
39+
(fields
40+
lock
41+
(mutable %closed?)
42+
buf
43+
(mutable slot)
44+
(mutable recv-waiting)
45+
(mutable send-waiting)
46+
cv-send
47+
cv-recv
48+
cap)
49+
(protocol
50+
(lambda (new)
51+
(lambda (cap)
52+
(new
53+
(make-mutex)
54+
#f
55+
(make-deque cap)
56+
#f
57+
0
58+
0
59+
(make-condition)
60+
(make-condition)
61+
cap)))))
62+
63+
(define chan (make-channel 0))
64+
65+
(define (channel-close chan)
66+
(with-mutex (channel-lock chan)
67+
(channel-%closed?-set! chan #t)
68+
(condition-broadcast (channel-cv-recv chan))
69+
(condition-broadcast (channel-cv-send chan))))
70+
71+
(define (channel-send chan v)
72+
"Send a value `v` to the channel `chan`.
73+
- Blocks until it can be delivered (buffer space or rendezvous).
74+
- Raises channel-closed-violation if the channel is closed."
75+
76+
(with-mutex (channel-lock chan)
77+
(when (channel-%closed? chan)
78+
(channel-closed-violation 'send "send on closed channel"))
79+
(cond
80+
[(= (channel-cap chan) 0) ; unbounded send
81+
(channel-send-waiting-set! chan (+ 1 (channel-send-waiting chan)))
82+
;; rendezvous: wait for a receiver, then handoff via slot.
83+
(let loop ()
84+
(when (and (not (channel-%closed? chan))
85+
(= (channel-recv-waiting chan) 0))
86+
87+
(condition-wait (channel-cv-send chan) (channel-lock chan))
88+
(loop)))
89+
(channel-send-waiting-set! chan
90+
(- (channel-send-waiting chan) 1))
91+
;; somebody might've closed the channel while we waited
92+
93+
(when (channel-%closed? chan)
94+
(channel-closed-violation 'send "send on closed channel"))
95+
96+
(channel-slot-set! chan (make-slot v))
97+
98+
(condition-signal (channel-cv-recv chan))
99+
100+
101+
;; wait until receiver takes it (slot becomes #f)
102+
(let wait-loop ()
103+
(when (and (not (channel-%closed? chan))
104+
(channel-slot chan))
105+
106+
(condition-wait (channel-cv-send chan) (channel-lock chan))
107+
(wait-loop)))]
108+
[else
109+
;; buffered: wait for space
110+
(let loop ()
111+
(when (and (not (channel-%closed? chan))
112+
(= (deque-length (channel-buf chan))
113+
(channel-cap chan)))
114+
115+
(condition-wait (channel-cv-send chan) (channel-lock chan))
116+
(loop)))
117+
(when (channel-%closed? chan)
118+
(channel-closed-violation 'send "send on closed channel"))
119+
(deque-push-back! (channel-buf chan) v)
120+
(condition-signal (channel-cv-recv chan))])))
121+
122+
(define (channel-recv chan)
123+
124+
(with-mutex (channel-lock chan)
125+
(cond
126+
[(= (channel-cap chan) 0) ; unbuffered
127+
(channel-recv-waiting-set! chan (+ 1 (channel-recv-waiting chan)))
128+
(condition-signal (channel-cv-send chan)) ; wake a sender
129+
(let loop ()
130+
(when (and (not (channel-%closed? chan))
131+
(not (channel-slot chan)))
132+
(condition-wait (channel-cv-recv chan) (channel-lock chan))
133+
(loop)))
134+
(channel-recv-waiting-set! chan (- (channel-recv-waiting chan) 1))
135+
136+
(cond
137+
[(channel-slot chan) =>
138+
(lambda (slot)
139+
(channel-slot-set! chan #f)
140+
(condition-signal (channel-cv-send chan))
141+
(slot-value slot))]
142+
[else
143+
#f])]
144+
[else
145+
(let loop ()
146+
(when (and (not (channel-%closed? chan))
147+
(= (deque-length (channel-buf chan)) 0))
148+
149+
(condition-wait (channel-cv-recv chan) (channel-lock chan))
150+
(loop)))
151+
(if (> (deque-length (channel-buf chan)) 0)
152+
(let ((v (deque-pop-front! (channel-buf chan))))
153+
(condition-signal (channel-cv-send chan))
154+
v)
155+
#f)])))
156+
157+
(define (channel-closed? chan)
158+
"Returns #t if the channel is closed, #f otherwise.
159+
160+
Note: a closed channel may still have buffered values to receive."
161+
(with-mutex (channel-lock chan)
162+
(channel-%closed? chan))))

lib/capy/deque.sls

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
(library (capy deque)
2+
(export
3+
make-deque
4+
deque
5+
deque-capacity
6+
deque-length
7+
deque-push-back!
8+
deque-push-front!
9+
deque-pop-back!
10+
deque-pop-front!
11+
deque-ref
12+
deque-set!
13+
deque->vector
14+
vector->deque
15+
list->deque
16+
deque-append!)
17+
(import (rnrs)
18+
(core struct)
19+
(only (scheme base) vector-copy)
20+
(only (scheme r5rs) modulo)
21+
(only (capy) register-tuple-printer! print))
22+
23+
(define-struct %deque
24+
(buf head len))
25+
26+
(register-tuple-printer!
27+
'type:%deque
28+
(lambda (obj port quote?)
29+
(display "#<deque " port)
30+
(let ((len (deque-length obj))
31+
(last (- (deque-length obj) 1)))
32+
(let loop ((i 0))
33+
(when (< i len)
34+
(print (deque-ref obj i) port quote?)
35+
(when (< i last)
36+
(display " " port))
37+
(loop (+ i 1)))))
38+
(display ">" port)))
39+
40+
41+
(define (make-deque capacity)
42+
(let ((initial-capacity (if (>= capacity 1)
43+
capacity
44+
8)))
45+
(make-%deque (make-vector initial-capacity) 0 0)))
46+
47+
(define (deque . args)
48+
(let ((dq (make-deque (max 8 (length args)))))
49+
(for-each (lambda (v) (deque-push-back! dq v)) args)
50+
dq))
51+
52+
(define (grow! dq)
53+
(let* ((old-buf (%deque-buf dq))
54+
(old-cap (vector-length old-buf))
55+
(new-cap (* 2 old-cap))
56+
(new-buf (make-vector new-cap)))
57+
;; Copy elements linearly to the new buffer
58+
(do ((i 0 (+ i 1)))
59+
((= i (deque-len dq)))
60+
(vector-set! new-buf i
61+
(vector-ref old-buf
62+
(modulo (+ (deque-head dq) i) old-cap))))
63+
(deque-buf-set! dq new-buf)
64+
(deque-head-set! dq 0)))
65+
(define (deque-capacity dq)
66+
(vector-length (%deque-buf dq)))
67+
(define (deque-length dq)
68+
(%deque-len dq))
69+
70+
(define (deque-push-back! dq value)
71+
(when (= (%deque-len dq) (deque-capacity dq))
72+
(grow! dq))
73+
(let* ((buf (%deque-buf dq))
74+
(head (%deque-head dq))
75+
(len (%deque-len dq))
76+
(cap (vector-length buf))
77+
(tail-index (modulo (+ head len) cap)))
78+
(vector-set! buf tail-index value)
79+
(%deque-len-set! dq (+ len 1))))
80+
81+
(define (deque-push-front! dq value)
82+
(when (= (%deque-len dq) (deque-capacity dq))
83+
(grow! dq))
84+
(let* ((buf (%deque-buf dq))
85+
(head (%deque-head dq))
86+
(len (%deque-len dq))
87+
(cap (vector-length buf))
88+
(new-head (modulo (+ head (- cap 1)) cap)))
89+
(vector-set! buf new-head value)
90+
(%deque-head-set! dq new-head)
91+
(%deque-len-set! dq (+ len 1))))
92+
93+
(define (deque-pop-back! dq)
94+
(unless (> (%deque-len dq) 0)
95+
(error 'deque-pop-back! "empty deque"))
96+
97+
(let* ((buf (%deque-buf dq))
98+
(head (%deque-head dq))
99+
(len (%deque-len dq))
100+
(cap (vector-length buf))
101+
(tail-index (modulo (+ head (- len 1)) cap))
102+
(value (vector-ref buf tail-index)))
103+
(%deque-len-set! dq (- len 1))
104+
value))
105+
106+
(define (deque-pop-front! dq)
107+
(unless (> (%deque-len dq) 0)
108+
(error 'deque-pop-front! "empty deque"))
109+
110+
(let* ((buf (%deque-buf dq))
111+
(head (%deque-head dq))
112+
(len (%deque-len dq))
113+
(cap (vector-length buf))
114+
(value (vector-ref buf head))
115+
(new-head (modulo (+ head 1) cap)))
116+
(%deque-head-set! dq new-head)
117+
(%deque-len-set! dq (- len 1))
118+
value))
119+
120+
(define (deque-ref dq index)
121+
(unless (and (>= index 0) (< index (%deque-len dq)))
122+
(error 'deque-ref "index out of bounds"))
123+
(let* ((buf (%deque-buf dq))
124+
(head (%deque-head dq))
125+
(cap (vector-length buf))
126+
(real-index (modulo (+ head index) cap)))
127+
(vector-ref buf real-index)))
128+
129+
(define (deque-set! dq index value)
130+
(unless (and (>= index 0) (< index (%deque-len dq)))
131+
(error 'deque-set! "index out of bounds"))
132+
(let* ((buf (%deque-buf dq))
133+
(head (%deque-head dq))
134+
(cap (vector-length buf))
135+
(real-index (modulo (+ head index) cap)))
136+
(vector-set! buf real-index value)))
137+
138+
(define (deque->vector dq)
139+
(let* ((buf (%deque-buf dq))
140+
(head (%deque-head dq))
141+
(len (%deque-len dq))
142+
(cap (vector-length buf))
143+
(result (make-vector len)))
144+
(do ((i 0 (+ i 1)))
145+
((= i len) result)
146+
(vector-set! result i
147+
(vector-ref buf
148+
(modulo (+ head i) cap))))))
149+
150+
(define (vector->deque vec)
151+
(let* ((len (vector-length vec))
152+
(dq (make-deque len)))
153+
(do ((i 0 (+ i 1)))
154+
((= i len) dq)
155+
(deque-push-back! dq (vector-ref vec i)))))
156+
157+
(define (list->deque lst)
158+
(let ((dq (make-deque (max 8 (length lst)))))
159+
(for-each (lambda (v) (deque-push-back! dq v)) lst)
160+
dq))
161+
162+
(define (deque-append! dq1 dq2)
163+
(do ((i 0 (+ i 1)))
164+
((= i (deque-length dq2)))
165+
(deque-push-back! dq1 (deque-ref dq2 i)))))
166+

0 commit comments

Comments
 (0)