add ffi/unsafe/collect-callback
The `ffi/unsafe/collect-callback` library exposes functionality formerly only available via Racket's C interface, but implement it for both Racket and RacketCs.
This commit is contained in:
parent
f8297f9c00
commit
d9ec0705cf
|
@ -12,7 +12,7 @@
|
|||
|
||||
(define collection 'multi)
|
||||
|
||||
(define version "7.0.0.8")
|
||||
(define version "7.0.0.9")
|
||||
|
||||
(define deps `("racket-lib"
|
||||
["racket" #:version ,version]))
|
||||
|
|
94
pkgs/racket-doc/scribblings/foreign/collect-callback.scrbl
Normal file
94
pkgs/racket-doc/scribblings/foreign/collect-callback.scrbl
Normal file
|
@ -0,0 +1,94 @@
|
|||
#lang scribble/doc
|
||||
@(require "utils.rkt"
|
||||
(for-label ffi/unsafe/collect-callback))
|
||||
|
||||
@title{Garbage Collection Callbacks}
|
||||
|
||||
@defmodule[ffi/unsafe/collect-callback]{The
|
||||
@racketmodname[ffi/unsafe/collect-callback] library provides functions
|
||||
to register constrained callbacks that are run just before and after a
|
||||
garbage collection.}
|
||||
|
||||
@history[#:added "7.0.0.9"]
|
||||
|
||||
|
||||
@defproc[(unsafe-add-collect-callbacks [pre (vectorof vector?)]
|
||||
[post (vectorof vector?)])
|
||||
any/c]{
|
||||
|
||||
Registers descriptions of foreign functions to be called just before
|
||||
and just after a garbage collection. The foreign functions must not
|
||||
allocate garbage-collected memory, and they are called in a way that
|
||||
does not allocate, which is why @var{pre_desc} and @var{post_desc} are
|
||||
function descriptions instead of thunks.
|
||||
|
||||
A description is a vector of vectors, where each of the inner vectors
|
||||
describes a single call, and the calls are performed in sequence. Each
|
||||
call vector starts with a symbol that indicates the protocol of the
|
||||
foreign function to be called. The following protocols are supported:
|
||||
@margin-note*{The apparently arbitrary and whimsical set of supported
|
||||
protocols is enough to allow DrRacket to show a garbage-collection
|
||||
icon.}
|
||||
|
||||
@itemlist[
|
||||
|
||||
@item{@racket['int->void] corresponds to @cpp{void (*)(int)}.}
|
||||
|
||||
@item{@racket['ptr_ptr_ptr->void] corresponds to @cpp{void
|
||||
(*)(void*, void*, void*)}.}
|
||||
|
||||
@item{@racket['ptr_ptr->save] corresponds to @cpp{void* (*)(void*,
|
||||
void*, void*)}, but the result is recored as the current ``save''
|
||||
value. The current ``save'' value starts as @cpp{NULL}.}
|
||||
|
||||
@item{@racket['save!_ptr->void] corresponds to @cpp{void (*)(void*,
|
||||
void*)}, but only if the current ``save'' value is not a @cpp{NULL}
|
||||
pointer, and passing that pointer as the function's first argument
|
||||
(so that only one additional argument is us from the description
|
||||
vector).}
|
||||
|
||||
@item{@racket['ptr_ptr_ptr_int->void] corresponds to @cpp{void
|
||||
(*)(void*, void*, void*, int)}.}
|
||||
|
||||
@item{@racket['ptr_ptr_float->void] corresponds to @cpp{void
|
||||
(*)(void*, void*, float)}.}
|
||||
|
||||
@item{@racket['ptr_ptr_double->void] corresponds to @cpp{void
|
||||
(*)(void*, void*, double)}.}
|
||||
|
||||
@item{@racket['ptr_ptr_ptr_int_int_int_int_int_int_int_int_int->void]
|
||||
corresponds to @cpp{void (*)(void*, void*, void*, int, int, int, int,
|
||||
int, int, int, int, int)}.}
|
||||
|
||||
@item{@racket['osapi_ptr_int->void] corresponds to @cpp{void
|
||||
(*)(void*, int)}, but using the stdcall calling convention
|
||||
on Windows.}
|
||||
|
||||
@item{@racket['osapi_ptr_ptr->void] corresponds to @cpp{void
|
||||
(*)(void*, void*)}, but using the stdcall calling convention
|
||||
on Windows.}
|
||||
|
||||
@item{@racket['osapi_ptr_int_int_int_int_ptr_int_int_long->void]
|
||||
corresponds to @cpp{void (*)(void*, int, int, int, int, void*,
|
||||
int, int, long)}, but using the stdcall calling convention
|
||||
on Windows.}
|
||||
|
||||
]
|
||||
|
||||
After the protocol symbol, the vector should contain a pointer to a
|
||||
foreign function and then an element for each of the function's
|
||||
arguments. Pointer values are represented as for the @racket[_pointer]
|
||||
representation defined by @racketmodname[ffi/unsafe].
|
||||
|
||||
The result is a key for use with @cpp{scheme_remove_gc_callback}. If
|
||||
the key becomes inaccessible, then the callback will be removed
|
||||
automatically (but beware that the pre-callback will have executed and
|
||||
the post-callback will not have executed)
|
||||
|
||||
}
|
||||
|
||||
@defproc[(unsafe-remove-collect-callbacks [key any/c]) void?]{
|
||||
|
||||
Unregisters pre- and post-collection callbacks that were previously
|
||||
registered by a call to @racket[unsafe-add-collect-callbacks] that
|
||||
returned @racket[v].}
|
|
@ -18,6 +18,7 @@
|
|||
@include-section["port.scrbl"]
|
||||
@include-section["global.scrbl"]
|
||||
@include-section["os-thread.scrbl"]
|
||||
@include-section["collect-callback.scrbl"]
|
||||
@include-section["objc.scrbl"]
|
||||
@include-section["ns.scrbl"]
|
||||
@include-section["com.scrbl"]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#lang scribble/doc
|
||||
@(require "utils.rkt" (for-label ffi/unsafe))
|
||||
@(require "utils.rkt" (for-label ffi/unsafe
|
||||
ffi/unsafe/collect-callback))
|
||||
|
||||
@title[#:tag "im:memoryalloc"]{Memory Allocation}
|
||||
|
||||
|
@ -1176,60 +1177,11 @@ array @var{shape}, so the array need not be retained.
|
|||
@function[(Scheme_Object* scheme_add_gc_callback [Scheme_Object* pre_desc]
|
||||
[Scheme_Object* post_desc])]{
|
||||
|
||||
Registers descriptions of foreign functions to be called just before
|
||||
and just after a garbage collection. The foreign functions must not
|
||||
allocate garbage-collected memory, and they are called in a way that
|
||||
does not allocate, which is why @var{pre_desc} and @var{post_desc} are
|
||||
function descriptions instead of thunks.
|
||||
|
||||
A description is a vector of vectors, where each of the inner vectors
|
||||
describes a single call, and the calls are performed in sequence. Each
|
||||
call vector starts with a symbol that indicates the protocol of the
|
||||
foreign function to be called. The following protocols are supported:
|
||||
|
||||
@itemlist[
|
||||
|
||||
@item{@racket['ptr_ptr_ptr->void] corresponds to @cpp{void
|
||||
(*)(void*, void*, void*)}.}
|
||||
|
||||
@item{@racket['ptr_ptr_ptr_int->void] corresponds to @cpp{void
|
||||
(*)(void*, void*, void*, int)}.}
|
||||
|
||||
@item{@racket['ptr_ptr_float->void] corresponds to @cpp{void
|
||||
(*)(void*, void*, float)}.}
|
||||
|
||||
@item{@racket['ptr_ptr_double->void] corresponds to @cpp{void
|
||||
(*)(void*, void*, double)}.}
|
||||
|
||||
@item{@racket['ptr_ptr_ptr_int_int_int_int_int_int_int_int_int->void]
|
||||
corresponds to @cpp{void (*)(void*, void*, void*, int, int, int, int,
|
||||
int, int, int, int, int)}.}
|
||||
|
||||
@item{@racket['osapi_ptr_int->void] corresponds to @cpp{void
|
||||
(*)(void*, int)}, but using the stdcall calling convention
|
||||
on Windows.}
|
||||
|
||||
@item{@racket['osapi_ptr_ptr->void] corresponds to @cpp{void
|
||||
(*)(void*, void*)}, but using the stdcall calling convention
|
||||
on Windows.}
|
||||
|
||||
@item{@racket['osapi_ptr_int_int_int_int_ptr_int_int_long->void]
|
||||
corresponds to @cpp{void (*)(void*, int, int, int, int, void*,
|
||||
int, int, long)}, but using the stdcall calling convention
|
||||
on Windows.}
|
||||
|
||||
]
|
||||
|
||||
After the protocol symbol, the vector should contain a pointer to a
|
||||
foreign function and then an element for each of the function's
|
||||
arguments. Pointer values are represented as for the @racket[_pointer]
|
||||
representation defined by @racketmodname[ffi/unsafe].
|
||||
|
||||
The result is a key for use with @cpp{scheme_remove_gc_callback}. If
|
||||
the key becomes inaccessible, then the callback will be removed
|
||||
automatically (but beware that the pre-callback will have executed and
|
||||
the post-callback will not have executed).}
|
||||
The same as @racket[unsafe-add-collect-callbacks] from
|
||||
@racketmodname[ffi/unsafe/collect-callback].}
|
||||
|
||||
@function[(void scheme_remove_gc_callback [Scheme_Object* key])]{
|
||||
|
||||
Removes a garbage-collection callback installed with @cpp{scheme_add_gc_callback}.}
|
||||
The same as @racket[unsafe-remove-collect-callbacks], removes
|
||||
garbage-collection callbacks installed with
|
||||
@cpp{scheme_add_gc_callback}.}
|
||||
|
|
5
racket/collects/ffi/unsafe/collect-callback.rkt
Normal file
5
racket/collects/ffi/unsafe/collect-callback.rkt
Normal file
|
@ -0,0 +1,5 @@
|
|||
#lang racket/base
|
||||
(require '#%unsafe)
|
||||
|
||||
(provide unsafe-add-collect-callbacks
|
||||
unsafe-remove-collect-callbacks)
|
|
@ -41,7 +41,9 @@
|
|||
unsafe-call-in-os-thread
|
||||
unsafe-make-os-semaphore
|
||||
unsafe-os-semaphore-post
|
||||
unsafe-os-semaphore-wait)
|
||||
unsafe-os-semaphore-wait
|
||||
unsafe-add-collect-callbacks
|
||||
unsafe-remove-collect-callbacks)
|
||||
(rename-out [new:unsafe-impersonate-procedure unsafe-impersonate-procedure]
|
||||
[new:unsafe-chaperone-procedure unsafe-chaperone-procedure])
|
||||
(prefix-out unsafe-
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
[prop:chaperone-unsafe-undefined (known-constant)]
|
||||
[unsafe-abort-current-continuation/no-wind (known-procedure 4)]
|
||||
[unsafe-add-post-custodian-shutdown (known-procedure 2)]
|
||||
[unsafe-add-collect-callbacks (known-procedure 4)]
|
||||
[unsafe-box*-cas! (known-procedure 8)]
|
||||
[unsafe-bytes-length (known-procedure/succeeds 2)]
|
||||
[unsafe-bytes-ref (known-procedure 4)]
|
||||
|
@ -123,6 +124,7 @@
|
|||
[unsafe-port->file-descriptor (known-procedure 2)]
|
||||
[unsafe-port->socket (known-procedure 2)]
|
||||
[unsafe-register-process-global (known-procedure 4)]
|
||||
[unsafe-remove-collect-callbacks (known-procedure 2)]
|
||||
[unsafe-s16vector-ref (known-procedure 4)]
|
||||
[unsafe-s16vector-set! (known-procedure 8)]
|
||||
[unsafe-set-box! (known-procedure 4)]
|
||||
|
|
|
@ -453,6 +453,8 @@
|
|||
make-phantom-bytes
|
||||
set-phantom-bytes!
|
||||
set-garbage-collect-notify! ; not exported to Racket
|
||||
unsafe-add-collect-callbacks
|
||||
unsafe-remove-collect-callbacks
|
||||
|
||||
;; not the same as Racket will executors:
|
||||
(rename
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
[(zero? (bitwise-and c collect-generation-radix-mask))
|
||||
(loop (bitwise-arithmetic-shift-right c log-collect-generation-radix) (add1 gen))]
|
||||
[else gen]))])])
|
||||
(run-collect-callbacks car)
|
||||
(collect gen)
|
||||
(let ([post-allocated (bytes-allocated)])
|
||||
(when (= gen (collect-maximum-generation))
|
||||
|
@ -59,7 +60,8 @@
|
|||
(garbage-collect-notify gen
|
||||
pre-allocated pre-allocated+overhead pre-time pre-cpu-time
|
||||
post-allocated (current-memory-bytes) (real-time) (cpu-time)))
|
||||
(poll-foreign-guardian))))
|
||||
(poll-foreign-guardian)
|
||||
(run-collect-callbacks cdr))))
|
||||
|
||||
(define collect-garbage
|
||||
(case-lambda
|
||||
|
@ -295,3 +297,90 @@
|
|||
(check who phantom-bytes? phantom-bstr)
|
||||
(check who exact-nonnegative-integer? k)
|
||||
(phantom-bytes-size-set! phantom-bstr k))
|
||||
|
||||
;; ----------------------------------------
|
||||
|
||||
;; List of (cons <pre> <post>)
|
||||
(define collect-callbacks '())
|
||||
|
||||
(define (unsafe-add-collect-callbacks pre post)
|
||||
(let ([p (cons pre post)])
|
||||
(with-interrupts-disabled
|
||||
(set! collect-callbacks (cons p collect-callbacks)))
|
||||
p))
|
||||
|
||||
(define (unsafe-remove-collect-callbacks p)
|
||||
(with-interrupts-disabled
|
||||
(set! collect-callbacks (#%remq p collect-callbacks))))
|
||||
|
||||
(define (run-collect-callbacks sel)
|
||||
(let loop ([l collect-callbacks])
|
||||
(unless (null? l)
|
||||
(let ([v (sel (car l))])
|
||||
(let loop ([i 0] [save #f])
|
||||
(unless (fx= i (#%vector-length v))
|
||||
(loop (fx+ i 1)
|
||||
(run-one-collect-callback (#%vector-ref v i) save sel))))
|
||||
(loop (cdr l))))))
|
||||
|
||||
(define-syntax (osapi-foreign-procedure stx)
|
||||
(syntax-case stx ()
|
||||
[(_ s ...)
|
||||
(case (machine-type)
|
||||
[(a6nt ta6nt i3nt ti3nt) #'(foreign-procedure _stdcall s ...)]
|
||||
[else #'(foreign-procedure s ...)])]))
|
||||
|
||||
;; This is an inconvenient callback interface, certainly, but it
|
||||
;; accomodates a limitatuon of the traditional Racket implementation
|
||||
(define (run-one-collect-callback v save sel)
|
||||
(let ([protocol (#%vector-ref v 0)]
|
||||
[proc (cpointer-address (#%vector-ref v 1))]
|
||||
[ptr (lambda (i)
|
||||
(cpointer*-address (#%vector-ref v (fx+ 2 i))))]
|
||||
[val (lambda (i)
|
||||
(#%vector-ref v (fx+ 2 i)))])
|
||||
(case protocol
|
||||
[(int->void)
|
||||
((foreign-procedure proc (int) void) (val 0))
|
||||
save]
|
||||
[(ptr_ptr_ptr_int->void)
|
||||
((foreign-procedure proc (void* void* void* int) void) (ptr 0) (ptr 1) (ptr 2) (val 3))
|
||||
save]
|
||||
[(ptr_ptr->save)
|
||||
((foreign-procedure proc (void* void*) void*) (ptr 0) (ptr 1))]
|
||||
[(save!_ptr->void)
|
||||
(and save (not (eqv? save 0))
|
||||
((foreign-procedure proc (void* void*) void*) save (ptr 0))
|
||||
save)]
|
||||
[(ptr_ptr_ptr->void)
|
||||
((foreign-procedure proc (void* void* void*) void) (ptr 0) (ptr 1) (ptr 2))
|
||||
save]
|
||||
[(ptr_ptr_float->void)
|
||||
((foreign-procedure proc (void* void* float) void) (ptr 0) (ptr 1) (val 2))
|
||||
save]
|
||||
[(ptr_ptr_double->void)
|
||||
((foreign-procedure proc (void* void* double) void) (ptr 0) (ptr 1) (val 2))
|
||||
save]
|
||||
[(float_float_float_float->void)
|
||||
((foreign-procedure proc (float float float float) void) (val 0) (val 1) (val 2) (val 3))
|
||||
save]
|
||||
[(ptr_ptr_ptr_int_int_int_int_int_int_int_int_int->void)
|
||||
((foreign-procedure proc (void* void* void* int int int int int int int int int) void)
|
||||
(ptr 0) (ptr 2) (ptr 2)
|
||||
(val 3) (val 4) (val 5) (val 6)
|
||||
(val 7) (val 8) (val 9) (val 10) (val 11))
|
||||
save]
|
||||
[(osapi_ptr_ptr->void)
|
||||
((osapi-foreign-procedure proc (void* void*) void) (ptr 0) (ptr 1))
|
||||
save]
|
||||
[(osapi_ptr_int->void)
|
||||
((osapi-foreign-procedure proc (void* int) void) (ptr 0) (val 1))
|
||||
save]
|
||||
[(osapi_ptr_int_int_int_int_ptr_int_int_long->void)
|
||||
((osapi-foreign-procedure proc (void* int int int int void* int int long) void)
|
||||
(ptr 0) (val 1) (val 2) (val 3) (val 4)
|
||||
(ptr 5) (val 6) (val 7) (val 8))
|
||||
save]
|
||||
[else
|
||||
(eprintf "unrecognized collect-callback protocol: ~s\n" protocol)
|
||||
save])))
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#define USE_COMPILED_STARTUP 1
|
||||
|
||||
#define EXPECTED_PRIM_COUNT 1435
|
||||
#define EXPECTED_PRIM_COUNT 1437
|
||||
|
||||
#ifdef MZSCHEME_SOMETHING_OMITTED
|
||||
# undef USE_COMPILED_STARTUP
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
consistently.)
|
||||
*/
|
||||
|
||||
#define MZSCHEME_VERSION "7.0.0.8"
|
||||
#define MZSCHEME_VERSION "7.0.0.9"
|
||||
|
||||
#define MZSCHEME_VERSION_X 7
|
||||
#define MZSCHEME_VERSION_Y 0
|
||||
#define MZSCHEME_VERSION_Z 0
|
||||
#define MZSCHEME_VERSION_W 8
|
||||
#define MZSCHEME_VERSION_W 9
|
||||
|
||||
#define MZSCHEME_VERSION_MAJOR ((MZSCHEME_VERSION_X * 100) + MZSCHEME_VERSION_Y)
|
||||
#define MZSCHEME_VERSION_MINOR ((MZSCHEME_VERSION_Z * 1000) + MZSCHEME_VERSION_W)
|
||||
|
|
|
@ -347,6 +347,9 @@ static Scheme_Object *unsafe_make_os_semaphore(int argc, Scheme_Object *argv[]);
|
|||
static Scheme_Object *unsafe_os_semaphore_wait(int argc, Scheme_Object *argv[]);
|
||||
static Scheme_Object *unsafe_os_semaphore_post(int argc, Scheme_Object *argv[]);
|
||||
|
||||
static Scheme_Object *unsafe_add_collect_callbacks(int argc, Scheme_Object *argv[]);
|
||||
static Scheme_Object *unsafe_remove_collect_callbacks(int argc, Scheme_Object *argv[]);
|
||||
|
||||
static Scheme_Object *make_plumber(int argc, Scheme_Object *argv[]);
|
||||
static Scheme_Object *plumber_p(int argc, Scheme_Object *argv[]);
|
||||
static Scheme_Object *plumber_flush_all(int argc, Scheme_Object *argv[]);
|
||||
|
@ -675,6 +678,9 @@ scheme_init_unsafe_thread (Scheme_Startup_Env *env)
|
|||
ADD_PRIM_W_ARITY("unsafe-make-os-semaphore", unsafe_make_os_semaphore, 0, 0, env);
|
||||
ADD_PRIM_W_ARITY("unsafe-os-semaphore-wait", unsafe_os_semaphore_wait, 1, 1, env);
|
||||
ADD_PRIM_W_ARITY("unsafe-os-semaphore-post", unsafe_os_semaphore_post, 1, 1, env);
|
||||
|
||||
ADD_PRIM_W_ARITY("unsafe-add-collect-callbacks", unsafe_add_collect_callbacks, 2, 2, env);
|
||||
ADD_PRIM_W_ARITY("unsafe-remove-collect-callbacks", unsafe_remove_collect_callbacks, 1, 1, env);
|
||||
}
|
||||
|
||||
void scheme_init_thread_places(void) {
|
||||
|
@ -8747,6 +8753,18 @@ void scheme_remove_gc_callback(Scheme_Object *key)
|
|||
}
|
||||
}
|
||||
|
||||
static Scheme_Object *unsafe_add_collect_callbacks(int argc, Scheme_Object *argv[])
|
||||
{
|
||||
return scheme_add_gc_callback(argv[0], argv[1]);
|
||||
}
|
||||
|
||||
static Scheme_Object *unsafe_remove_collect_callbacks(int argc, Scheme_Object *argv[])
|
||||
{
|
||||
scheme_remove_gc_callback(argv[0]);
|
||||
return scheme_void;
|
||||
}
|
||||
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
# define mzOSAPI WINAPI
|
||||
#else
|
||||
|
|
Loading…
Reference in New Issue
Block a user