diff --git a/pkgs/racket-doc/scribblings/foreign/schedule.scrbl b/pkgs/racket-doc/scribblings/foreign/schedule.scrbl index 391d9c9268..0806e435f2 100644 --- a/pkgs/racket-doc/scribblings/foreign/schedule.scrbl +++ b/pkgs/racket-doc/scribblings/foreign/schedule.scrbl @@ -6,7 +6,9 @@ @defmodule[ffi/unsafe/schedule]{The @racketmodname[ffi/unsafe/schedule] library provides functions for -cooperating with the thread scheduler and manipulating it.} +cooperating with the thread scheduler and manipulating it. These +operations are unsafe because callbacks run in @tech{atomic mode} +and in an unspecified thread.} @history[#:added "6.11.0.1"] @@ -16,8 +18,8 @@ cooperating with the thread scheduler and manipulating it.} Produces a @deftech{poller} value that is allowed as a @racket[prop:evt] value, even though it is not a procedure or itself an @racket[evt?]. The @racket[poll] callback is called in @tech{atomic -mode} to check whether the event is ready or to allow it to register a -wakeup trigger. +mode} in an unspecified thread to check whether the event is ready or +to allow it to register a wakeup trigger. The first argument to @racket[poll] is always the object that is used as a @tech[#:doc reference.scrbl]{synchronizable event} with the @@ -74,14 +76,6 @@ Causes the Racket process will wake up and resume polling at the point when @racket[(current-inexact-milliseconds)] starts returning a value that is @racket[msecs] or greater.} - -Registers a file descriptor (Unix and Mac OS) or socket (all -platforms) to cause the Racket process to wake up if the file -descriptor or socket becomes ready for reading, writing, or error -reporting, as selected by @racket[mode]. The @racket[wakeups] argument -must be a non-@racket[#f] value that is passed by the scheduler to a -@racket[unsafe-poller]-wrapped procedure.} - @defproc[(unsafe-set-sleep-in-thread! [foreground-sleep (-> any/c)] [fd fixnum?]) void?]{ @@ -101,6 +95,6 @@ Racket implementation. It always works for Mac OS.} @defproc[(unsafe-signal-received) void?]{ For use with @racket[unsafe-set-sleep-in-thread!] by -@racket[foreground-sleep] or something that it triggers, causes the -default sleeping function to request @racket[foreground-sleep] to +@racket[_foreground-sleep] or something that it triggers, causes the +default sleeping function to request @racket[_foreground-sleep] to return.} diff --git a/pkgs/racket-doc/scribblings/reference/evts.scrbl b/pkgs/racket-doc/scribblings/reference/evts.scrbl index 02808a0265..8948c3db0d 100644 --- a/pkgs/racket-doc/scribblings/reference/evts.scrbl +++ b/pkgs/racket-doc/scribblings/reference/evts.scrbl @@ -1,7 +1,8 @@ #lang scribble/doc @(require scribble/struct "mz.rkt" - (for-label racket/async-channel)) + (for-label racket/async-channel + (only-in ffi/unsafe/schedule unsafe-poller))) @(define evt-eval (make-base-eval)) @@ -355,6 +356,11 @@ A @tech{structure type property} that identifies structure types whose ] +@margin-note{For working with foreign libraries, a @racket[prop:evt] + value can also be a result of @racket[unsafe-poller], + although that possibility is omitted from the safe + contract of @racket[prop:evt].} + Instances of a structure type with the @racket[prop:input-port] or @racket[prop:output-port] property are also @tech{synchronizable events} by virtue of being a port. If the structure type has more than one of diff --git a/pkgs/racket-test-core/tests/racket/sync.rktl b/pkgs/racket-test-core/tests/racket/sync.rktl index 0dab256a2a..5b7600d6fd 100644 --- a/pkgs/racket-test-core/tests/racket/sync.rktl +++ b/pkgs/racket-test-core/tests/racket/sync.rktl @@ -1,7 +1,8 @@ - (load-relative "loadtest.rktl") +(require ffi/unsafe/schedule) + (Section 'synchronization) (define SYNC-SLEEP-DELAY 0.025) @@ -973,6 +974,37 @@ ;; make sure it's ok for rewind to be the first action: (test (void) thread-wait (thread (lambda () (thread-rewind-receive '(1 2 3))))) +;; ---------------------------------------- +;; Unsafe poller + +(let () + (struct p (results) + #:property prop:evt (unsafe-poller + (lambda (self wakeups) + (values (p-results self) #f)))) + + (test 17 sync (p '(17))) + (test add1 sync (p (list add1))) + (test-values '(16 17) (lambda () (sync (p '(16 17))))) + (test-values '() (lambda () (sync (p '()))))) + +(let () + ;; Let the scheduler poll up to `counter` times: + (define counter 20) + (struct p () + #:property prop:evt (unsafe-poller + (lambda (self wakeups) + (cond + [(zero? counter) + (values '(#t) #f)] + [else + (set! counter (sub1 counter)) + (when wakeups + ;; Cancel any sleep: + (unsafe-poll-ctx-milliseconds-wakeup wakeups (current-inexact-milliseconds))) + (values #f self)])))) + (test #t sync (p))) + ;; ---------------------------------------- ;; Garbage collection diff --git a/racket/src/racket/src/struct.c b/racket/src/racket/src/struct.c index 4e386e9051..5903090468 100644 --- a/racket/src/racket/src/struct.c +++ b/racket/src/racket/src/struct.c @@ -1646,6 +1646,21 @@ static Scheme_Object *return_wrapped(void *data, int argc, Scheme_Object *argv[] return (Scheme_Object *)data; } +static Scheme_Object *return_wrapped_multi(void *data, int argc, Scheme_Object *argv[]) +{ + Scheme_Object **a, *l = (Scheme_Object *)data; + int i, n; + + n = scheme_list_length(l); + a = MALLOC_N(Scheme_Object *, n); + for (i = 0; i < n; i++) { + a[i] = SCHEME_CAR(l); + l = SCHEME_CDR(l); + } + + return scheme_values(n, a); +} + static int evt_struct_is_ready(Scheme_Object *o, Scheme_Schedule_Info *sinfo) { Scheme_Object *v; @@ -1731,8 +1746,19 @@ static int evt_struct_is_ready(Scheme_Object *o, Scheme_Schedule_Info *sinfo) scheme_end_in_scheduler(); if (v) { - if (done && SCHEME_PROCP(v)) { - v = scheme_make_closed_prim_w_arity(return_wrapped, (void *)v, "wrapper", 1, 1); + if (done) { + int check_proc = 1; + if (SCHEME_PAIRP(v) && SCHEME_NULLP(SCHEME_CDR(v)) && !SCHEME_PROCP(SCHEME_CAR(v))) + v = SCHEME_CAR(v); /* single result */ + else if (!SCHEME_NULLP(v) && !SCHEME_PAIRP(v)) { + /* wrong result, be we allow it for backward compatibility */ + } else { + v = scheme_make_closed_prim_w_arity(return_wrapped_multi, (void *)v, "multi-wrapper", 1, 1); + check_proc = 0; + } + + if (check_proc && SCHEME_PROCP(v)) + v = scheme_make_closed_prim_w_arity(return_wrapped, (void *)v, "wrapper", 1, 1); } scheme_set_sync_target(sinfo, v, (done ? v : NULL), NULL, 0, 0, NULL); return 1;