From: Eric Cooper <ecc@cmu.edu>
To: guile-user@gnu.org
Subject: Re: Guile continuations
Date: Fri, 21 Aug 2009 23:08:24 -0400 [thread overview]
Message-ID: <20090822030824.GD7342@localhost> (raw)
In-Reply-To: <3ae3aa420908211653k78e79c9bsb321acfbabbae663@mail.gmail.com>
On Fri, Aug 21, 2009 at 06:53:29PM -0500, Linas Vepstas wrote:
> I'm trying to find an elegant way of multi-threading between C
> and scheme code, without actually using threads... Basically,
> I want to do some scheme stuff for a while, break off, run some
> C code for a while, then resume the scheme execution where
> I left off.
You can implement coroutines using call/cc, and the coroutines can be
either Scheme or C code.
Note that this only works if the threads explicitly call (yield) -- it
won't do the "right thing" when a thread does some blocking I/O
operation. For that, you either need to define alternate versions of
those potentially blocking operations, or switch to real
(pre-emptively scheduled) POSIX threads.
Here's a simple proof of concept:
;;; A naive queue for thread scheduling.
(define *queue* '())
(define (empty-queue?)
(null? *queue*))
(define (enqueue x)
(set! *queue* (append *queue* (list x))))
(define (dequeue)
(let ((x (car *queue*)))
(set! *queue* (cdr *queue*))
x))
;;; This starts a new thread running (proc).
(define (fork proc)
(call/cc
(lambda (k)
(enqueue k)
(proc))))
;;; This yields the processor to another thread, if there is one.
(define (yield)
(call/cc
(lambda (k)
(enqueue k)
((dequeue)))))
;;; This terminates the current thread, or the entire program
;;; if there are no other threads left.
(define (thread-exit)
(if (empty-queue?)
(exit)
((dequeue))))
;;; The body of a Scheme thread:
(define (printer str)
(lambda ()
(let loop ((n 0))
(format #t "~A (~A)\n" str n)
(yield)
(loop (1+ n)))))
The C code:
#include <libguile.h>
SCM yield;
SCM printer() {
int n = 0;
for (;;) {
printf("C thread (%d)\n", n);
scm_call_0(yield);
n += 1;
}
}
void init_printer() {
yield = scm_c_eval_string("yield");
scm_c_define_gsubr("c-printer", 0, 0, 0, printer);
}
Compile this as a shared library:
gcc -shared -o printer.so -fPIC printer.c
Then run guile with the shared library accessible to it:
$ LTDL_LIBRARY_PATH=. guile
guile> (load "threads.scm")
guile> (fork (printer "Scheme!"))
Scheme! (0)
guile> (load-extension "printer" "init_printer")
guile> (fork c-printer)
C thread (0)
Scheme! (1)
guile> (thread-exit)
C thread (1)
Scheme! (2)
C thread (2)
Scheme! (3)
C thread (3)
....
I hope this helps (and that I haven't completely misunderstood your
question.)
--
Eric Cooper e c c @ c m u . e d u
next prev parent reply other threads:[~2009-08-22 3:08 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-08-21 23:53 Guile continuations Linas Vepstas
2009-08-22 3:08 ` Eric Cooper [this message]
2009-08-22 15:26 ` Linas Vepstas
2009-08-22 16:01 ` Eric Cooper
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=20090822030824.GD7342@localhost \
--to=ecc@cmu.edu \
--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).