Skip to content

guile gi meeting

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

As explained in the general overview, each 'a-sync' block (see (a-sync coroutines)) or 'compose-a-sync' block (see (a-sync compose)) is a separate unit of computation which appears within itself to proceed sequentially but which also appears to execute concurrently with other 'a-sync' or 'compose-a-sync' blocks running on the same event loop. Each 'a-sync' or 'compose-a-sync' block is therefore in some sense analogous to a thread of execution.

This (a-sync guile-gi meeting) module provides a 'glib-meeting' type which can be used to synchronize between such "pseudo-threads" (that is, between a-sync or compose-a-sync blocks) running on the default glib main loop. A 'glib-meeting' object is, in terms of communicating sequential processes, an unbuffered (synchronous) channel. Unbuffered channels in the CSP style are a natural fit for use with coroutines running on a single native thread (but not necessarily for native threads running in parallel unless combined with work stealing). They are therefore a natural fit for synchronizing the "pseudo-threads" provided by this library.

Look at the (a-sync meeting) documentation for examples of the use of meeting objects and how and when synchronization takes place, which applies also to glib-meeting objects: this is not repeated again here, save to translate the parallel-map example to glib-meeting form:

(use-modules (a-sync guile-gi base)
             (a-sync guile-gi meeting)
             (a-sync coroutines)
             (a-sync compose)
             (ice-9 match)
             (gi)
             (gi repository))

(require "GLib" "2.0")
(load-by-name "GLib" "MainLoop")

(define main-loop (main-loop:new #f #f))

(define (parallel-map await resume proc lst)
  (match lst
    (() '())
    ((head . tail)
     (let ((m1 (make-glib-meeting)))
       (compose-a-sync ((hd (await-glib-task-in-thread (lambda () (proc head)))))
                       (glib-meeting-send m1 hd))
       (let* ((tl (parallel-map await resume proc tail))
              (hd (glib-meeting-receive await resume m1)))
         (cons hd tl))))))

;; simulate a computational task which takes 1 second to complete
(define (do-work i) (sleep 1) (* i 2)) 

(a-sync
 (lambda (await resume)
   (let ((lst (parallel-map await resume do-work (list 1 2 3 4 5))))
     (display lst)(newline)
     (a-sync-glib-quit main-loop))))
(run main-loop)

The (a-sync guile-gi meeting) module provides the following procedures:


(make-glib-meeting)

This procedure makes and returns a glib-meeting object. glib-meetings are objects on which a-sync or compose-a-sync blocks running on the default glib main loop can synchronize by one passing a datum to another.

Strictly speaking this procedure can be called in any native OS thread, but since it carries out no synchronization of native threads the user would have to provide her own synchronization if called in other than the thread of the event loop with respect to which the meeting will be held; so it is best if this procedure is called in the thread of that event loop.


`(glib-meeting? obj)

This procedure indicates whether 'obj' is a glib-meeting object constructed by make-glib-meeting.


(glib-meeting-close meeting)

This closes a glib-meeting object. It's purpose is to wake up any "pseudo-thread" (that is, any a-sync or compose-a-sync block) waiting in glib-meeting-send or glib-meeting-receive by causing either procedure to return with a 'stop-iteration value.

Where that is not necessary (say, the receiver already knows how many items are to be sent), then this procedure does not need to be applied. It is not needed in order to release resources.


(glib-meeting-ready? meeting)

This indicates whether applying glib-meeting-send or glib-meeting-receive (as the case may be) to the glib-meeting object 'meeting' will return immediately: in other words, this procedure will return #t if another a-sync or compose-a-sync block is already waiting on the object or the glib-meeting object has been closed, otherwise #f.


(glib-meeting-send await resume m0 [m1 ...] datum)

This sends a datum to a receiver via one or more glib-meeting objects 'm0 m1 ...'. If no receiver is waiting for the datum, this procedure waits until a receiver calls glib-meeting-receive on one of the glib-meeting objects to request the datum. If a receiver is already waiting, this procedure passes on the datum and returns immediately.

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

Multiple senders may wait on a glib-meeting object to permit fan in. The provided datum of each sender will be passed to a receiver (as and when a receiver becomes available) in the order in which this procedure was invoked. In addition, this procedure has 'select'-like behavior: multiple glib-meeting objects may be passed and this procedure will send to the first one which becomes available to receive the datum.

Once a datum exchange has taken place, the glib-meeting object(s) can be reused for making another exchange (provided the glib-meeting objects have not been closed).

This procedure must be called in the native OS thread in which the default glib main loop runs. To have other native OS threads communicate with that loop, use await-glib-task-in-thread, await-glib-generator-in-thread, await-glib-task-in-thread-pool or await-glib-generator-in-thread-pool.

This procedure always returns #f unless glib-meeting-close has been applied to a glib-meeting object, in which case 'stop-iteration is returned. Note that if multiple glib-meeting objects are passed to this procedure and one of them is then closed, this procedure will return 'stop-iteration and any wait will be abandonned. It is usually a bad idea to close a glib-meeting object on which this procedure is waiting where this procedure is selecting on more than one glib-meeting object.


(glib-meeting-receive await resume m0 [m1 ...])

This receives a datum from a sender via one or more glib-meeting objects 'm0 m1 ...'. If no sender is waiting to pass the datum, this procedure waits until a sender calls glib-meeting-send on one of the glib-meeting objects to provide the datum. If a sender is already waiting, this procedure returns immediately with the datum supplied.

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

Multiple receivers may wait on a glib-meeting object to permit fan out. The waiting receivers will be released (as and when a sender provides a datum) in the order in which this procedure was invoked. In addition, this procedure has 'select'-like behavior: multiple glib-meeting objects may be passed and this procedure will receive from the first one which sends a datum.

Once a datum exchange has taken place, the glib-meeting object(s) can be reused for making another exchange (provided the glib-meeting objects have not been closed).

This procedure must be called in the native OS thread in which the default glib main loop runs. To have other native OS threads communicate with that loop, use await-glib-task-in-thread, await-glib-generator-in-thread, await-glib-task-in-thread-pool or await-glib-generator-in-thread-pool.

This procedure always returns the datum value supplied by glib-meeting-send unless glib-meeting-close has been applied to a glib-meeting object, in which case 'stop-iteration is returned. Note that if multiple glib-meeting objects are passed to this procedure and one of them is then closed, this procedure will return 'stop-iteration and any wait will be abandonned. It is usually a bad idea to close a glib-meeting object on which this procedure is waiting where this procedure is selecting on more than one glib-meeting object.