add #:callback-exns? to _fun and _cprocedure

In Racket BC, callbacks don't have to be atomic, and it's ok for the
callback to raise an exception (as long as the foreign library is ok
with a longjmp escape). Using `#:callback-exns? #t` on a foreign
callout in both CS and BC allows an atomic callback (invoked during
the foreign call) to raise an exception. Terms and conditions apply.
This commit is contained in:
Matthew Flatt 2021-02-17 08:31:31 -07:00
parent 5ed105ef8a
commit bf8741e727
9 changed files with 269 additions and 74 deletions

View File

@ -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]))

View File

@ -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?])

View File

@ -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);
}

View File

@ -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))

View File

@ -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)))])

View File

@ -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",

View File

@ -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);
}

View File

@ -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)))))

View File

@ -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