Skip to content

g golf await ports

Chris Vine edited this page Jan 17, 2020 · 1 revision

Most of the scheme files provided by this library are by default compiled by this library to bytecode. That is not the case with this module, so as not to create a hard dependency on g-golf.

Including this module will automatically enable suspendable ports. To disable suspendable ports again, uninstall-suspendable-ports! can be called, but this means that those procedures can no longer be used while suspendable ports are disabled. In addition, any port using those procedures must be made non-blocking using fcntl as follows:

(fcntl *port* F_SETFL (logior O_NONBLOCK
                      (fcntl *port* F_GETFL)))

See the introductory remarks at (a-sync await-ports) for more about the guile i/o procedures which can safely be used with suspendable ports (and so with await-glib-read-suspendable and await-glib-write-suspendable).

The (a-sync g-golf await-ports) module provides the following procedures:


(await-glib-read-suspendable await resume port proc)

'proc' is a procedure taking a single argument, to which the port will be passed when it is invoked. The purpose of 'proc' is to carry out i/o operations on 'port' using the port's normal read procedures. 'port' must be a suspendable non-blocking port. This procedure will return when 'proc' returns, as if by blocking read operations, with the value returned by 'proc'. However, the glib event loop will not be blocked by this procedure even if only individual characters or bytes comprising part characters are available at any one time. It is intended to be called in a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments). 'proc' must not itself explicitly apply 'await' and 'resume' as those are potentially in use by the suspendable port while 'proc' is executing.

If an exceptional condition ('pri) is encountered by the implementation, #f will be returned by this procedure and the read operations to be performed by 'proc' will be abandonned; there is however no guarantee that any exceptional condition that does arise will be encountered by the implementation - the user procedure 'proc' may get there first and deal with it, or it may not. However exceptional conditions are very rare, usually comprising only out-of-band data on a TCP socket, or a pseudoterminal master in packet mode seeing state change in a slave. In the absence of an exceptional condition, the value(s) returned by 'proc' will be returned.

This procedure must (like the a-sync procedure) be called in the same thread as that in which the event loop runs.

Exceptions (say, from 'proc' because of port or conversion errors) will propagate out of this procedure in the first instance, and if not caught locally will then propagate out of g-main-loop-run.

Unlike the await-* procedures in the (a-sync g-golf base) module, this procedure will not call 'await' if the read operation(s) in 'proc' can be effected immediately without waiting: instead, after reading this procedure would return straight away without invoking the glib main loop.

This procedure is first available in version 0.19 of this library.

As an example of how to use await-glib-read-suspendable, here is the implementation of await-glib-getline:

(define (await-glib-getline await resume port)
  (await-glib-read-suspendable await resume port
                               (lambda (p)
                                 (read-line p))))

(await-glib-getline await resume port)

This procedure is provided mainly to retain compatibility with the guile-a-sync library for guile-2.0, because it is trivial to implement with await-glib-read-suspendable (and is implemented by await-glib-read-suspendable).

It is intended to be called in a waitable procedure invoked by a-sync, and reads a line of text from a non-blocking suspendable port and returns it (without the terminating '\n' character). See the documentation on the await-glib-read-suspendable procedure for further particulars about this procedure.

This procedure is first available in version 0.19 of this library.

Here is an example of the use of await-glib-getline:

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
          (display "Enter a line of text at the keyboard\n")
          (let ((port (open "/dev/tty" O_RDONLY)))
            (fcntl port F_SETFL (logior O_NONBLOCK
                                (fcntl port F_GETFL)))
            (simple-format #t
                           "The line was: ~A\n"
                           (await-glib-getline await resume
                                               port)))
          (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-geteveryline await resume port proc)

This procedure is provided mainly to retain compatibility with the guile-a-sync library for guile-2.0, because it is trivial to implement with await-glib-read-suspendable (and is implemented by await-glib-read-suspendable).

It is intended to be called within a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments), and will apply 'proc' to every complete line of text received (without the terminating '\n' character). The watch will not end until end-of-file or an exceptional condition ('pri) is reached. In the event of that happening, this procedure will end and return an end-of-file object or #f respectively.

When 'proc' executes, 'await' and 'resume' will still be in use by this procedure, so they may not be reused by 'proc' (even though 'proc' runs in the event loop thread).

See the documentation on the await-glib-read-suspendable procedure for further particulars about this procedure.

This procedure is first available in version 0.19 of this library.

Here is an example of the use of await-glib-geteveryline (because the keyboard has no end-of-file, use Ctrl-C to exit this code snippet):

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
          (display "Enter lines of text at the keyboard, ^C to finish\n")
          (let ((port (open "/dev/tty" O_RDONLY)))
            (fcntl port F_SETFL (logior O_NONBLOCK
                                        (fcntl port F_GETFL)))
            (await-glib-geteveryline await resume
                                     port
                                     (lambda (line)
                                       (simple-format #t
                                                      "The line was: ~A\n"
                                                      line))))
          (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-getsomelines await resume port proc)

This procedure is intended to be called within a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments), and does the same as await-glib-geteveryline, except that it provides a second argument to 'proc', namely an escape continuation which can be invoked by 'proc' to cause the procedure to return before end-of-file is reached. Behavior is identical to await-glib-geteveryline if the continuation is not invoked.

