unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
From: Andy Wingo <wingo@pobox.com>
To: Chris Vine <chris@cvine.freeserve.co.uk>
Cc: guile-user <guile-user@gnu.org>, guile-devel <guile-devel@gnu.org>
Subject: Re: anyone define port types?
Date: Sun, 19 Jun 2016 19:48:03 +0200	[thread overview]
Message-ID: <8737o9kr3g.fsf@pobox.com> (raw)
In-Reply-To: <20160619163327.36246706@dell.homenet> (Chris Vine's message of "Sun, 19 Jun 2016 16:33:27 +0100")

On Sun 19 Jun 2016 17:33, Chris Vine <chris@cvine.freeserve.co.uk> writes:

> The answer I have adopted when reading from TCP sockets is to extract
> individual bytes only from the port into a bytevector using R6RS's
> get-u8 procedure and (if the port is textual rather than binary) to
> reconstruct characters from that using bytevector->string at, say, a
> line end[1].  An EAGAIN/EWOULDBLOCK exception is then just treated as
> an invitation to return to the prompt, and read state is retained in
> the bytevector.

Yep, makes sense, though it will be really slow of course.  And, you'd
have to make people rewrite their code to use your I/O primitives
instead of using read-line from (ice-9 rdelim); a bit of a drag, but
OK.

>> Why not just (install-suspendable-ports!) and
>> 
>>   (parameterize ((current-read-waiter my-read-waiter)) ...)
>> 
>> etc?  It is entirely possible with Guile 2.1.3 to build an
>> asynchronous coroutine-style concurrent system in user-space using
>> these primitives. See the wip-ethread branch for an example
>> implementation.
>
> I would want to continue using an external event loop implemented with
> poll() or select() and delimited continuations.

Yes yes a million times yes!  That's what I want too.  To that end, the
current-read-waiter parameter's value is a function which takes the port
being waited on, and when it returns the operation will retry.  In
wip-ethreads I suspend to a prompt, add the port's fd to an epoll set,
and when the fd becomes readable again we resume the continuation,
resulting in a return from the current-read-waiter.

> I don't think I have got to grips with how to do that with read-waiter,
> because the read-waiter comprises in effect another loop (in which the
> main event loop with its own prompts would have to run) until the read
> request has been satisfied.  I would need to think about it.  Since
> ethreads use a poll()/epoll() loop, presumably you think it is
> straightforward enough to integrate the two, even if at present I don't.

Here is the code.  First, a helper:

  ;; The AFTER-SUSPEND thunk allows the user to suspend the current
  ;; thread, saving its state, and then perform some other nonlocal
  ;; control flow.
  ;;
  (define* (suspend #:optional (after-suspend (lambda (ctx thread) #f)))
    ((abort-to-prompt (econtext-prompt-tag (current-econtext))
                      after-suspend)))

Assume there is some current-econtext parameter with a "context" which
holds the prompt tag associated with the scheduler.  As you see when the
continuation resumes, it resumes by calling a thunk, allowing exceptions
to be thrown from the context of the suspend.

Here's the main loop function, which you could replace with the GLib
main loop or whatever:

  (define* (run #:optional (ctx (ensure-current-econtext))
                #:key (install-suspendable-ports? #t))
    (when install-suspendable-ports? (install-suspendable-ports!))
    (parameterize ((current-econtext ctx)
                   (current-read-waiter wait-for-readable)
                   (current-write-waiter wait-for-writable))
      (let lp ()
        (run-ethread ctx (next-thread ctx))
        (lp))))

Cool.  Now the wait functions:

  (define (wait-for-readable port)
    (wait-for-events port (port-read-wait-fd port) (logior EPOLLIN EPOLLRDHUP)))

  (define (wait-for-writable port)
    (wait-for-events port (port-write-wait-fd port) EPOLLOUT))

Now the wait-for-events function:

  (define (wait-for-events port fd events)
    (handle-events
     port
     events
     (suspend
      (lambda (ctx thread)
        ...))))

Well that's a mess, but the thing to know is that the `suspend' will
abort to the relevant prompt, and then invoke the thunk that's its
argument.  Here's `handle-events' and we are done:

  (define (handle-events port events revents)
    (unless (zero? (logand revents EPOLLERR))
      (error "error reading from port" port)))

But I guess that error could be "reading or writing"; oh well.

See
http://git.savannah.gnu.org/cgit/guile.git/tree/module/ice-9/ethreads.scm?h=wip-ethreads&id=253dc1a7114b89351a3aa330caf173b98c5a65dd,
it's not long but it takes some time to read.  I think I can fairly ask
that of you though, given your interest in this area :)

> On a side issue, I am still trying to understand the point of causing
> guile-2.2's read of a non-blocking C port to block.  The whole point of
> making a descriptor non-blocking is that that shouldn't happen, and
> there are circumstances where pealing individual bytes off a
> non-blocking port as they become available is what you want to do.  It
> makes guile's select wrapper unusable with TCP sockets on linux.  I
> understand that suspendable-ports work differently, but that is another
> matter.

I would start by saying that many things are possible including adding
the ability to enable the error-throwing behavior.  I think your use
case is a great one and we should support it well.  I think though that
perhaps you didn't see how the waiter parameters could apply to your use
case; are things clearer now?  I think that once you allow yourself to
use port buffers, things will be much easier and much more efficient as
well.  But, I could be misunderstanding too.

Guile's I/O in the past was very oriented towards ASCII.  2.0 went a
long way towards good textual I/O with other encodings, and with 2.2 we
will have good textual non-blocking I/O.  It could be that by focusing
on this, I have neglected some binary non-blocking I/O use cases, though
I think that get-bytevector-some, having enabled suspendable ports,
serves many of these purposes very well.  But if there is a need for an
additional binary I/O primitive, let's figure out what it is and
implement it.

Andy



  reply	other threads:[~2016-06-19 17:48 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-28 19:04 anyone define port types? Andy Wingo
2016-03-28 23:53 ` Matt Wette
2016-04-05 14:06   ` Mark H Weaver
2016-04-05 23:55     ` Matt Wette
2016-06-11 16:50       ` Andy Wingo
2016-03-29  7:58 ` tomas
2016-03-29  8:52 ` Nala Ginrut
2016-03-30  6:29 ` Panicz Maciej Godek
2016-03-30 11:18   ` Jan Nieuwenhuizen
2016-03-30 17:17     ` Panicz Maciej Godek
2016-03-30 17:53       ` Marko Rauhamaa
2016-03-30 19:02         ` Panicz Maciej Godek
2016-03-30 19:57           ` Marko Rauhamaa
2016-03-31 16:11             ` Barry Fishman
2016-03-31 19:28               ` Marko Rauhamaa
2016-03-30 19:43         ` Jan Wedekind
2016-03-30 20:07           ` Marko Rauhamaa
2016-03-30 21:01             ` Jan Wedekind
2016-03-30 22:44               ` Marko Rauhamaa
2016-03-31 20:42                 ` Jan Wedekind
2016-03-31 22:28                   ` Marko Rauhamaa
2016-06-11 16:53   ` Andy Wingo
2016-04-01 14:38 ` Ludovic Courtès
2016-06-11 16:57   ` Andy Wingo
2016-04-14 14:08 ` Ludovic Courtès
2016-06-11 17:02   ` Andy Wingo
2016-06-12  8:25     ` Chris Vine
2016-06-19  9:13       ` Andy Wingo
2016-06-19  9:55         ` Marko Rauhamaa
2016-06-19 15:27           ` Andy Wingo
2016-06-19 15:33         ` Chris Vine
2016-06-19 17:48           ` Andy Wingo [this message]
2016-06-19 20:09             ` Chris Vine
2016-06-20  3:38               ` William ML Leslie
2016-06-20  6:45                 ` Chris Vine
2016-06-20  7:34                   ` Andy Wingo
2016-06-20  9:01                     ` Chris Vine
2016-06-22 22:44                       ` Chris Vine
2016-06-23  7:36                         ` Andy Wingo
2016-06-23  8:56                           ` Andy Wingo
2016-06-23  9:24                           ` Chris Vine
2016-06-23  9:50                             ` Marko Rauhamaa
2016-06-23 10:43                             ` Andy Wingo
2016-06-23 11:49                               ` William ML Leslie

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=8737o9kr3g.fsf@pobox.com \
    --to=wingo@pobox.com \
    --cc=chris@cvine.freeserve.co.uk \
    --cc=guile-devel@gnu.org \
    --cc=guile-user@gnu.org \
    /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
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).