From: Julian Stecklina
Subject: conditions vs. call/cc
Date: 
Message-ID: <86hdpm2vwa.fsf@goldenaxe.localnet>
Hello,

I want to write a function that reads from file descriptors, but does
not block. But it would be nice, if I could write my program like a
blocking one, as this is far simpler. What I need is some kind of
non-local jumping from here to there. I imagine something like
(misusing the condition system functions a bit):

(defun safe-read (fd length)
  (signal 'possibly-blocking-read :fd fd)
  (real-read fd length))

And in the event loop there would be:

(with-handlers ((possibly-blocking-read (condition)
		  (register-reading-possible-event (fd condition)
						   (lambda ()
						     (continue condition)))))
  (loop for event = (get-event)
	do (handle-event)))

How would I do this in "real" Common Lisp?

I know next to nothing about call/cc, but it sounds like a good job
for it. I recently discovered arnesi on common-lisp.net that includes
an implementation of (part of?) call/cc, but it lacks some small
examples (or I missed them). Is my problem something this call/cc
implementation could solve?

Regards,
-- 
                    ____________________________
 Julian Stecklina  /  _________________________/
  ________________/  /
  \_________________/  LISP - truly beautiful

From: Russell McManus
Subject: Re: conditions vs. call/cc
Date: 
Message-ID: <87sm950xj9.fsf@thelonious.dyndns.org>
Julian Stecklina <··········@web.de> writes:

> I want to write a function that reads from file descriptors, but
> does not block. But it would be nice, if I could write my program
> like a blocking one, as this is far simpler. What I need is some
> kind of non-local jumping from here to there. I imagine something
> like (misusing the condition system functions a bit):

My recommendation is to do one of two things:

1. Use an implementation with real threads, like Lispworks, and then
just write blocking IO code and "be happy".

2. Use an implementation with a friendly layer on top of the 'select'
system call like cmucl, and program in continuation passing style.

For example:

(write-buffer socket-stream
              string
              (lambda (condition)
                (if condition
                    (handle-failure condition)
                    (continue-computation-here))))

Implementing 'write-buffer' in a non-blocking fashion on top of cmucl
is not hard.  Let me know if you want to explore this.  I think that
scheme's call/cc is overkill for this problem.

Of course the above can be macro-fied to have a more pleasing
interface, such that when you're reading the code, you don't see the
continuation passing.

-russ
From: marco
Subject: Re: conditions vs. call/cc
Date: 
Message-ID: <m2llexuev5.fsf@bese.it>
Russell McManus <···············@yahoo.com> writes:

> Of course the above can be macro-fied to have a more pleasing
> interface, such that when you're reading the code, you don't see the
> continuation passing.

use the with-call/cc macro from arnesi:

(with-call/cc 
  (unless (write-buffer socket-stream string)
    (handle-failure condition))
  (continue-as-normal))

after having defined write-buffer as:

(defun/cc write-buffer (socket-stream string)
  (let/cc k
    (select-and-call-continuation socket-stream string k)))

someone should think about the ability to transform event (or polling)
based code into "linear" code via cps.

-- 
-Marco
Ring the bells that still can ring.
Forget your perfect offering.
There is a crack in everything.
That's how the light gets in.
     -Leonard Cohen
From: Julian Stecklina
Subject: Re: conditions vs. call/cc
Date: 
Message-ID: <863c14hq40.fsf@goldenaxe.localnet>
marco <··@bese.it> writes:

> Russell McManus <···············@yahoo.com> writes:
>
>> Of course the above can be macro-fied to have a more pleasing
>> interface, such that when you're reading the code, you don't see the
>> continuation passing.
>
> use the with-call/cc macro from arnesi:
>
> (with-call/cc 
>   (unless (write-buffer socket-stream string)
>     (handle-failure condition))
>   (continue-as-normal))
>
> after having defined write-buffer as:
>
> (defun/cc write-buffer (socket-stream string)
>   (let/cc k
>     (select-and-call-continuation socket-stream string k)))
>

Ok, I will play with that tomorrow. Hopefully this solves my
problem. Is there anything one should be aware off when using this
call/cc implementation?

Regards,
-- 
                    ____________________________
 Julian Stecklina  /  _________________________/
  ________________/  /
  \_________________/  LISP - truly beautiful
From: Julian Stecklina
Subject: Re: conditions vs. call/cc
Date: 
Message-ID: <867jqghqc2.fsf@goldenaxe.localnet>
Russell McManus <···············@yahoo.com> writes:

> 1. Use an implementation with real threads, like Lispworks, and then
> just write blocking IO code and "be happy".

LispWorks is not available for FreeBSD. That leaves AllegroCL, but if
I ever purchase a license for it, it will be for Windoze (because of
job-related issues).

> 2. Use an implementation with a friendly layer on top of the 'select'
> system call like cmucl, and program in continuation passing style.

But kqueues have many, many nice features (like monitoring fds,
processes, network devices...) that I like to use in my other
projects, so a nice non-blocking interface to sockets/files via kqueue
would be an overall win for me.

> Implementing 'write-buffer' in a non-blocking fashion on top of cmucl
> is not hard.  Let me know if you want to explore this.  I think that
> scheme's call/cc is overkill for this problem.

It would be very helpful if you could point me to some small
application that uses cps.

Regards,
-- 
                    ____________________________
 Julian Stecklina  /  _________________________/
  ________________/  /
  \_________________/  LISP - truly beautiful
From: Russell McManus
Subject: Re: conditions vs. call/cc
Date: 
Message-ID: <873c13rjnu.fsf@thelonious.dyndns.org>
Julian Stecklina <··········@web.de> writes:

> LispWorks is not available for FreeBSD. That leaves AllegroCL, but if
> I ever purchase a license for it, it will be for Windoze (because of
> job-related issues).

Have you tried running the Linux version on FreeBSD with Linux binary
emulation enabled?  I've used Lispworks successfully under NetBSD in
this way. YMMV.

And finally, yes, kqueues are cool.

-russ
From: Rahul Jain
Subject: Re: conditions vs. call/cc
Date: 
Message-ID: <878yawwzuj.fsf@nyct.net>
Julian Stecklina <··········@web.de> writes:

> Hello,
>
> I want to write a function that reads from file descriptors, but does
> not block. But it would be nice, if I could write my program like a
> blocking one, as this is far simpler. What I need is some kind of
> non-local jumping from here to there.

This sounds like CMUCL's SERVE-EVENT.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: Julian Stecklina
Subject: Re: conditions vs. call/cc
Date: 
Message-ID: <86y8iv50h4.fsf@goldenaxe.localnet>
Rahul Jain <·····@nyct.net> writes:

> Julian Stecklina <··········@web.de> writes:
>
>> Hello,
>>
>> I want to write a function that reads from file descriptors, but does
>> not block. But it would be nice, if I could write my program like a
>> blocking one, as this is far simpler. What I need is some kind of
>> non-local jumping from here to there.
>
> This sounds like CMUCL's SERVE-EVENT.

The problem is to write non-blocking I/O as if it were blocking. So
you would have the advantages of non-blocking I/O together with the
simplicity of blocking I/O. serve-event cannot do this. My kqueue
interface cannot do this either. Basically it does quite the same as
serve-event: You can register events for file descriptors and when
this event happens, your callback is called.

Regards,
-- 
                    ____________________________
 Julian Stecklina  /  _________________________/
  ________________/  /
  \_________________/  LISP - truly beautiful
From: Rob Warnock
Subject: Re: conditions vs. call/cc
Date: 
Message-ID: <JdCdnUVi_5Y1QMXcRVn-qA@speakeasy.net>
Julian Stecklina  <··········@web.de> wrote:
+---------------
| Rahul Jain <·····@nyct.net> writes:
| > Julian Stecklina <··········@web.de> writes:
| >> I want to write a function that reads from file descriptors, but does
| >> not block. But it would be nice, if I could write my program like a
| >> blocking one, as this is far simpler. What I need is some kind of
| >> non-local jumping from here to there.
| >
| > This sounds like CMUCL's SERVE-EVENT.
| 
| The problem is to write non-blocking I/O as if it were blocking. So
| you would have the advantages of non-blocking I/O together with the
| simplicity of blocking I/O. serve-event cannot do this. My kqueue
| interface cannot do this either. Basically it does quite the same as
| serve-event: You can register events for file descriptors and when
| this event happens, your callback is called.
+---------------

Perhaps the default I/O provided with CMUCL's "MP" (multiprogramming,
i.e., green threads) package [*not* SERVE-EVENT] might be what you want.
The main issue is that you have to spawn a separate thread (what "MP"
calls a "process") for each instance of a read/write loop that you want
to be independent of other such instances. For example, in a web server
you might spawn a separate thread for each HTTP request, and then use
(apparently) "blocking" I/O to service that request, while under the hood
CMUCL is actually using non-blocking I/O [and Unix "select()"] and doing
thread-switching for you as needed.

I have a web application server I wrote in CMUCL that uses that approach,
and it seems to work just fine. If one thread's I/O is slow (because one
remote client is using a slow dialup line) the other threads (HTTP requests)
continue to run at speed. And by calling ENSURE-IDLE-PROCESS-RUNNING
during the initialization of your app, the top-level REPL *also* runs as
a separate thread.


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Julian Stecklina
Subject: Re: conditions vs. call/cc
Date: 
Message-ID: <86y8iukfk4.fsf@goldenaxe.localnet>
····@rpw3.org (Rob Warnock) writes:

> Perhaps the default I/O provided with CMUCL's "MP" (multiprogramming,
> i.e., green threads) package [*not* SERVE-EVENT] might be what you
> want.

Possibly. I'll consider this. It would be nice to use SBCL, but it
does not support multiple threads of execution on FreeBSD. Playing
around with continuations has a higher priority for now. I'll see,
what is the least painful way to get something done. :)

Regards,
-- 
                    ____________________________
 Julian Stecklina  /  _________________________/
  ________________/  /
  \_________________/  LISP - truly beautiful