source: project/release/5/timed-resource/trunk/timed-resource.scm @ 38596

Last change on this file since 38596 was 38596, checked in by Kon Lovett, 6 months ago

add use-timed-resource

File size: 8.7 KB
Line 
1;;;; timed-resource.scm
2;;;; Kon Lovett, Mar '20
3;;;; Kon Lovett, Jun '17
4;;;; Kon Lovett, Oct '09
5
6;; Issues
7;;
8
9(module timed-resource
10
11(;export
12  timed-resource-shutdown?
13  default-timed-resource-timeout
14  make-timed-resource
15  timed-resource? check-timed-resource error-timed-resource
16  timed-resource-open?
17  timed-resource-name
18  call-with-timed-resource
19  use-timed-resource
20  ;deprecated
21  with-timed-resource)
22
23(import scheme)
24(import (chicken base))
25(import (chicken type))
26(import (only (chicken string) ->string))
27(import (only (chicken condition) abort handle-exceptions with-exception-handler))
28(import (only (srfi 1) delete!))
29(import (only (srfi 18) make-thread thread-start! thread-join! thread-sleep!
30  thread-terminate! thread-signal! make-mutex mutex-name
31  terminated-thread-exception? uncaught-exception?))
32(import (only miscmacros let/cc define-parameter))
33(import (only record-variants define-record-type-variant))
34(import (only thread-utils print-exception-error thread-unblock!))
35(import (only thread-reaper thread-reaper-shutdown? thread-reap!))
36(import (only synch-object make-synch-with-object synch-with-object?))
37(import (only synch-dynexn synch-with set!-synch-with record-synch))
38(import (only synch-open %synch-with %set!-synch-with %record-synch))
39(import (only type-checks check-procedure check-number check-positive-number
40  define-check+error-type))
41
42;; Deprecations
43
44(: with-timed-resource (deprecated call-with-timed-resource))
45
46(define with-timed-resource call-with-timed-resource)
47
48;;
49
50(define-inline (->boolean x) (and x #t))
51
52(define-constant DEFAULT-SHUTDOWN-TIMEOUT #f)
53
54(: default-timed-resource-timeout (#!optional (or boolean number) -> (or boolean number)))
55
56(define-parameter default-timed-resource-timeout DEFAULT-SHUTDOWN-TIMEOUT (lambda (x)
57  (and x (check-positive-number 'default-timed-resource-timeout x))))
58
59;;
60
61(define-type timed-resource (struct timed-resource))
62
63(define-constant timed-resource 'timed-resource) ;type tag variable
64(define-record-type-variant timed-resource (unsafe unchecked inline)
65  (%make-timed-resource op cl to mtx th it)
66  %timed-resource?
67  (op @timed-resource-open)
68  (cl @timed-resource-close)
69  (to timed-resource-timeout)
70  (mtx timed-resource-mutex)
71  (th timed-resource-thread timed-resource-thread-set!)
72  (it timed-resource-item timed-resource-item-set!) )
73
74;;
75
76(: check-timed-resource (* * #!rest --> timed-resource))
77
78(define-check+error-type timed-resource %timed-resource?)
79
80(: timed-resource?      (* -> boolean : timed-resource))
81(: timed-resource-name  (timed-resource --> *))
82(: timed-resource-open?  (timed-resource -> boolean))
83
84(define (timed-resource? obj) (%timed-resource? obj))
85(define (timed-resource-name tr)
86  (mutex-name (timed-resource-mutex (check-timed-resource 'timed-resource-name tr))) )
87(define (timed-resource-open? tr)
88  (->boolean (timed-resource-item (check-timed-resource 'timed-resource-open? tr))) )
89
90;; Open & close a Resource
91
92(define (timed-resource-close tr)
93  (let ((res (timed-resource-item tr)))
94    ;Drop the ref just in case the closer blows up
95    (timed-resource-item-set! tr #f)
96    ((@timed-resource-close tr) res) ) )
97
98(define (timed-resource-open! tr)
99  (timed-resource-item-set! tr ((@timed-resource-open tr))) )
100
101;catches & returns exception conditions
102(define (checked-timed-resource-close tr succflag)
103  (let/cc return
104    (with-exception-handler
105      (lambda (exn) (return exn))
106      (lambda () (timed-resource-close tr) succflag))) )
107
108;; Set of timed resources
109
110(define *timed-resources* #f) ;The set of timed resource objects
111(define *shutdown?* #f)       ;Program shutdown?
112
113(define-inline (*timed-resource-shutdown?) (or *shutdown?* (thread-reaper-shutdown?)))
114
115;Cleanly shutdown remaining timed-resources.
116;Cannot use the reaper since it can shutdown before we do!
117
118(define UNBLOCKED-TAG '(timed-resource-unblocked))
119(define CLOSED-TAG '(timed-resource-closed))
120
121;note that the set of timed resource objects is kept only for shutdown
122;processing.
123(define (shutdown-timed-resources!)
124  (set! *shutdown?* #t)
125  (when *timed-resources*
126    (%synch-with *timed-resources* trs
127      ;release every blocked timed-resource and "manually" reap. Note that if
128      ;the tr is still in the list then it is not queued by the reaper!
129      (for-each
130        (lambda (tr)
131          (let ((th (timed-resource-thread tr)))
132            (thread-unblock! th)
133            (thread-signal! th UNBLOCKED-TAG)
134            (thread-join! th (default-timed-resource-timeout)) ) )
135        trs) ) ) )
136
137#; ;UNUSED
138(define (timed-resource-terminate! tr)
139  (let (
140    (th (timed-resource-thread tr)) )
141    (thread-terminate! th)
142    (when (timed-resource-item tr)
143      (let (
144        (res (checked-timed-resource-close tr CLOSED-TAG)) )
145        (unless (eq? CLOSED-TAG res)
146          (print-exception-error res) ) ) )
147    (handle-exceptions exn
148        (cond
149          ((and
150            (uncaught-exception? exn)
151            (terminated-thread-exception? (uncaught-exception-reason exn)))
152            ;expecting so ignore
153            (void) )
154          (else
155            (print-exception-error exn) ) )
156      (thread-join! th) ) ) )
157
158(define (setup-timed-resource)
159  (set! *timed-resources* (make-synch-with-object '() 'timed-resources))
160  (on-exit shutdown-timed-resources!) )
161
162(define (add-timed-resource! tr)
163  (unless *timed-resources* (setup-timed-resource)) ;done once
164  (%set!-synch-with *timed-resources* trs (cons tr trs)) )
165
166(define (remove-timed-resource! tr)
167  (%set!-synch-with *timed-resources* trs (delete! tr trs eq?)) )
168
169;; The timer thread
170
171;returns CLOSED-TAG for success, otherwise an exception object
172(define (release-timed-resource! tr)
173  ;(assert (eq? (current-thread) (timed-resource-thread tr))) ;used only by a tr!
174  ;(can use weaker synch since close catches exceptions)
175  (%record-synch tr timed-resource
176    ;(returns any exception conditions or CLOSED-TAG for success)
177    (let (
178      (res (checked-timed-resource-close tr CLOSED-TAG))
179      (th (timed-resource-thread tr)) )
180      (cond
181        ;when teminating anyway no exceptions but make a note of any problem
182        ((*timed-resource-shutdown?)
183          (unless (eq? CLOSED-TAG res)
184            (print-exception-error res th) )
185          CLOSED-TAG )
186        (else
187          (remove-timed-resource! tr)
188          (thread-reap! th)
189          res ) ) ) ) )
190
191(define ((make-timed-resource-timer-thunk tr))
192  ;We're active
193  (add-timed-resource! tr)
194  ;allow "timeout" seconds of access
195  (handle-exceptions
196    ;as
197    exn
198    ;with
199    (unless (eq? UNBLOCKED-TAG exn) ;early unblock only "handled exception"
200      (abort exn) )
201    ;in
202    (thread-sleep! (timed-resource-timeout tr)) )
203  ;release the resource but propagate any exceptions
204  (let ((res (release-timed-resource! tr)))
205    (unless (eq? CLOSED-TAG res)
206      (abort res) ) ) )
207
208(define (start-timed-resource-timer! tr)
209  (let (
210    (th
211      (make-thread
212        (make-timed-resource-timer-thunk tr)
213        (timed-resource-name tr))) )
214    (timed-resource-thread-set! tr th)
215    (thread-start! th) ) )
216
217;; Get a timed resource
218
219(define (timed-resource-start! tr)
220  ;open resource before starting the timer thread so the overhead doesn't count
221  ;(and any exceptions can propagate in caller's thread)
222  (timed-resource-open! tr)
223  (start-timed-resource-timer! tr) )
224
225;; Get a resource object
226
227(define (timed-resource-aquire tr)
228  (or
229    ;just return resource if already open
230    (timed-resource-item tr)
231    ;otherwise begin a timed open of the resource
232    (begin
233      (timed-resource-start! tr)
234      (timed-resource-item tr) ) ) )
235
236;(string-append (->string name) "-")
237(define (make-timed-resource-name name) (gensym name))
238
239;;;
240
241(: timed-resource-shutdown? (-> boolean))
242(: make-timed-resource (procedure procedure number #!optional * -> timed-resource))
243(: with-timed-resource (timed-resource (* -> *) -> *))
244
245(define (timed-resource-shutdown?) (*timed-resource-shutdown?))
246
247(define (make-timed-resource opener closer timeout #!optional (name 'timed-resource))
248  (and
249    (not (*timed-resource-shutdown?)) ;Shouldn't be necessary
250    (%make-timed-resource
251      (check-procedure 'make-timed-resource opener 'open-procedure)
252      (check-procedure 'make-timed-resource closer 'close-procedure)
253      (check-positive-number 'make-timed-resource timeout 'timeout)
254      (make-mutex (make-timed-resource-name name))
255      #f #f) ) )
256
257(define (call-with-timed-resource tr proc)
258  (check-timed-resource 'with-timed-resource tr)
259  (check-procedure 'with-timed-resource proc)
260  (unless (*timed-resource-shutdown?) ;Shouldn't be necessary
261    (record-synch tr timed-resource (proc (timed-resource-aquire tr))) ) )
262
263(define-syntax use-timed-resource
264  (syntax-rules ()
265    ((use-timed-resource (?tr ?v) ?body ...)
266      (call-with-timed-resource ?tr (lambda (?v) ?body ...)) ) ) )
267
268) ;module timed-resource
Note: See TracBrowser for help on using the repository browser.