forked from cadence/breezewiki
		
	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