From: Maxime Devos <maximedevos@telenet.be> To: Andy Wingo <wingo@pobox.com> Cc: guile-user@gnu.org Subject: [WIP PATCH] (for fibers): wait-until-port-readable-operation / wait-until-port-writable-operation Date: Sun, 14 Feb 2021 17:20:22 +0100 Message-ID: <92c5306a988b9af3eab466258e4db81fe89e1daf.camel@telenet.be> (raw) [-- Attachment #1.1: Type: text/plain, Size: 1038 bytes --] Hi Guilers, This is a (buggy) patch to guile-fibers that adds a ‘wait-until-port-readable-operation’ and ‘wait-until-port-writable’ that can be used to wait until a port (backed by a fd) becomes readable or writable respectively. Now I seem to have hit a problem: the first few tests (more to be written later) pass just fine, but the following test never completes: ;; XXX why does this hang? Shouldn't there ;; be a time-out? (assert-run-fibers-returns (#f) (writable/timeout A)) (writable/timeout is a combination of wait-until-port-writable and sleep-operation, that also retries a few times to allow for spurious wakeups.) Oddly, the same test but with readable/timeout completes just fine. Any thoughts? (Warning: I don't know what I'm doing; I'm not familiar with relevant literature on operations, I'm just trying to complete wingo's proposal). Wild guess: maybe schedule-task-when-fd-writable prevents the fiber from being resumed by other causes? Greetings, Maxime. [-- Attachment #1.2: 0001-WIP-Implement-operations-for-waiting-for-readability.patch --] [-- Type: text/x-patch, Size: 10557 bytes --] From b8ac732543661b5ee7e2f38482f824b638664bd2 Mon Sep 17 00:00:00 2001 From: Maxime Devos <maximedevos@telenet.be> Date: Sun, 14 Feb 2021 17:04:02 +0100 Subject: [PATCH] (WIP) Implement operations for waiting for readability / writability * fibers/io-wakeup.scm (wait-until-port-readable-operation) (wait-until-port-writable-operation): new operations. (writable?): helper procedure for 'wait-until-port-writable-operation' * tests/io-wakeup.scm: test 'fibers/io-wakeup.scm' * Makefile.am (SOURCES): compile 'fibers/io-wakeup.scm'. (TESTS): run 'tests/io-wakeup.scm'. --- Makefile.am | 2 + fibers/io-wakeup.scm | 97 ++++++++++++++++++++++++++++++ tests/io-wakeup.scm | 139 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 fibers/io-wakeup.scm create mode 100644 tests/io-wakeup.scm diff --git a/Makefile.am b/Makefile.am index e2db57e..0134255 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,6 +33,7 @@ SOURCES = \ fibers/deque.scm \ fibers/epoll.scm \ fibers/interrupts.scm \ + fibers/io-wakeup.scm \ fibers/nameset.scm \ fibers/operations.scm \ fibers/posix-clocks.scm \ @@ -67,6 +68,7 @@ TESTS = \ tests/conditions.scm \ tests/channels.scm \ tests/foreign.scm \ + tests/io-wakeup.scm \ tests/parameters.scm \ tests/preemption.scm \ tests/speedup.scm diff --git a/fibers/io-wakeup.scm b/fibers/io-wakeup.scm new file mode 100644 index 0000000..6a91abe --- /dev/null +++ b/fibers/io-wakeup.scm @@ -0,0 +1,97 @@ +;; Fibers: cooperative, event-driven user-space threads. + +;;;; Copyright (C) 2016,2021 Free Software Foundation, Inc. +;;;; Copyright (C) 2021 Maxime Devos +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Lesser General Public +;;;; License as published by the Free Software Foundation; either +;;;; version 3 of the License, or (at your option) any later version. +;;;; +;;;; This library is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;;;; Lesser General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Lesser General Public +;;;; License along with this library; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;;;; + +(define-module (fibers io-wakeup) + #:use-module (fibers scheduler) + #:use-module (fibers operations) + #:use-module (ice-9 atomic) + #:use-module (ice-9 match) + #:use-module (ice-9 threads) + #:export (wait-until-port-readable-operation + wait-until-port-writable-operation)) +;; Make sure (ice-9 port) is loaded before +;; trying to load (ice-9 port internal). +(use-modules (ice-9 ports)) +(use-modules (ice-9 ports internal)) + +(define *poll-sched* (make-atomic-box #f)) + +(define (poll-sched) + (or (atomic-box-ref *poll-sched*) + (let ((sched (make-scheduler))) + (cond + ((atomic-box-compare-and-swap! *poll-sched* #f sched)) + (else + ;; FIXME: Would be nice to clean up this thread at some point. + (call-with-new-thread + (lambda () + (define (finished?) #f) + (run-scheduler sched finished?))) + sched))))) + +;; These procedure are subject to spurious wakeups. + +(define (wait-until-port-readable-operation port) + "Make an operation that will succeed when PORT is readable." + (make-base-operation #f + (lambda () + (and (char-ready? port) + values)) + (lambda (flag sched resume) + (define (commit) + (match (atomic-box-compare-and-swap! flag 'W 'S) + ('W (resume values)) + ('C (commit)) + ('S #f))) + (if sched + (schedule-task-when-fd-readable + sched (port-read-wait-fd port) commit) + (schedule-task + (poll-sched) + (lambda () + (perform-operation (wait-until-port-readable-operation port)) + (commit))))))) + +(define (writable? port) + "Test if PORT is writable." + (match (select #() (vector port) #() 0) + ((#() #() #()) #f) + ((#() #(_) #()) #t))) + +(define (wait-until-port-writable-operation port) + "Make an operation that will succeed when PORT is writable." + (make-base-operation #f + (lambda () + (and (writable? port) + values)) + (lambda (flag sched resume) + (define (commit) + (match (atomic-box-compare-and-swap! flag 'W 'S) + ('W (resume values)) + ('C (commit)) + ('S #f))) + (if sched + (schedule-task-when-fd-writable + sched (port-write-wait-fd port) commit) + (schedule-task + (poll-sched) + (lambda () + (perform-operation (wait-until-port-writable-operation port)) + (commit))))))) diff --git a/tests/io-wakeup.scm b/tests/io-wakeup.scm new file mode 100644 index 0000000..ea8babe --- /dev/null +++ b/tests/io-wakeup.scm @@ -0,0 +1,139 @@ +;; Fibers: cooperative, event-driven user-space threads. + +;;;; Copyright (C) 2016 Free Software Foundation, Inc. +;;;; Copyright (C) 2021 Maxime Devos +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Lesser General Public +;;;; License as published by the Free Software Foundation; either +;;;; version 3 of the License, or (at your option) any later version. +;;;; +;;;; This library is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;;;; Lesser General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Lesser General Public +;;;; License along with this library; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;;;; + +(define-module (tests io-wakeup) + #:use-module (fibers) + #:use-module (fibers io-wakeup) + #:use-module (fibers operations) + #:use-module (fibers timers)) + +(define failed? #f) + +(define-syntax-rule (assert-equal expected actual) + (let ((x expected)) + (format #t "assert ~s equal to ~s: " 'actual x) + (force-output) + (let ((y actual)) + (cond + ((equal? x y) (format #t "ok\n")) + (else + (format #t "no (got ~s)\n" y) + (set! failed? #t)))))) + +(define-syntax-rule (assert-run-fibers-terminates exp) + (begin + (format #t "assert run-fibers on ~s terminates: " 'exp) + (force-output) + (let ((start (get-internal-real-time))) + (call-with-values (lambda () (run-fibers (lambda () exp))) + (lambda vals + (format #t "ok (~a s)\n" (/ (- (get-internal-real-time) start) + 1.0 internal-time-units-per-second)) + (apply values vals)))))) + +(define-syntax-rule (assert-run-fibers-returns (expected ...) exp) + (begin + (call-with-values (lambda () (assert-run-fibers-terminates exp)) + (lambda run-fiber-return-vals + (assert-equal '(expected ...) run-fiber-return-vals))))) + +(define* (with-timeout op #:key (seconds 0.05) (wrap values)) + (choice-operation op + (wrap-operation (sleep-operation seconds) wrap))) + +(define* (readable/timeout port #:key (allowed-spurious 5)) + "Does waiting for readability not time-out? +Allow @var{allowed-spurious} spurious wakeups." + (and (perform-operation + (with-timeout + (wrap-operation (wait-until-port-readable-operation port) + (lambda () #t)) + #:wrap (lambda () #f))) + (or (<= (pk 'spurious/readable allowed-spurious) 0) + (readable/timeout port #:allowed-spurious + (- allowed-spurious 1))))) + +(define* (writable/timeout port #:key (allowed-spurious 5)) + "Does waiting for readability not time-out? +Allow @var{allowed-spurious} spurious wakeups." + (and (perform-operation + (with-timeout + (wrap-operation (wait-until-port-writable-operation port) + (lambda () #t)) + #:wrap (lambda () #f))) + (or (<= (pk 'spurious/writable allowed-spurious) 0) + (writable/timeout port #:allowed-spurious + (- allowed-spurious 1))))) + +;; Tests: +;; * wait-until-port-readable-operaton / wait-until-port-writable-operation +;; blocks if the port isn't ready for input / output. +;; +;; This is tested with a pipe (read & write) +;; and a listening socket (read, or accept in this case). +;; +;; Due to the possibility of spurious wakeups, +;; a limited few spurious wakeups are tolerated. +;; +;; * these operations succeed if the port is ready for input / output. +;; +;; These are again tested with a pipe and a listening socket +;; +;; Blocking is detected with a small time-out. + +(define (make-listening-socket) + (let ((server (socket PF_INET SOCK_DGRAM 0))) + (bind server AF_INET INADDR_LOOPBACK 0) + server)) + +(let ((s (make-listening-socket))) + (assert-run-fibers-returns (#f) + (readable/timeout s)) + (assert-equal #f (readable/timeout s)) + (close s)) + +(define-syntax-rule (with-pipes (A B) exp exp* ...) + (let* ((pipes (pipe)) + (A (car pipes)) + (B (cdr pipes))) + exp exp* ... + (close A) + (close B))) + +(with-pipes (A B) + (setvbuf A 'none) + (setvbuf B 'none) + (assert-run-fibers-returns (#f) + (readable/timeout A)) + (assert-equal #f (readable/timeout A)) + + ;; XXX why does this hang? Shouldn't there + ;; be a time-out? + (assert-run-fibers-returns (#f) + (writable/timeout A)) + (assert-equal #f (writable/timeout A)) + + ) + +(exit (if failed? 1 0)) + +;; Local Variables: +;; eval: (put 'with-pipes 'scheme-indent-function 1) +;; End: -- 2.30.0 [-- Attachment #2: This is a digitally signed message part --] [-- Type: application/pgp-signature, Size: 260 bytes --]
reply other threads:[~2021-02-14 16:20 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style List information: https://www.gnu.org/software/guile/ * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=92c5306a988b9af3eab466258e4db81fe89e1daf.camel@telenet.be \ --to=maximedevos@telenet.be \ --cc=guile-user@gnu.org \ --cc=wingo@pobox.com \ --subject='Re: [WIP PATCH] (for fibers): wait-until-port-readable-operation / wait-until-port-writable-operation' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
unofficial mirror of guile-user@gnu.org This inbox may be cloned and mirrored by anyone: git clone --mirror https://yhetil.org/guile-user/0 guile-user/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 guile-user guile-user/ https://yhetil.org/guile-user \ guile-user@gnu.org public-inbox-index guile-user Example config snippet for mirrors. Newsgroups are available over NNTP: nntp://news.yhetil.org/yhetil.lisp.guile.user nntp://news.gmane.io/gmane.lisp.guile.user AGPL code for this site: git clone http://ou63pmih66umazou.onion/public-inbox.git