1+ #lang racket
2+ ;; based on racket/draw/unsafe/callback
3+ (provide guard-foreign-escape)
4+ (require ffi/unsafe
5+ ffi/unsafe/atomic)
6+
7+ (define callback-atomic? (eq? 'chez-scheme (system-type 'vm )))
8+
9+ (define-syntax-rule (guard-foreign-escape e0 e ... )
10+ (call-guarding-foreign-escape (lambda () e0 e ... )))
11+
12+ (define (call-guarding-foreign-escape thunk )
13+ (if callback-atomic?
14+ ((call-with-c-return
15+ (lambda ()
16+ (with-handlers ([(lambda (x) #t )
17+ (lambda (x)
18+ ;; Deliver an exception re-raise after returning back
19+ ;; from `call-with-c-return`:
20+ (lambda ()
21+ (when (in-atomic-mode?)
22+ (end-atomic)) ; error happened during atomic mode
23+ ;(enable-interrupts) ; ... with interrupts disabled
24+ (void/reference-sink call-with-c-return-box)
25+ (raise x)))])
26+ (let ([vs (call-with-values thunk list)])
27+ ;; Deliver successful values after returning back from
28+ ;; `call-with-c-return`:
29+ (lambda ()
30+ (void/reference-sink call-with-c-return-box)
31+ (apply values vs)))))))
32+ (thunk )))
33+
34+ (define call-with-c-return-box (box #f ))
35+
36+ ;; `call-with-c-return` looks like a foreign function, due to a cast
37+ ;; to and from a callback, so returning from `call-with-c-return` will
38+ ;; pop and C frame stacks (via longjmp internally) that were pushed
39+ ;; since `call-with-c-return` was called.
40+ (define call-with-c-return
41+ (and callback-atomic?
42+ (cast (lambda (thunk ) (thunk ))
43+ (_fun #:atomic? #t
44+ #:keep call-with-c-return-box
45+ _racket -> _racket)
46+ (_fun _racket -> _racket))))
0 commit comments