; Copyright (c) 1993-2001 by Richard Kelsey and Jonathan Rees. See file COPYING. ; This is file fluid.scm. ; Fluid (dynamic) variables. ; Fluid variables are implemented using deep binding. This allows ; each thread in a multiprocessor system to have its own fluid ; environment, and allows for fast thread switching in a multitasking ; one. ; CURRENT-THREAD and SET-CURRENT-THREAD! access a special virtual ; machine register. On a multiprocessor, each processor would have ; its own current-thread register. The run-time system stores the ; current thread in this register. ; Here we define a particular thread record, but a different one is ; defined by the (uniprocessor) threads package. The current thread ; may actually be any kind of record as long as its first component ; can be used by the fluid variable implementation to maintain the ; deep-binding dynamic environment and its second component can be ; used by DYNAMIC-WIND. This is kind of gross but it is motivated by ; efficiency concerns. (define-record-type thread :thread (make-thread dynamic-env dynamic-point cell-env) (dynamic-env thread-dynamic-env) (dynamic-point thread-dynamic-point) (cell-env thread-cell-env)) (define (get-dynamic-env) (record-ref (current-thread) 1)) (define (set-dynamic-env! env) (record-set! (current-thread) 1 env)) ; The dynamic-wind point used to be just an ordinary fluid variable, but that ; doesn't work well with threads. (define (get-dynamic-point) (record-ref (current-thread) 2)) (define (set-dynamic-point! point) (record-set! (current-thread) 2 point)) ;---------------- ; Dynamic environment ; A dynamic environment is an alist where the cars are fluid records. (define (with-dynamic-env env thunk) (let ((saved-env (get-dynamic-env))) (set-dynamic-env! env) (set! env #f) ;For GC and debugger (call-with-values ;; thunk (let ((x thunk)) (set! thunk #f) x) ;For GC (lambda results (set-dynamic-env! saved-env) (apply values results))))) (define (empty-dynamic-env) '()) ; Each fluid has a top-level value that is used when the fluid is unbound ; in the current dynamic environment. (define-record-type fluid :fluid (make-fluid top) (top fluid-top-level-value set-fluid-top-level-value!)) (define (fluid f) (let ((probe (assq f (get-dynamic-env)))) (if probe (cdr probe) (fluid-top-level-value f)))) ; Deprecated. (define (set-fluid! f val) (let ((probe (assq f (get-dynamic-env)))) (if probe (set-cdr! probe val) (set-fluid-top-level-value! f val)))) (define (let-fluid f val thunk) (with-dynamic-env (cons (cons f val) (get-dynamic-env)) thunk)) (define (let-fluids . args) (let loop ((args args) (env (get-dynamic-env))) (if (null? (cdr args)) (with-dynamic-env env (car args)) (loop (cddr args) (cons (cons (car args) (cadr args)) env))))) ; Handy utilities. (define (fluid-cell-ref f) (cell-ref (fluid f))) (define (fluid-cell-set! f value) (cell-set! (fluid f) value)) ; Thread cells (define-record-type thread-cell :thread-cell (make-thread-cell default) (default thread-cell-default)) (define (get-thread-cell-env) (record-ref (current-thread) 3)) (define (set-thread-cell-env! value) (record-set! (current-thread) 3 value)) (define (empty-thread-cell-env) '()) (define (thread-cell-ref thread-cell) (let ((probe (assq thread-cell (get-thread-cell-env)))) (if probe (cdr probe) (thread-cell-default thread-cell)))) (define (thread-cell-set! thread-cell value) (let ((probe (assq thread-cell (get-thread-cell-env)))) (if probe (set-cdr! probe value) (set-thread-cell-env! (cons (cons thread-cell value) (get-thread-cell-env)))))) ; Initialize (define (initialize-dynamic-state!) (set-current-thread! (make-thread (empty-dynamic-env) #f (empty-thread-cell-env)))) (initialize-dynamic-state!)