From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Eric Cooper Newsgroups: gmane.lisp.guile.user Subject: Re: Guile continuations Date: Fri, 21 Aug 2009 23:08:24 -0400 Message-ID: <20090822030824.GD7342@localhost> References: <3ae3aa420908211653k78e79c9bsb321acfbabbae663@mail.gmail.com> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: ger.gmane.org 1250910538 691 80.91.229.12 (22 Aug 2009 03:08:58 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sat, 22 Aug 2009 03:08:58 +0000 (UTC) To: guile-user@gnu.org Original-X-From: guile-user-bounces+guile-user=m.gmane.org@gnu.org Sat Aug 22 05:08:51 2009 Return-path: Envelope-to: guile-user@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1MegyI-0002qD-Oc for guile-user@m.gmane.org; Sat, 22 Aug 2009 05:08:51 +0200 Original-Received: from localhost ([127.0.0.1]:55909 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MegyI-0007qY-2z for guile-user@m.gmane.org; Fri, 21 Aug 2009 23:08:50 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Megy2-0007q4-5P for guile-user@gnu.org; Fri, 21 Aug 2009 23:08:34 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Megxx-0007nj-Jy for guile-user@gnu.org; Fri, 21 Aug 2009 23:08:33 -0400 Original-Received: from [199.232.76.173] (port=36916 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Megxx-0007ng-Ev for guile-user@gnu.org; Fri, 21 Aug 2009 23:08:29 -0400 Original-Received: from smtp03.srv.cs.cmu.edu ([128.2.217.198]:51573) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1Megxw-00033w-T9 for guile-user@gnu.org; Fri, 21 Aug 2009 23:08:29 -0400 Original-Received: from stratocaster.home (pool-96-235-26-179.pitbpa.fios.verizon.net [96.235.26.179]) (authenticated bits=0) by smtp03.srv.cs.cmu.edu (8.13.6/8.13.6) with ESMTP id n7M38OpX027390 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NO) for ; Fri, 21 Aug 2009 23:08:25 -0400 (EDT) Original-Received: from ecc by stratocaster.home with local (Exim 4.69) (envelope-from ) id 1Megxs-0003LP-GW for guile-user@gnu.org; Fri, 21 Aug 2009 23:08:24 -0400 Mail-Followup-To: guile-user@gnu.org Content-Disposition: inline In-Reply-To: <3ae3aa420908211653k78e79c9bsb321acfbabbae663@mail.gmail.com> User-Agent: Mutt/1.5.20 (2009-06-14) X-Scanned-By: mimedefang-cmuscs on 128.2.217.198 X-detected-operating-system: by monty-python.gnu.org: Genre and OS details not recognized. X-BeenThere: guile-user@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: General Guile related discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: guile-user-bounces+guile-user=m.gmane.org@gnu.org Errors-To: guile-user-bounces+guile-user=m.gmane.org@gnu.org Xref: news.gmane.org gmane.lisp.guile.user:7416 Archived-At: 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 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