[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Guile continuations
From: |
Eric Cooper |
Subject: |
Re: Guile continuations |
Date: |
Fri, 21 Aug 2009 23:08:24 -0400 |
User-agent: |
Mutt/1.5.20 (2009-06-14) |
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