This procedure will apply 'proc' to every complete line of text received (without the terminating '\n' character). The watch will not end until end-of-file or an exceptional condition ('pri) is reached, which would cause this procedure to end and return an end-of-file object or #f respectively, or until the escape continuation is invoked, in which case the value passed to the escape continuation will be returned.

When 'proc' executes, 'await' and 'resume' will still be in use by this procedure, so they may not be reused by 'proc' (even though 'proc' runs in the event loop thread).

See the documentation on the await-glib-read-suspendable procedure for further particulars about this procedure.

This procedure is first available in version 0.19 of this library.

Here is an example of the use of await-glib-getsomelines:

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
          (display "Enter lines of text at the keyboard, enter an empty line to finish\n")
          (let ((port (open "/dev/tty" O_RDONLY)))
            (fcntl port F_SETFL (logior O_NONBLOCK
                                        (fcntl port F_GETFL)))
            (await-glib-getsomelines await resume
                                     port
                                     (lambda (line k)
                                       (when (string=? line "")
                                         (k #f))
                                       (simple-format #t
                                                     "The line was: ~A\n"
                                                     line))))
          (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-getblock await resume port size)

This procedure is provided mainly to retain compatibility with the guile-a-sync library for guile-2.0, because an implementation is trivial to implement with await-glib-read-suspendable (and is implemented by await-glib-read-suspendable).

It is intended to be called in a waitable procedure invoked by a-sync, and reads a block of data, such as a binary record, of size 'size' from a non-blocking suspendable port 'port'. This procedure will return a pair, normally comprising as its car a bytevector of length 'size' containing the data, and as its cdr the number of bytes received and placed in the bytevector (which will be the same as 'size' unless an end-of-file object was encountered part way through receiving the data). If an end-of-file object is encountered without any bytes of data, a pair with eof-object as car and #f as cdr will be returned.

See the documentation on the await-glib-read-suspendable procedure for further particulars about this procedure.

This procedure is first available in version 0.19 of this library.


(await-glib-geteveryblock await resume port size proc)

This procedure is provided mainly to retain compatibility with the guile-a-sync library for guile-2.0, because it is trivial to implement this kind of functionality with await-glib-read-suspendable (and is implemented by await-glib-read-suspendable).

It is intended to be called within a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments), and will apply 'proc' to any block of data received, such as a binary record. 'proc' should be a procedure taking two arguments, first a bytevector of length 'size' containing the block of data read and second the size of the block of data placed in the bytevector. The value passed as the size of the block of data placed in the bytevector will always be the same as 'size' unless end-of-file has been encountered after receiving only a partial block of data. The watch will not end until end-of-file or an exceptional condition ('pri) is reached. In the event of that happening, this procedure will end and return an end-of-file object or #f respectively.

For efficiency reasons, this procedure passes its internal bytevector buffer to 'proc' as proc's first argument and, when 'proc' returns, re-uses it. Therefore, if 'proc' stores its first argument for use after 'proc' has returned, it should store it by copying it.

When 'proc' executes, 'await' and 'resume' will still be in use by this procedure, so they may not be reused by 'proc' (even though 'proc' runs in the event loop thread).

See the documentation on the await-glib-read-suspendable procedure for further particulars about this procedure.

This procedure is first available in version 0.19 of this library.


(await-glib-getsomeblocks await resume port size proc)

This procedure is intended to be called within a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments), and does the same as await-glib-geteveryblock, except that it provides a third argument to 'proc', namely an escape continuation which can be invoked by 'proc' to cause the procedure to return before end-of-file is reached. Behavior is identical to await-glib-geteveryblock if the continuation is not invoked.

This procedure will apply 'proc' to any block of data received, such as a binary record. 'proc' should be a procedure taking three arguments, first a bytevector of length 'size' containing the block of data read, second the size of the block of data placed in the bytevector and third an escape continuation. The value passed as the size of the block of data placed in the bytevector will always be the same as 'size' unless end-of-file has been encountered after receiving only a partial block of data. The watch will not end until end-of-file or an exceptional condition ('pri) is reached, which would cause this procedure to end and return an end-of-file object or #f respectively, or until the escape continuation is invoked, in which case the value passed to the escape continuation will be returned.

For efficiency reasons, this procedure passes its internal bytevector buffer to 'proc' as proc's first argument and, when 'proc' returns, re-uses it. Therefore, if 'proc' stores its first argument for use after 'proc' has returned, it should store it by copying it.

When 'proc' executes, 'await' and 'resume' will still be in use by this procedure, so they may not be reused by 'proc' (even though 'proc' runs in the event loop thread).

See the documentation on the await-glib-read-suspendable procedure for further particulars about this procedure.

This procedure is first available in version 0.19 of this library.


(await-glib-write-suspendable await resume port proc)

'proc' is a procedure taking a single argument, to which the port will be passed when it is invoked. The purpose of 'proc' is to carry out i/o operations on 'port' using the port's normal write procedures. 'port' must be a suspendable non-blocking port. This procedure will return when 'proc' returns, as if by blocking write operations, with the value(s) returned by 'proc'. However, the glib main loop will not be blocked by this procedure even if only individual characters or bytes comprising part characters can be written at any one time. It is intended to be called in a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments). 'proc' must not itself explicitly apply 'await' and 'resume' as those are potentially in use by the suspendable port while 'proc' is executing.

This procedure must (like the a-sync procedure) be called in the same thread as that in which the default glib main loop runs.

Exceptions (say, from 'proc' because of port or conversion errors) will propagate out of this procedure in the first instance, and if not caught locally will then propagate out of g-main-loop-run.

Unlike the await-* procedures in the (a-sync g-golf base) module, this procedure will not call 'await' if the write operation(s) in 'proc' can be effected immediately without waiting: instead, after writing this procedure would return straight away without invoking the glib main loop.

This procedure is first available in version 0.19 of this library.

As an example of how to use await-glib-write-suspendable, here is the implementation of await-glib-put-string:

(define (await-glib-put-string await resume port text)
  (await-glib-write-suspendable await resume port
                                (lambda (p)
                                  (put-string p text)
                                  ;; enforce a flush when the current
                                  ;; write-waiter is still in operation
                                  (force-output p))))

(await-glib-put-bytevector await resume port bv)

This procedure is provided mainly to retain compatibility with the guile-a-sync library for guile-2.0, because it is trivial to implement with await-glib-write-suspendable (and is implemented by await-glib-write-suspendable).

It is intended to be called in a waitable procedure invoked by a-sync, and will write a bytevector to the port.

See the documentation on the await-glib-write-suspendable procedure for further particulars about this procedure.

This procedure is first available in version 0.19 of this library.

As mentioned in relation to the await-glib-write-suspendable procedure, write exceptions will propagate out of this procedure in the first instance, and if not caught locally (say by placing a catch block immediately around this procedure) will then propagate out of g-main-loop-run. So one way of testing for EPIPE is as follows:

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
	  (catch 'system-error
		 (lambda ()
		   (await-glib-put-bytevector await resume port bv))
		 (lambda args
		   (if (= (system-error-errno args) EPIPE)
		       (begin
			 ... do something to cater for EPIPE ...)
		       (begin
			 ;; possibly rethrow the exception
			 (apply throw args)))))
	  (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-put-string await resume port text)

This procedure is provided mainly to retain compatibility with the guile-a-sync library for guile-2.0, because it is trivial to implement with await-glib-write-suspendable (and is implemented by await-glib-write-suspendable).

It is intended to be called in a waitable procedure invoked by a-sync, and will write a string to the port.

If CR-LF line endings are to be written when outputting the string, the '\r' character (as well as the '\n' character) must be embedded in the string.

See the documentation on the await-glib-write-suspendable procedure for further particulars about this procedure.

This procedure is first available in version 0.19 of this library.

As mentioned in relation to the await-glib-write-suspendable procedure, write exceptions will propagate out of this procedure in the first instance, and if not caught locally (say by placing a catch block immediately around this procedure) will then propagate out of g-main-loop-run. So one way of testing for EPIPE is as follows:

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
	  (catch 'system-error
		 (lambda ()
		   (await-glib-put-string await resume port "test"))
		 (lambda args
		   (if (= (system-error-errno args) EPIPE)
		       (begin
			 ... do something to cater for EPIPE ...)
		       (begin
			 ;; possibly rethrow the exception
			 (apply throw args)))))
	  (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-accept await resume sock)

This procedure is provided mainly to retain compatibility with the guile-a-sync library for guile-2.0, because it is trivial to implement with await-glib-read-suspendable (and is implemented by await-glib-read-suspendable).

This procedure will start a watch on listening socket 'sock' for a connection. 'sock' must be a non-blocking socket port. This procedure wraps the guile 'accept' procedure and therefore returns a pair, comprising as car a connection socket, and as cdr a socket address object containing particulars of the address of the remote connection. This procedure is intended to be called within a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments).

See the documentation on the await-glib-read-suspendable procedure for further particulars about this procedure.

This procedure is first available in version 0.19 of this library.


(await-glib-connect await resume sock . args)

This procedure is provided mainly to retain compatibility with the guile-a-sync library for guile-2.0, because it is trivial to implement with await-glib-write-suspendable (and is implemented by await-glib-write-suspendable).

This procedure will connect socket 'sock' to a remote host. Particulars of the remote host are given by 'args' which are the arguments (other than 'sock') taken by guile's 'connect' procedure, which this procedure wraps. 'sock' must be a non-blocking socket port. This procedure is intended to be called in a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments).

There are cases where it will not be helpful to use this procedure. Where a connection request is immediately followed by a write to the remote server (say, a get request), the call to 'connect' and to 'put-string' can be combined in a single procedure passed to await-glib-write-suspendable.

See the documentation on the await-glib-write-suspendable procedure for further particulars about this procedure.

This procedure is first available in version 0.19 of this library.