diff --git a/pkgs/base/info.rkt b/pkgs/base/info.rkt index 4eaf49157b..9dc6b5456b 100644 --- a/pkgs/base/info.rkt +++ b/pkgs/base/info.rkt @@ -14,7 +14,7 @@ ;; In the Racket source repo, this version should change only when ;; "racket_version.h" changes: -(define version "8.0.0.7") +(define version "8.0.0.8") (define deps `("racket-lib" ["racket" #:version ,version])) diff --git a/pkgs/racket-doc/scribblings/foreign/types.scrbl b/pkgs/racket-doc/scribblings/foreign/types.scrbl index ad239ee058..29547a904a 100644 --- a/pkgs/racket-doc/scribblings/foreign/types.scrbl +++ b/pkgs/racket-doc/scribblings/foreign/types.scrbl @@ -523,6 +523,7 @@ the later case, the result is the @racket[ctype]).} [#:lock-name lock-name (or/c string? #f) #f] [#:in-original-place? in-original-place? any/c #f] [#:blocking? blocking? any/c #f] + [#:callback-exns? callback-exns? any/c #f] [#:save-errno save-errno (or/c #f 'posix 'windows) #f] [#:wrapper wrapper (or/c #f (procedure? . -> . procedure?)) #f] @@ -624,6 +625,23 @@ For @tech{callouts} to foreign functions with the generated type: value of @racket[async-apply], even if they are always applied in the OS thread used to run Racket.} + @item{If @racket[callback-exns?] is true, then a foreign + @tech{callout} allows an atomic @tech{callback} during the + foreign call to raise an exception that escapes from the + foreign call. From the foreign library's perspective, the + exception escapes via @tt{longjmp}. Exception escapes are + implemented through an exception handler that catches and + reraises the exception. + + A callback that raises an exception must be an atomic callback + in the @BC[] implementation of Racket (and callbacks are always + atomic in the @CS[] implementation). Raising an exception is + not allowed in a callback that has an @racket[async-apply], + since the callback will run in an unspecified context. Raising + an exception is also not allowed if the callout (that led to + the callback) was created with @racket[in-original-place?] as + true and called in a non-original place.} + @item{Values that are provided to a @tech{callout} (i.e., the underlying callout, and not the replacement produced by a @racket[wrapper], if any) are always considered reachable by the @@ -716,10 +734,12 @@ For @tech{callbacks} to Racket functions with the generated type: synchronization with other threads, or else it may lead to deadlock. In addition, the Racket code must not perform any potentially blocking operation (such as I/O), it must not raise - an uncaught exception, it must not perform any escaping - continuation jumps, and its non-tail recursion must be minimal - to avoid C-level stack overflow; otherwise, the process may - crash or misbehave. + an uncaught exception unless called through a @tech{callout} + that supports exception (with @racket[#:callback-exns? #t]), it + must not perform any escaping continuation jumps, and (at + least for the @BC[] implementation) its + non-tail recursion must be minimal to avoid C-level stack + overflow; otherwise, the process may crash or misbehave. Callbacks are always atomic in the @CS[] implementation of Racket, because Racket threads do not capture C-stack context. Even on @@ -779,7 +799,8 @@ For @tech{callbacks} to Racket functions with the generated type: @history[#:changed "6.3" @elem{Added the @racket[#:lock-name] argument.} #:changed "6.12.0.2" @elem{Added the @racket[#:blocking?] argument.} - #:changed "7.9.0.16" @elem{Added the @racket[#:varargs-after] argument.}]} + #:changed "7.9.0.16" @elem{Added the @racket[#:varargs-after] argument.} + #:changed "8.0.0.8" @elem{Added the @racket[#:callback-exns?] argument.}]} @defform/subs[#:literals (->> :: :) (_fun fun-option ... maybe-args type-spec ... ->> type-spec @@ -793,6 +814,7 @@ For @tech{callbacks} to Racket functions with the generated type: (code:line #:lock-name lock-name-expr) (code:line #:in-original-place? in-original-place?-expr) (code:line #:blocking? blocking?-expr) + (code:line #:callback-exns? callback-exns?-expr) (code:line #:retry (retry-id [arg-id init-expr]))] [maybe-args code:blank (code:line formals ::)] @@ -821,8 +843,8 @@ and returns an integer. See @racket[_cprocedure] for information about the @racket[#:abi], @racket[#:varargs-after], @racket[#:save-errno], @racket[#:keep], @racket[#:atomic?], -@racket[#:async-apply], @racket[#:in-original-place?], and -@racket[#:blocking] options. +@racket[#:async-apply], @racket[#:in-original-place?], +@racket[#:blocking], and @racket[#:callback-exns?] options. In its full form, the @racket[_fun] syntax provides an IDL-like language that creates a wrapper function around the @@ -916,7 +938,8 @@ specifications: @history[#:changed "6.2" @elem{Added the @racket[#:retry] option.} #:changed "6.3" @elem{Added the @racket[#:lock-name] option.} #:changed "6.12.0.2" @elem{Added the @racket[#:blocking?] option.} - #:changed "7.9.0.16" @elem{Added the @racket[#:varargs-after] option.}]} + #:changed "7.9.0.16" @elem{Added the @racket[#:varargs-after] option.} + #:changed "8.0.0.8" @elem{Added the @racket[#:callback-exns?] option.}]} @defproc[(function-ptr [ptr-or-proc (or cpointer? procedure?)] [fun-type ctype?]) diff --git a/pkgs/racket-test-core/tests/racket/foreign-test.c b/pkgs/racket-test-core/tests/racket/foreign-test.c index 40af3f089e..9f71595bea 100644 --- a/pkgs/racket-test-core/tests/racket/foreign-test.c +++ b/pkgs/racket-test-core/tests/racket/foreign-test.c @@ -402,3 +402,8 @@ X long varargs_check(int init, int n, ...) { return accum; } + +X int callback_hungry(int (*f)(void*)) { + char use_stack_space[10000]; + return f(use_stack_space); +} diff --git a/pkgs/racket-test-core/tests/racket/foreign-test.rktl b/pkgs/racket-test-core/tests/racket/foreign-test.rktl index 367bf96ef9..567aadc928 100644 --- a/pkgs/racket-test-core/tests/racket/foreign-test.rktl +++ b/pkgs/racket-test-core/tests/racket/foreign-test.rktl @@ -908,6 +908,26 @@ (check (lambda (f) (f)) add1) (check (box 20) (lambda (x) 20))) +;; check `#:callback-exns?` +(let ([callback_hungry (get-ffi-obj 'callback_hungry test-lib + (_fun #:callback-exns? #t + (_fun #:atomic? #t -> _int) -> _int))]) + (test 17 callback_hungry (lambda () 17)) + (for ([i 1000]) + (with-handlers ([(lambda (x) (eq? x 'out)) void]) + (callback_hungry (lambda () (raise 'out)))) + (sync (system-idle-evt)))) +;; Same thing, with a lock +(let ([callback_hungry (get-ffi-obj 'callback_hungry test-lib + (_fun #:callback-exns? #t + #:lock-name "hungry" + (_fun #:atomic? #t -> _int) -> _int))]) + (test 170 callback_hungry (lambda () 170)) + (for ([i 1000]) + (with-handlers ([(lambda (x) (eq? x 'out)) void]) + (callback_hungry (lambda () (raise 'out)))) + (sync (system-idle-evt)))) + ;; check in-array (let () (define _t (_array _int 6)) diff --git a/racket/collects/ffi/unsafe.rkt b/racket/collects/ffi/unsafe.rkt index 530b01dec6..b0194458b4 100644 --- a/racket/collects/ffi/unsafe.rkt +++ b/racket/collects/ffi/unsafe.rkt @@ -509,10 +509,13 @@ #:atomic? [atomic? #f] #:in-original-place? [orig-place? #f] #:blocking? [blocking? #f] + #:callback-exns? [callback-exns? #f] #:lock-name [lock-name #f] #:async-apply [async-apply #f] #:save-errno [errno #f]) - (_cprocedure* itypes otype abi varargs-after wrapper keep atomic? orig-place? blocking? async-apply errno lock-name)) + (_cprocedure* itypes otype abi varargs-after wrapper keep + atomic? orig-place? blocking? callback-exns? + async-apply errno lock-name)) ;; A lightwegith delay meachnism for a single-argument function when ;; it's ok (but unlikely) to evaluate `expr` more than once and keep @@ -533,9 +536,12 @@ ;; for internal use (define held-callbacks (make-weak-hasheq)) -(define (_cprocedure* itypes otype abi varargs-after wrapper keep atomic? orig-place? blocking? async-apply errno lock-name) +(define (_cprocedure* itypes otype abi varargs-after wrapper keep + atomic? orig-place? blocking? callback-exns? + async-apply errno lock-name) (define make-ffi-callback (delay/cas (ffi-callback-maker itypes otype abi atomic? async-apply varargs-after))) - (define make-ffi-call (delay/cas (ffi-call-maker itypes otype abi errno orig-place? lock-name blocking? varargs-after))) + (define make-ffi-call (delay/cas (ffi-call-maker itypes otype abi errno + orig-place? lock-name blocking? varargs-after callback-exns?))) (define-syntax-rule (make-it wrap) (make-ctype _fpointer (lambda (x) @@ -572,7 +578,7 @@ (provide _fun) (define-for-syntax _fun-keywords `([#:abi ,#'#f] [#:varargs-after ,#'#f] [#:keep ,#'#t] [#:atomic? ,#'#f] - [#:in-original-place? ,#'#f] [#:blocking? ,#'#f] [#:lock-name ,#'#f] + [#:in-original-place? ,#'#f] [#:blocking? ,#'#f] [#:callback-exns? ,#'#f] [#:lock-name ,#'#f] [#:async-apply ,#'#f] [#:save-errno ,#'#f] [#:retry #f])) (define-syntax (_fun stx) @@ -737,6 +743,7 @@ #,(kwd-ref '#:atomic?) #,(kwd-ref '#:in-original-place?) #,(kwd-ref '#:blocking?) + #,(kwd-ref '#:callback-exns?) #,(kwd-ref '#:async-apply) #,(kwd-ref '#:save-errno) #,(kwd-ref '#:lock-name)))]) diff --git a/racket/src/bc/foreign/foreign.c b/racket/src/bc/foreign/foreign.c index 71fe1f03dd..2994d58029 100644 --- a/racket/src/bc/foreign/foreign.c +++ b/racket/src/bc/foreign/foreign.c @@ -3656,6 +3656,30 @@ static void finish_ffi_call(ffi_cif *cif, void *c_func, intptr_t cfoff, ffi_call(cif, (VoidFun)W_OFFSET(c_func, cfoff), p, avalues); } +static void finish_ffi_call_handle_exn(ffi_cif *cif, void *c_func, intptr_t cfoff, + int nargs, GC_CAN_IGNORE ForeignAny *ivals, void **avalues, + intptr_t *offsets, void *p, + Scheme_Object *lock) +{ + mz_jmp_buf * volatile save, fresh; + + save = scheme_current_thread->error_buf; + scheme_current_thread->error_buf = &fresh; + + if (scheme_setjmp(scheme_error_buf)) { + if (SCHEME_TRUEP(lock)) + release_ffi_lock(lock); + scheme_end_in_scheduler(); + scheme_longjmp(*save, 1); + } else { + finish_ffi_call(cif, c_func, cfoff, + nargs, ivals, avalues, + offsets, p); + } + + scheme_current_thread->error_buf = save; +} + static Scheme_Object *ffi_do_call(int argc, Scheme_Object *argv[], Scheme_Object *self) /* data := {name, c-function, itypes, otype, cif} */ { @@ -3674,8 +3698,9 @@ static Scheme_Object *ffi_do_call(int argc, Scheme_Object *argv[], Scheme_Object : SCHEME_INT_VAL(SCHEME_VEC_ELS(data)[5])); int save_errno = SCHEME_INT_VAL(SCHEME_VEC_ELS(data)[6]); Scheme_Object *lock = SCHEME_VEC_ELS(data)[7]; + int callback_exns = SCHEME_TRUEP(SCHEME_VEC_ELS(data)[8]); #ifdef MZ_USE_PLACES - int orig_place = SCHEME_TRUEP(SCHEME_VEC_ELS(data)[8]); + int orig_place = SCHEME_TRUEP(SCHEME_VEC_ELS(data)[9]); #endif int nargs /* = cif->nargs, after checking cif */; /* When the foreign function is called, we need an array (ivals) of nargs @@ -3762,9 +3787,16 @@ static Scheme_Object *ffi_do_call(int argc, Scheme_Object *argv[], Scheme_Object offsets, p); else #endif - finish_ffi_call(cif, c_func, cfoff, - nargs, ivals, avalues, - offsets, p); + { + if (callback_exns) + finish_ffi_call_handle_exn(cif, c_func, cfoff, + nargs, ivals, avalues, + offsets, p, lock); + else + finish_ffi_call(cif, c_func, cfoff, + nargs, ivals, avalues, + offsets, p); + } if (SCHEME_TRUEP(lock)) release_ffi_lock(lock); @@ -3879,14 +3911,14 @@ static Scheme_Object *ffi_call_or_curry(const char *who, int curry, int argc, Sc intptr_t ooff; GC_CAN_IGNORE ffi_type *rtype, **atypes; GC_CAN_IGNORE ffi_cif *cif; - int i, nargs, save_errno; + int i, nargs, save_errno, callback_exns; Scheme_Object *lock = scheme_false; Scheme_Performance_State perf_state; # ifdef MZ_USE_PLACES int orig_place = MZ_USE_FFIPOLL_COND; -# define FFI_CALL_VEC_SIZE 9 +# define FFI_CALL_VEC_SIZE 10 # else /* MZ_USE_PLACES undefined */ -# define FFI_CALL_VEC_SIZE 8 +# define FFI_CALL_VEC_SIZE 9 # endif /* MZ_USE_PLACES */ scheme_performance_record_start(&perf_state); if (!curry) { @@ -3941,6 +3973,10 @@ static Scheme_Object *ffi_call_or_curry(const char *who, int curry, int argc, Sc varargs_after = extract_varargs_after(who, argc, argv, ARGPOS(8), nargs); } else varargs_after = -1; + if (argc > ARGPOS(9)) + callback_exns = SCHEME_TRUEP(argv[ARGPOS(9)]); + else + callback_exns = 0; if (cp && SCHEME_FFIOBJP(cp)) name = scheme_make_byte_string(((ffi_obj_struct*)(cp))->name); @@ -3971,8 +4007,9 @@ static Scheme_Object *ffi_call_or_curry(const char *who, int curry, int argc, Sc SCHEME_VEC_ELS(data)[5] = scheme_make_integer(ooff); SCHEME_VEC_ELS(data)[6] = scheme_make_integer(save_errno); SCHEME_VEC_ELS(data)[7] = lock; + SCHEME_VEC_ELS(data)[8] = (callback_exns ? scheme_true : scheme_false); # ifdef MZ_USE_PLACES - SCHEME_VEC_ELS(data)[8] = (orig_place ? scheme_true : scheme_false); + SCHEME_VEC_ELS(data)[9] = (orig_place ? scheme_true : scheme_false); # endif /* MZ_USE_PLACES */ scheme_register_finalizer(data, free_fficall_data, cif, NULL, NULL); a[0] = data; @@ -3993,7 +4030,7 @@ static Scheme_Object *ffi_call_or_curry(const char *who, int curry, int argc, Sc #undef ARGPOS } -/* (ffi-call ffi-obj in-types out-type [abi varargs-after save-errno? orig-place? lock-name blocking?]) -> (in-types -> out-value) */ +/* (ffi-call ffi-obj in-types out-type [abi save-errno? orig-place? lock-name blocking? varargs-after exns?]) -> (in-types -> out-value) */ /* the real work is done by ffi_do_call above */ #define MYNAME "ffi-call" static Scheme_Object *foreign_ffi_call(int argc, Scheme_Object *argv[]) @@ -4002,7 +4039,7 @@ static Scheme_Object *foreign_ffi_call(int argc, Scheme_Object *argv[]) } #undef MYNAME -/* (ffi-call-maker in-types out-type [abi varargs-after save-errno? orig-place? lock-name blocking?]) -> (ffi->obj -> (in-types -> out-value)) */ +/* (ffi-call-maker in-types out-type [abi save-errno? orig-place? lock-name blocking? varargs-after exns?]) -> (ffi->obj -> (in-types -> out-value)) */ /* Curried version of `ffi-call` */ #define MYNAME "ffi-call-maker" static Scheme_Object *foreign_ffi_call_maker(int argc, Scheme_Object *argv[]) @@ -5172,9 +5209,9 @@ void scheme_init_foreign(Scheme_Startup_Env *env) scheme_addto_prim_instance("make-sized-byte-string", scheme_make_noncm_prim(foreign_make_sized_byte_string, "make-sized-byte-string", 2, 2), env); scheme_addto_prim_instance("ffi-call", - scheme_make_noncm_prim(foreign_ffi_call, "ffi-call", 3, 9), env); + scheme_make_noncm_prim(foreign_ffi_call, "ffi-call", 3, 10), env); scheme_addto_prim_instance("ffi-call-maker", - scheme_make_noncm_prim(foreign_ffi_call_maker, "ffi-call-maker", 2, 8), env); + scheme_make_noncm_prim(foreign_ffi_call_maker, "ffi-call-maker", 2, 9), env); scheme_addto_prim_instance("ffi-callback", scheme_make_noncm_prim(foreign_ffi_callback, "ffi-callback", 3, 6), env); scheme_addto_prim_instance("ffi-callback-maker", @@ -5539,9 +5576,9 @@ void scheme_init_foreign(Scheme_Env *env) scheme_addto_primitive_instance("make-sized-byte-string", scheme_make_noncm_prim((Scheme_Prim *)unimplemented, "make-sized-byte-string", 2, 2), env); scheme_addto_primitive_instance("ffi-call", - scheme_make_noncm_prim((Scheme_Prim *)unimplemented, "ffi-call", 3, 9), env); + scheme_make_noncm_prim((Scheme_Prim *)unimplemented, "ffi-call", 3, 10), env); scheme_addto_primitive_instance("ffi-call-maker", - scheme_make_noncm_prim((Scheme_Prim *)unimplemented, "ffi-call-maker", 2, 8), env); + scheme_make_noncm_prim((Scheme_Prim *)unimplemented, "ffi-call-maker", 2, 9), env); scheme_addto_primitive_instance("ffi-callback", scheme_make_noncm_prim((Scheme_Prim *)unimplemented, "ffi-callback", 3, 6), env); scheme_addto_primitive_instance("ffi-callback-maker", diff --git a/racket/src/bc/foreign/foreign.rktc b/racket/src/bc/foreign/foreign.rktc index 3717500f6f..4abc57c126 100755 --- a/racket/src/bc/foreign/foreign.rktc +++ b/racket/src/bc/foreign/foreign.rktc @@ -2823,6 +2823,30 @@ static void finish_ffi_call(ffi_cif *cif, void *c_func, intptr_t cfoff, ffi_call(cif, (VoidFun)W_OFFSET(c_func, cfoff), p, avalues); } +static void finish_ffi_call_handle_exn(ffi_cif *cif, void *c_func, intptr_t cfoff, + int nargs, GC_CAN_IGNORE ForeignAny *ivals, void **avalues, + intptr_t *offsets, void *p, + Scheme_Object *lock) +{ + mz_jmp_buf * volatile save, fresh; + + save = scheme_current_thread->error_buf; + scheme_current_thread->error_buf = &fresh; + + if (scheme_setjmp(scheme_error_buf)) { + if (SCHEME_TRUEP(lock)) + release_ffi_lock(lock); + scheme_end_in_scheduler(); + scheme_longjmp(*save, 1); + } else { + finish_ffi_call(cif, c_func, cfoff, + nargs, ivals, avalues, + offsets, p); + } + + scheme_current_thread->error_buf = save; +} + static Scheme_Object *ffi_do_call(int argc, Scheme_Object *argv[], Scheme_Object *self) /* data := {name, c-function, itypes, otype, cif} */ { @@ -2841,8 +2865,9 @@ static Scheme_Object *ffi_do_call(int argc, Scheme_Object *argv[], Scheme_Object : SCHEME_INT_VAL(SCHEME_VEC_ELS(data)[5])); int save_errno = SCHEME_INT_VAL(SCHEME_VEC_ELS(data)[6]); Scheme_Object *lock = SCHEME_VEC_ELS(data)[7]; + int callback_exns = SCHEME_TRUEP(SCHEME_VEC_ELS(data)[8]); #ifdef MZ_USE_PLACES - int orig_place = SCHEME_TRUEP(SCHEME_VEC_ELS(data)[8]); + int orig_place = SCHEME_TRUEP(SCHEME_VEC_ELS(data)[9]); #endif int nargs /* = cif->nargs, after checking cif */; /* When the foreign function is called, we need an array (ivals) of nargs @@ -2929,10 +2954,17 @@ static Scheme_Object *ffi_do_call(int argc, Scheme_Object *argv[], Scheme_Object offsets, p); else #endif - finish_ffi_call(cif, c_func, cfoff, - nargs, ivals, avalues, - offsets, p); - + { + if (callback_exns) + finish_ffi_call_handle_exn(cif, c_func, cfoff, + nargs, ivals, avalues, + offsets, p, lock); + else + finish_ffi_call(cif, c_func, cfoff, + nargs, ivals, avalues, + offsets, p); + } + if (SCHEME_TRUEP(lock)) release_ffi_lock(lock); @@ -3046,14 +3078,14 @@ static Scheme_Object *ffi_call_or_curry(const char *who, int curry, int argc, Sc intptr_t ooff; GC_CAN_IGNORE ffi_type *rtype, **atypes; GC_CAN_IGNORE ffi_cif *cif; - int i, nargs, save_errno; + int i, nargs, save_errno, callback_exns; Scheme_Object *lock = scheme_false; Scheme_Performance_State perf_state; @@@IFDEF{MZ_USE_PLACES}{ int orig_place = MZ_USE_FFIPOLL_COND; - @DEFINE{FFI_CALL_VEC_SIZE 9} + @DEFINE{FFI_CALL_VEC_SIZE 10} }{ - @DEFINE{FFI_CALL_VEC_SIZE 8} + @DEFINE{FFI_CALL_VEC_SIZE 9} } scheme_performance_record_start(&perf_state); if (!curry) { @@ -3108,6 +3140,10 @@ static Scheme_Object *ffi_call_or_curry(const char *who, int curry, int argc, Sc varargs_after = extract_varargs_after(who, argc, argv, ARGPOS(8), nargs); } else varargs_after = -1; + if (argc > ARGPOS(9)) + callback_exns = SCHEME_TRUEP(argv[ARGPOS(9)]); + else + callback_exns = 0; if (cp && SCHEME_FFIOBJP(cp)) name = scheme_make_byte_string(((ffi_obj_struct*)(cp))->name); @@ -3138,8 +3174,9 @@ static Scheme_Object *ffi_call_or_curry(const char *who, int curry, int argc, Sc SCHEME_VEC_ELS(data)[5] = scheme_make_integer(ooff); SCHEME_VEC_ELS(data)[6] = scheme_make_integer(save_errno); SCHEME_VEC_ELS(data)[7] = lock; + SCHEME_VEC_ELS(data)[8] = (callback_exns ? scheme_true : scheme_false); @@IFDEF{MZ_USE_PLACES}{ - SCHEME_VEC_ELS(data)[8] = (orig_place ? scheme_true : scheme_false); + SCHEME_VEC_ELS(data)[9] = (orig_place ? scheme_true : scheme_false); } scheme_register_finalizer(data, free_fficall_data, cif, NULL, NULL); a[0] = data; @@ -3160,15 +3197,15 @@ static Scheme_Object *ffi_call_or_curry(const char *who, int curry, int argc, Sc #undef ARGPOS } -/* (ffi-call ffi-obj in-types out-type [abi varargs-after save-errno? orig-place? lock-name blocking?]) -> (in-types -> out-value) */ +/* (ffi-call ffi-obj in-types out-type [abi save-errno? orig-place? lock-name blocking? varargs-after exns?]) -> (in-types -> out-value) */ /* the real work is done by ffi_do_call above */ -@cdefine[ffi-call 3 9]{ +@cdefine[ffi-call 3 10]{ return ffi_call_or_curry(MYNAME, 0, argc, argv); } -/* (ffi-call-maker in-types out-type [abi varargs-after save-errno? orig-place? lock-name blocking?]) -> (ffi->obj -> (in-types -> out-value)) */ +/* (ffi-call-maker in-types out-type [abi save-errno? orig-place? lock-name blocking? varargs-after exns?]) -> (ffi->obj -> (in-types -> out-value)) */ /* Curried version of `ffi-call` */ -@cdefine[ffi-call-maker 2 8]{ +@cdefine[ffi-call-maker 2 9]{ return ffi_call_or_curry(MYNAME, 1, argc, argv); } diff --git a/racket/src/cs/rumble/foreign.ss b/racket/src/cs/rumble/foreign.ss index e7f492a644..98b93e23cc 100644 --- a/racket/src/cs/rumble/foreign.ss +++ b/racket/src/cs/rumble/foreign.ss @@ -1532,39 +1532,48 @@ (define/who ffi-call (case-lambda [(p in-types out-type) - (ffi-call p in-types out-type #f #f #f)] + (ffi-call p in-types out-type #f #f #f #f)] [(p in-types out-type abi) - (ffi-call p in-types out-type abi #f #f #f)] + (ffi-call p in-types out-type abi #f #f #f #f)] [(p in-types out-type abi save-errno) - (ffi-call p in-types out-type abi save-errno #f #f)] + (ffi-call p in-types out-type abi save-errno #f #f #f)] [(p in-types out-type abi save-errno orig-place?) - (ffi-call p in-types out-type abi save-errno orig-place? #f #f)] + (ffi-call p in-types out-type abi save-errno orig-place? #f #f #f)] [(p in-types out-type abi save-errno orig-place? lock-name) - (ffi-call p in-types out-type abi save-errno orig-place? lock-name #f #f)] + (ffi-call p in-types out-type abi save-errno orig-place? lock-name #f #f #f)] [(p in-types out-type abi save-errno orig-place? lock-name blocking?) - (ffi-call p in-types out-type abi save-errno orig-place? lock-name blocking? #f)] + (ffi-call p in-types out-type abi save-errno orig-place? lock-name blocking? #f #f)] [(p in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after) + (ffi-call p in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after #f)] + [(p in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after exns?) (check who cpointer? p) (check-ffi-call who in-types out-type abi varargs-after save-errno lock-name) - ((ffi-call/callable #t in-types out-type abi varargs-after save-errno lock-name blocking? orig-place? #f #f) p)])) + ((ffi-call/callable #t in-types out-type abi varargs-after + save-errno lock-name (and blocking? #t) (and orig-place? #t) #f (and exns? #t) + #f) + p)])) (define/who ffi-call-maker (case-lambda [(in-types out-type) - (ffi-call-maker in-types out-type #f #f #f #f)] + (ffi-call-maker in-types out-type #f #f #f #f #f)] [(in-types out-type abi) - (ffi-call-maker in-types out-type abi #f #f #f)] + (ffi-call-maker in-types out-type abi #f #f #f #f)] [(in-types out-type abi save-errno) - (ffi-call-maker in-types out-type abi save-errno #f #f)] + (ffi-call-maker in-types out-type abi save-errno #f #f #f)] [(in-types out-type abi save-errno orig-place?) - (ffi-call-maker in-types out-type abi save-errno orig-place? #f #f)] + (ffi-call-maker in-types out-type abi save-errno orig-place? #f #f #f)] [(in-types out-type abi save-errno orig-place? lock-name) - (ffi-call-maker in-types out-type abi save-errno orig-place? lock-name #f #f)] + (ffi-call-maker in-types out-type abi save-errno orig-place? lock-name #f #f #f)] [(in-types out-type abi save-errno orig-place? lock-name blocking?) - (ffi-call-maker in-types out-type abi save-errno orig-place? lock-name blocking? #f)] + (ffi-call-maker in-types out-type abi save-errno orig-place? lock-name blocking? #f #f)] [(in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after) + (ffi-call-maker in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after #f)] + [(in-types out-type abi save-errno orig-place? lock-name blocking? varargs-after exns?) (check-ffi-call who in-types out-type abi varargs-after save-errno lock-name) - (ffi-call/callable #t in-types out-type abi varargs-after save-errno lock-name blocking? orig-place? #f #f)])) + (ffi-call/callable #t in-types out-type abi varargs-after + save-errno lock-name (and blocking? #t) (and orig-place? #t) #f (and exns? #t) + #f)])) (define (check-ffi-call who in-types out-type abi varargs-after save-errno lock-name) (check-ffi who in-types out-type abi varargs-after) @@ -1586,7 +1595,9 @@ (define call-locks (make-eq-hashtable)) -(define (ffi-call/callable call? in-types out-type abi varargs-after save-errno lock-name blocking? orig-place? atomic? async-apply) +(define (ffi-call/callable call? in-types out-type abi varargs-after + save-errno lock-name blocking? orig-place? atomic? exns? + async-apply) (let* ([conv* (let ([conv* (case abi [(stdcall) '(__stdcall)] [(sysv) '(__cdecl)] @@ -1686,6 +1697,7 @@ [(and (not ret-id) (not blocking?) (not orig-place?) + (not exns?) (not save-errno) (#%andmap (lambda (in-type) (case (ctype-host-rep in-type) @@ -1787,22 +1799,31 @@ (when blocking? (currently-blocking? #t)) (retain orig-args - (let ([r (#%apply (gen-proc (cpointer-address proc-p)) - (append - (if ret-ptr - (begin - (lock-cpointer ret-ptr) - (list (ret-maker (cpointer-address ret-ptr)))) - '()) - (map (lambda (arg in-type maker) - (let ([host-rep (array-rep-to-pointer-rep - (ctype-host-rep in-type))]) - (case host-rep - [(void*) (cpointer-address arg)] - [(struct union) - (maker (cpointer-address arg))] - [else arg]))) - args in-types arg-makers)))]) + (let ([r (let ([args (append + (if ret-ptr + (begin + (lock-cpointer ret-ptr) + (list (ret-maker (cpointer-address ret-ptr)))) + '()) + (map (lambda (arg in-type maker) + (let ([host-rep (array-rep-to-pointer-rep + (ctype-host-rep in-type))]) + (case host-rep + [(void*) (cpointer-address arg)] + [(struct union) + (maker (cpointer-address arg))] + [else arg]))) + args in-types arg-makers))] + [proc (gen-proc (cpointer-address proc-p))]) + (cond + [(not exns?) + (#%apply proc args)] + [else + (call-guarding-foreign-escape + (lambda () (#%apply proc args)) + (lambda () + (when lock (mutex-release lock)) + (when blocking? (currently-blocking? #f))))]))]) (when lock (mutex-release lock)) (when blocking? (currently-blocking? #f)) (case save-errno @@ -1843,7 +1864,7 @@ [arg (c->s type (case (ctype-host-rep type) [(struct union) - ;; Like old Racket, refer to argument on stack: + ;; Like Racket BC, refer to argument on stack: (make-cpointer (ftype-pointer-address arg) #f) #; (let* ([size (compound-ctype-size type)] @@ -1918,6 +1939,9 @@ (scheduler-start-atomic) ;; Now that the schedule is in atomic mode, reenable interrupts (for GC) (enable-interrupts) + ;; See also `call-guarding-foreign-escape`, which will need to take + ;; appropriate steps if `(thunk)` escapes, which currently means ending + ;; the scheduler's atomic mode (let ([v (thunk)]) (disable-interrupts) (scheduler-end-atomic) @@ -1949,6 +1973,46 @@ ;; ---------------------------------------- +;; Call `thunk` to enter a foreign call while wrapping it with a way +;; to escape with an exception from a foreign callback during the +;; call: +(define (call-guarding-foreign-escape thunk clean-up) + ((call-with-c-return + (lambda () + (call-with-current-continuation + (lambda (esc) + (call-with-exception-handler + (lambda (x) + ;; Deliver an exception re-raise after returning back + ;; from `call-with-c-return`: + (|#%app| esc (lambda () + (scheduler-end-atomic) ; error in callback means during atomic mode + (clean-up) + (raise x)))) + (lambda () + (call-with-values thunk + ;; Deliver successful values after returning back from + ;; `call-with-c-return`: + (case-lambda + [(v) (lambda () v)] + [args (lambda () (#%apply values args))])))))))))) + +;; `call-with-c-return` looks like a foreign function, due to a "cast" +;; to and from a callback, so returning from `call-with-c-return` will +;; pop and C frame stacks (via longjmp internally) that were pushed +;; since `call-with-c-return` was called. +(define call-with-c-return + (let ([call (lambda (thunk) (thunk))]) + (define-ftype ptr->ptr (function (ptr) ptr)) + (let ([fptr (make-ftype-pointer ptr->ptr call)]) + (let ([v (ftype-ref ptr->ptr () fptr)]) + (unlock-object + (foreign-callable-code-object + (ftype-pointer-address fptr))) + v)))) + +;; ---------------------------------------- + (define-record-type (callback create-callback ffi-callback?) (fields code)) @@ -1982,7 +2046,9 @@ (ffi-callback-maker* in-types out-type abi varargs-after atomic? async-apply)])) (define (ffi-callback-maker* in-types out-type abi varargs-after atomic? async-apply) - (let ([make-code (ffi-call/callable #f in-types out-type abi varargs-after #f #f #f #f (and atomic? #t) async-apply)]) + (let ([make-code (ffi-call/callable #f in-types out-type abi varargs-after + #f #f #f #f (and atomic? #t) #f + async-apply)]) (lambda (proc) (check 'make-ffi-callback procedure? proc) (create-callback (make-code proc))))) diff --git a/racket/src/version/racket_version.h b/racket/src/version/racket_version.h index fa9873102c..92384cd2b0 100644 --- a/racket/src/version/racket_version.h +++ b/racket/src/version/racket_version.h @@ -16,7 +16,7 @@ #define MZSCHEME_VERSION_X 8 #define MZSCHEME_VERSION_Y 0 #define MZSCHEME_VERSION_Z 0 -#define MZSCHEME_VERSION_W 7 +#define MZSCHEME_VERSION_W 8 /* A level of indirection makes `#` work as needed: */ #define AS_a_STR_HELPER(x) #x