You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

135 lines
4.2 KiB
Scheme

;;; engine.ss
;;; Copyright 1984-2017 Cisco Systems, Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.
;;; Notes:
;;; The engine code defines three functions: make-engine,
;;; engine-block, and engine-return.
;;; Keyboard interrupts are caught while an engine is running
;;; and the engine disabled while the handler is running.
;;; All of the engine code is defined within local state
;;; containing the following variables:
;;; *active* true iff an engine is running
;;; *exit* the continuation to the engine invoker
;;; *keybd* the saved keyboard interrupt handler
;;; *timer* the saved timer interrupt handler
(let ()
(define-threaded *exit*)
(define-threaded *keybd*)
(define-threaded *timer*)
(define-threaded *active* #f)
(define cleanup
(lambda (who)
(unless *active* ($oops who "no engine active"))
(set! *active* #f)
(keyboard-interrupt-handler *keybd*)
(timer-interrupt-handler *timer*)
(set! *keybd* (void))
(set! *exit* (void))
(set! *timer* (void))))
(define setup
(lambda (exit)
(set! *active* #t)
(set! *keybd* (keyboard-interrupt-handler))
(keyboard-interrupt-handler (exception *keybd*))
(set! *timer* (timer-interrupt-handler))
(timer-interrupt-handler block)
(set! *exit* exit)))
(define block
; disable engine and return the continuation
(lambda ()
(let ([exit *exit*])
(cleanup 'engine-block)
(set-timer (call/cc (lambda (k) (exit (lambda () k))))))))
(define return
; disable engine and return list (ticks value ...)
(lambda (args)
(let ([n (set-timer 0)])
(let ([exit *exit*])
(cleanup 'engine-return)
(exit (lambda () (cons n args)))))))
(define exception
; disable engine while calling the handler
(lambda (handler)
(lambda args
(let ([ticks (set-timer 0)])
(let ([exit *exit*])
(cleanup 'engine-exception)
(apply handler args)
(setup exit)
(if (= ticks 0) (block) (set-timer ticks)))))))
(define run-engine
; run a continuation as an engine
(lambda (k ticks)
((call/cc
(lambda (exit)
(set-timer 0)
(when *active* ($oops 'engine "cannot nest engines"))
(setup exit)
(k ticks))))))
(define eng
; create an engine from a procedure or continuation
(lambda (k)
(lambda (ticks complete expire)
(unless (and (fixnum? ticks) (not (negative? ticks)))
($oops 'engine "invalid ticks ~s" ticks))
(unless (procedure? complete)
($oops 'engine "~s is not a procedure" complete))
(unless (procedure? expire)
($oops 'engine "~s is not a procedure" expire))
(if (= ticks 0)
(expire (eng k))
(let ([x (run-engine k ticks)])
(if (procedure? x)
(expire (eng x))
(apply complete x)))))))
(set! engine-return (lambda args (return args)))
(set! engine-block (lambda () (set-timer 0) (block)))
(set! make-engine
(lambda (x)
(unless (procedure? x) ($oops 'make-engine "~s is not a procedure" x))
(eng (lambda (ticks)
(with-exception-handler
(lambda (c)
(let ([ticks (set-timer 0)])
(let ([exit *exit*])
(cleanup 'raise)
(call/cc
(lambda (k)
(exit
(lambda ()
(let-values ([vals (raise-continuable c)])
(setup exit)
(if (= ticks 0) (block) (set-timer ticks))
(apply k vals)))))))))
(lambda ()
(set-timer ticks)
(call-with-values x (lambda args (return args)))))))))
)