Replace thread-let with thread-utils
This commit is contained in:
parent
5fa6e2fb9e
commit
501dcaa3fc
9 changed files with 255 additions and 215 deletions
|
@ -4,8 +4,6 @@
|
|||
(provide
|
||||
; help make a nested if. if/in will gain the same false form of its containing if/out.
|
||||
if/out
|
||||
; let, but the value for each variable is evaluated within a thread
|
||||
thread-let
|
||||
; cond, but values can be defined between conditions
|
||||
cond/var
|
||||
; wrap sql statements into lambdas so they can be executed during migration
|
||||
|
@ -25,7 +23,6 @@
|
|||
|
||||
(provide
|
||||
transform-if/out
|
||||
transform-thread-let
|
||||
transform/out-cond/var)
|
||||
|
||||
(define (transform-if/out stx)
|
||||
|
@ -51,26 +48,6 @@
|
|||
[#t node])))
|
||||
(datum->syntax stx (cons 'if result)))
|
||||
|
||||
(define (transform-thread-let stx)
|
||||
(define tree (cdr (syntax->datum stx)))
|
||||
(define defs (car tree))
|
||||
(define forms (cdr tree))
|
||||
(when (eq? (length forms) 0)
|
||||
(error (format "thread-let: bad syntax (need some forms to execute after the threads)~n forms: ~a" forms)))
|
||||
(define counter (build-list (length defs) values))
|
||||
(datum->syntax
|
||||
stx
|
||||
`(let ([chv (build-vector ,(length defs) (λ (_) (make-channel)))])
|
||||
,@(map (λ (n)
|
||||
(define def (list-ref defs n))
|
||||
`(thread (λ () (channel-put (vector-ref chv ,n) (let _ () ,@(cdr def))))))
|
||||
counter)
|
||||
(let ,(map (λ (n)
|
||||
(define def (list-ref defs n))
|
||||
`(,(car def) (channel-get (vector-ref chv ,n))))
|
||||
counter)
|
||||
,@forms))))
|
||||
|
||||
(define (transform/out-cond/var stx)
|
||||
(define tree (transform-cond/var (cdr (syntax->datum stx))))
|
||||
(datum->syntax
|
||||
|
@ -119,35 +96,6 @@
|
|||
(check-equal? (if/out #t (if/in #f 'yes) 'no) 'no)
|
||||
(check-equal? (if/out #f (if/in #f 'yes) 'no) 'no))
|
||||
|
||||
(define-syntax (thread-let stx)
|
||||
(transform-thread-let stx))
|
||||
(module+ test
|
||||
; check that it is transformed as expected
|
||||
(check-syntax-equal?
|
||||
(transform-thread-let
|
||||
#'(thread-let ([a (hey "this is a")]
|
||||
[b (hey "this is b")])
|
||||
(list a b)))
|
||||
#'(let ([chv (build-vector 2 (λ (_) (make-channel)))])
|
||||
(thread (λ () (channel-put (vector-ref chv 0) (let _ () (hey "this is a")))))
|
||||
(thread (λ () (channel-put (vector-ref chv 1) (let _ () (hey "this is b")))))
|
||||
(let ([a (channel-get (vector-ref chv 0))]
|
||||
[b (channel-get (vector-ref chv 1))])
|
||||
(list a b))))
|
||||
; check that they actually execute concurrently
|
||||
(define ch (make-channel))
|
||||
(check-equal? (thread-let ([a (begin
|
||||
(channel-put ch 'a)
|
||||
(channel-get ch))]
|
||||
[b (begin0
|
||||
(channel-get ch)
|
||||
(channel-put ch 'b))])
|
||||
(list a b))
|
||||
'(b a))
|
||||
; check that it assigns the correct value to the correct variable
|
||||
(check-equal? (thread-let ([a (sleep 0) 'a] [b 'b]) (list a b))
|
||||
'(a b)))
|
||||
|
||||
(define-syntax (cond/var stx)
|
||||
(transform/out-cond/var stx))
|
||||
(module+ test
|
||||
|
|
72
lib/thread-utils.rkt
Normal file
72
lib/thread-utils.rkt
Normal file
|
@ -0,0 +1,72 @@
|
|||
#lang racket/base
|
||||
(require (prefix-in easy: net/http-easy)
|
||||
"../src/data.rkt"
|
||||
"xexpr-utils.rkt")
|
||||
|
||||
(provide
|
||||
thread-values)
|
||||
|
||||
(module+ test
|
||||
(require rackunit))
|
||||
|
||||
(define (thread-values . thunks)
|
||||
(parameterize-break #t
|
||||
(define the-exn (box #f))
|
||||
(define original-thread (current-thread))
|
||||
(define (break e)
|
||||
(when (box-cas! the-exn #f e)
|
||||
(break-thread original-thread))
|
||||
(sleep 0))
|
||||
(define-values (threads channels)
|
||||
(for/fold ([ts null]
|
||||
[chs null]
|
||||
#:result (values (reverse ts) (reverse chs)))
|
||||
([th thunks])
|
||||
(define ch (make-channel))
|
||||
(define t
|
||||
(thread (λ ()
|
||||
(with-handlers ([exn? break])
|
||||
(channel-put ch (th))))))
|
||||
(values (cons t ts) (cons ch chs))))
|
||||
(apply
|
||||
values
|
||||
(with-handlers ([exn:break? (λ (_)
|
||||
(for ([t threads]) (kill-thread t))
|
||||
(if (unbox the-exn)
|
||||
(raise (unbox the-exn))
|
||||
(error 'thread-values "a thread broke, but without reporting its exception")))])
|
||||
(for/list ([ch channels])
|
||||
(channel-get ch))))))
|
||||
|
||||
(module+ test
|
||||
; check that they actually execute concurrently
|
||||
(define ch (make-channel))
|
||||
(check-equal? (let-values ([(a b)
|
||||
(thread-values
|
||||
(λ ()
|
||||
(begin
|
||||
(channel-put ch 'a)
|
||||
(channel-get ch)))
|
||||
(λ ()
|
||||
(begin0
|
||||
(channel-get ch)
|
||||
(channel-put ch 'b))))])
|
||||
(list a b))
|
||||
'(b a))
|
||||
; check that it assigns the correct value to the correct variable
|
||||
(check-equal? (let-values ([(a b)
|
||||
(thread-values
|
||||
(λ () (sleep 0) 'a)
|
||||
(λ () 'b))])
|
||||
(list a b))
|
||||
'(a b))
|
||||
; check that exceptions are passed to the original thread, and other threads are killed
|
||||
;; TODO: if the other thread was making an HTTP request, could it be left stuck open by the kill?
|
||||
(check-equal? (let* ([x "!"]
|
||||
[res
|
||||
(with-handlers ([exn:fail:user? (λ (e) (exn-message e))])
|
||||
(thread-values
|
||||
(λ () (sleep 0) (set! x "?") (println "this side effect should not happen"))
|
||||
(λ () (raise-user-error "catch me"))))])
|
||||
(string-append res x))
|
||||
"catch me!"))
|
|
@ -277,8 +277,8 @@
|
|||
; check that noscript images are removed
|
||||
(check-equal? ((query-selector (λ (t a c) (eq? t 'noscript)) transformed)) #f)
|
||||
; benchmark
|
||||
(when (file-exists? "../misc/Frog.html")
|
||||
(with-input-from-file "../misc/Frog.html"
|
||||
(when (file-exists? "../storage/Frog.html")
|
||||
(with-input-from-file "../storage/Frog.html"
|
||||
(λ ()
|
||||
(define tree (html->xexp (current-input-port)))
|
||||
(time (length (update-tree-wiki tree "minecraft")))))))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue