Skip to content

g golf base

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

When using the g-golf bindings for gtk+, in order to provide await semantics on gtk+ callbacks it will normally be necessary to use the 'await' and 'resume' procedures provided by the a-sync procedure in the (a-sync coroutines) module directly (calling 'resume' in the gtk+ callback when ready, and waiting on that callback using 'await'). However when launching timeouts, file watches or other events on the glib main loop, convenience procedures are possible similar to those provided for the event loop in the (a-sync event-loop) module. These are set out out in these (a-sync g-golf ...) modules.

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.

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


(a-sync-glib-quit loop)

Usually the default glib main loop runs throughout a program's lifetime and only quits (say, via GTK) when the program is brought to an end by the user. Notwithstanding that, in a non-GUI program sometimes you may need to quit the main loop programmatically (most of the glib example code in this library does so). To quit a main loop using g-main-loop-quit, the loop must be running. One problem that may arise from this is that the suspendable port procedures in the (a-sync g-golf await-ports) module do not suspend to the main loop if they do not need to. This procedure deals with the issue by posting a g-main-loop-quit event to the main loop instead of calling g-main-loop-quit directly, so ensuring that the main loop must be running when g-main-loop-quit is called.


(await-glib-task-in-thread await resume thunk [handler])

This is a convenience procedure which will run 'thunk' in its own thread, and then post an event to the default glib main loop when 'thunk' has finished. This procedure calls 'await' in the default glib main loop and will return the thunk's return value. It is intended to be called in a waitable procedure invoked by a-sync. If the optional 'handler' argument is provided, then it will be run in the default glib main loop if 'thunk' throws and its return value will be the return value of this procedure; otherwise the program will terminate if an unhandled exception propagates out of 'thunk'. 'handler' should take the same arguments as a guile catch handler (this is implemented using catch).

This procedure must (like the a-sync procedure) be called in the same thread as that in which the default glib main loop runs, where the result of calling 'thunk' will be received. As mentioned above, the thunk itself will run in its own thread.

Exceptions may propagate out of this procedure if they arise while setting up (that is, before the worker thread starts), which shouldn't happen unless memory is exhausted or pthread has run out of resources. Exceptions arising during execution of the task, if not caught by a handler procedure, will terminate the program. Exceptions thrown by the handler procedure will propagate out of g-main-loop-run.

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

Here is an example of the use of await-glib-task-in-thread:

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
          (simple-format #t "1 + 1 is ~A\n"
                         (await-glib-task-in-thread await resume
                                                    (lambda ()
                                                      (+ 1 1))))
          (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-task await resume thunk)

This is a convenience procedure for use with glib, which will run 'thunk' in the default glib main loop. This procedure calls 'await' and will return the thunk's return value. It is intended to be called in a waitable procedure invoked by a-sync. It is the single-threaded corollary of await-glib-task-in-thread. This means that (unlike with await-glib-task-in-thread) while 'thunk' is running other events in the main loop will not make progress, so blocking calls should not be made in 'thunk'.

When 'thunk' is executed, this procedure is waiting on 'await', so 'await' and 'resume' cannot be used again in 'thunk' (although 'thunk' can call a-sync to start another series of asynchronous operations with a new await-resume pair). For that reason, await-glib-yield is usually more convenient for composing asynchronous tasks. In retrospect, this procedure offers little over await-glib-yield, apart from symmetry with await-glib-task-in-thread.

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 may propagate out of this procedure if they arise while setting up (that is, before the task starts), which shouldn't happen unless memory is exhausted. Exceptions arising during execution of the task, if not caught locally, will propagate out of g-main-loop-run.

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

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

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
          (simple-format #t "1 + 1 is ~A\n"
                         (await-glib-task await resume
                                          (lambda ()
                                            (+ 1 1))))
          (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-yield await resume)

This is a convenience procedure for use with glib, which will surrender execution to the default glib main loop, so that code in other a-sync or compose-a-sync blocks can run. The remainder of the code after the call to await-glib-yield in the current a-sync or compose-a-sync block will execute on the next iteration through the loop. It is intended to be called within a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments). It's effect is similar to calling await-glib-task with a task that does nothing.

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

This procedure should not throw any exceptions unless memory is exhausted.

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

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

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
	  (display "In first iteration through event loop\n")
	  (await-glib-yield await resume)
	  (display "In next iteration through event loop\n")))
	  (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-generator-in-thread await resume generator proc [handler])

This is a convenience procedure for acting asynchronously on values yielded by generator procedures. The 'generator' argument is a procedure taking one argument, namely a yield argument (see the documentation on the make-iterator procedure for further details). This await-glib-generator-in-thread procedure will run 'generator' in its own worker thread, and whenever 'generator' yields a value will cause 'proc' to execute in the default glib main loop.

'proc' should be a procedure taking a single argument, namely the value yielded by the generator. If the optional 'handler' argument is provided, then that handler will be run in the default glib main loop if 'generator' throws; otherwise the program will terminate if an unhandled exception propagates out of 'generator'. 'handler' should take the same arguments as a guile catch handler (this is implemented using catch).

This procedure calls 'await' and will return when the generator has finished or, if 'handler' is provided, upon the generator throwing an exception. This procedure will return #f if the generator completes normally, or 'guile-a-sync-thread-error if the generator throws an exception and 'handler' is run (the 'guile-a-sync-thread-error symbol is reserved to the implementation and should not be yielded by the generator).

This procedure is intended to be called in a waitable procedure invoked by a-sync. It must (like the a-sync procedure) be called in the same thread as that in which the default glib main loop runs. As mentioned above, the generator itself will run in its own thread.

Exceptions may propagate out of this procedure if they arise while setting up (that is, before the worker thread starts), which shouldn't happen unless memory is exhausted or pthread has run out of resources. Exceptions arising during execution of the generator, if not caught by a handler procedure, will terminate the program. Exceptions thrown by the handler procedure will propagate out of g-main-loop-run. Exceptions thrown by 'proc', if not caught locally, will also propagate out of g-main-loop-run.

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

Here is an example of the use of await-glib-generator-in-thread:

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
          (await-glib-generator-in-thread await resume
                                          (lambda (yield)
                                            (let loop ((count 0))
                                              (when (< count 5)
                                                (yield (* 2 count))
                                                (loop (1+ count)))))
                                          (lambda (val)
                                            (display val)
                                            (newline)))
          (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-generator await resume generator proc)

This is a convenience procedure for acting asynchronously on values yielded by generator procedures. The 'generator' argument is a procedure taking one argument, namely a yield argument (see the documentation on the make-iterator procedure for further details). This await-glib-generator procedure will run 'generator', and whenever 'generator' yields a value will cause 'proc' to execute in the default glib main loop - each time 'proc' runs it will do so as a separate event in the main loop and so be multi-plexed with other events. 'proc' should be a procedure taking a single argument, namely the value yielded by the generator.

This procedure is intended to be called in a waitable procedure invoked by a-sync. It is the single-threaded corollary of await-glib-generator-in-thread. This means that (unlike with await-glib-generator-in-thread) while 'generator' is running other events in the main loop will not make progress, so blocking calls (other than to the yield procedure) should not be made in 'generator'.

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

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).

Exceptions may propagate out of this procedure if they arise while setting up (that is, before the task starts), which shouldn't happen unless memory is exhausted. Exceptions arising during execution of the generator, if not caught locally, will propagate out of await-glib-generator. Exceptions thrown by 'proc', if not caught locally, will propagate out of g-main-loop-run.

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

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

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
          (await-glib-generator await resume
                                (lambda (yield)
                                  (let loop ((count 0))
                                    (when (< count 5)
                                      (yield (* 2 count))
                                      (loop (1+ count)))))
                                (lambda (val)
                                  (display val)
                                  (newline)))
          (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-timeout await resume msecs thunk)

This is a convenience procedure for use with a glib main loop, which will run 'thunk' in the default glib main loop when the timeout expires. This procedure calls 'await' and will return the thunk's return value. It is intended to be called in a waitable procedure invoked by a-sync. The timeout is single shot only - as soon as 'thunk' has run once and completed, the timeout will be removed from the event loop.

In practice, calling await-glib-sleep may often be more convenient for composing asynchronous code than using this procedure. That is because, when 'thunk' is executed, this procedure is waiting on 'await', so 'await' and 'resume' cannot be used again in 'thunk' (although 'thunk' can call a-sync to start another series of asynchronous operations with a new await-resume pair). In retrospect, this procedure offers little over await-glib-sleep.

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 may propagate out of this procedure if they arise while setting up (that is, before the first call to 'await' is made), which shouldn't happen unless memory is exhausted. Exceptions thrown by 'thunk', if not caught locally, will propagate out of g-main-loop-run.

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

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

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
          (simple-format #t
                         "Timeout ~A\n"
                         (await-glib-timeout await resume
                                             100
                                             (lambda ()
                                               "expired")))
          (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-sleep await resume msecs)

This is a convenience procedure for use with a glib main loop, which will suspend execution of code in the current a-sync or compose-a-sync block for the duration of 'msecs' milliseconds. The event loop will not be blocked by the sleep - instead any other events in the event loop (including any other a-sync or compose-a-sync blocks) will be serviced. It is intended to be called within a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments).

