-
Notifications
You must be signed in to change notification settings - Fork 0
/
throttle.ss
86 lines (73 loc) · 3.11 KB
/
throttle.ss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#lang scheme
(require "base.ss")
; Structure types --------------------------------
; (struct thread-descriptor channel channel)
;
; Interface to a throttle server:
; - thread-descriptor is the thread descriptor of the server thread;
; - delay is the number of milliseconds to wait between throttled sections;
; - start is a channel with which to request to start a throttled section;
; - finish is a channel with which to acknowledge that a throttled section
; has finished.
(define-struct throttle (thread-descriptor delay start-channel finish-channel) #:transparent)
; Private stuff ----------------------------------
; Returns an alarm event that delays for delay milliseconds.
;
; integer -> alarm-evt
(define (make-throttle-evt delay)
(alarm-evt (+ (current-inexact-milliseconds) delay)))
; Public stuff -----------------------------------
; integer -> throttle
(define (create-throttle delay)
(letrec ([start (make-channel)]
[finish (make-channel)]
[loop (lambda ()
; Wait until someone wants to start a thread.
; Tell them they can go by posting #t back to them.
(channel-put (channel-get start) #t)
; Wait until the request is finished.
(channel-get finish)
; Sleep for 1 second.
(sync (make-throttle-evt delay))
; On to the next request.
(loop))]
[descriptor (thread loop)])
(make-throttle descriptor delay start finish)))
; Terminates a throttle's server thread.
;
; throttle -> void
(define (kill-throttle! throttle)
(if (throttle-alive? throttle)
(let ([descriptor (throttle-thread-descriptor throttle)])
(kill-thread descriptor))
(raise-exn exn:fail:contract
(format "The throttle has been killed: ~a" throttle))))
; Returns #t if the throttle control is still able to receive requests,
; or #f if it has been killed with kill-throttle!.
;
; throttle -> boolean
(define (throttle-alive? throttle)
(not (thread-dead? (throttle-thread-descriptor throttle))))
; throttle (-> a) -> a
(define (call-with-throttle throttle thunk)
(if (throttle-alive? throttle)
(let ([start (throttle-start-channel throttle)]
[finish (throttle-finish-channel throttle)]
[response (make-channel)])
(dynamic-wind
(lambda ()
(channel-put start response)
(channel-get response))
thunk
(lambda ()
(channel-put finish #t))))
(raise-exn exn:fail:contract
(format "The throttle has been killed: ~a" throttle))))
; Provide statements -----------------------------
(provide throttle?)
(provide/contract
[rename create-throttle make-throttle (-> (and/c integer? (>=/c 0)) throttle?)]
[throttle-delay (-> throttle? integer?)]
[throttle-alive? (-> throttle? boolean?)]
[kill-throttle! (-> throttle? void?)]
[call-with-throttle (-> throttle? procedure? any)])