Calling this procedure is equivalent to calling await-glib-timeout with a 'proc' argument comprising a lambda expression that does nothing.

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

This procedure should not throw any exceptions unless memory is exhausted.

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

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

(define main-loop (g-main-loop-new #f #f))
(a-sync (lambda (await resume)
          (display "Entering sleep\n")
          (await-glib-sleep await resume 500)
          (display "Timeout expired\n")
          (a-sync-glib-quit main-loop)))
(g-main-loop-run main-loop)

(await-glib-task-in-thread-pool await resume pool thunk [handler])

This is a convenience procedure for use with a glib main loop, which will run 'thunk' in the thread pool specified by the 'pool' argument (see (a-sync thread-pool)). The result of executing 'thunk' will then be posted to the default glib main loop, and will comprise this procedure's return value. This procedure is intended to be called within a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments).

If the optional 'handler' argument is provided, then that handler will run if 'thunk' throws, and the return value of the handler would become the return value of this procedure; otherwise the program will terminate if an unhandled exception propagates out of 'thunk'. 'handler' should take the same arguments as a guile catch handler (this is implemented using catch). Note that unlike a handler passed to the thread-pool-add! procedure, 'handler' will run in the default glib main loop thread and not in a thread pool thread. Exceptions thrown by the handler procedure will propagate out of g-main-loop-run.

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

Exceptions may propagate out of this procedure if they arise while setting up, which shouldn't happen unless the thread pool given by the 'pool' argument has been closed (in which case a 'thread-pool-error exception will arise), the thread pool tries to start an additional native thread which the operating system fails to supply (which would cause a system exception to arise) or memory is exhausted.

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

Here is an example of the use of await-glib-task-in-thread-pool:

(define main-loop (g-main-loop-new #f #f))
(let ((pool (make-thread-pool #:max-threads 4)))
  (a-sync (lambda (await resume)
	    (simple-format #t "1 + 1 is ~A\n"
                           (await-glib-task-in-thread-pool await resume
                                                           pool
                                                           (lambda ()
                                                             (+ 1 1))))
            (a-sync-glib-quit main-loop))))
(g-main-loop-run main-loop)

(await-glib-generator-in-thread-pool await resume pool generator proc [handler])

The 'generator' argument is a procedure taking one argument, namely a yield argument (see the documentation on the make-iterator procedure for further details). This await-glib-generator-in-thread-pool procedure will cause 'generator' to run as a task in the 'pool' thread pool (see (a-sync thread-pool)), and whenever 'generator' yields a value this will cause 'proc' to execute in the default glib main loop. 'proc' should be a procedure taking a single argument, namely the value yielded by the generator.

This procedure is intended to be called within a waitable procedure invoked by a-sync (which supplies the 'await' and 'resume' arguments).

If the optional 'handler' argument is provided, then that handler will run if 'generator' throws an exception; otherwise the program will terminate if an unhandled exception propagates out of 'generator'. 'handler' should take the same arguments as a guile catch handler (this is implemented using catch). Note that unlike a handler passed to the thread-pool-add! procedure, 'handler' will run in the default glib main loop thread and not in a thread pool thread. This procedure will return #f if the generator completes normally, or 'guile-a-sync-thread-error if the generator throws an exception and 'handler' is run (the 'guile-a-sync-thread-error symbol is reserved to the implementation and should not be yielded by the generator). Exceptions thrown by the handler procedure will propagate out of g-main-loop-run.

This procedure calls 'await' and will return when the generator has finished or, if 'handler' is provided, upon the generator raising an exception. 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 may propagate out of this procedure if they arise while setting up, which shouldn't happen unless the thread pool given by the 'pool' argument has been closed (in which case a 'thread-pool-error exception will arise), the thread pool tries to start an additional native thread which the operating system fails to supply (which would cause a system exception to arise) or memory is exhausted. Exceptions arising during the execution of 'proc', if not caught locally, will propagate out of g-main-loop-run.

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

Here is an example of the use of await-glib-generator-in-thread-pool:

(define main-loop (g-main-loop-new #f #f))
(let ((pool (make-thread-pool #:max-threads 4)))
  (a-sync (lambda (await resume)
            (await-glib-generator-in-thread-pool await resume
                                                 pool
                                                 (lambda (yield)
                                                   (let loop ((count 0))
                                                     (when (< count 5)
                                                       (yield (* 2 count))
                                                       (loop (1+ count)))))
                                                 (lambda (val)
                                                   (display val)
                                                   (newline)))
            (a-sync-glib-quit main-loop))))
(g-main-loop-run main-loop)