Gary, this is pretty much what I was wondering, and I think you answered it well...it would be a lot of work and duplicated effort. To be honest, it's probably something that I would not be able to accomplish without intimate knowledge of the internals of CCL, and even then would take me a long time to do. I think for now I'll give your idea a shot and see how it fares, or use some sort of code walker that pretends to do what I want.<div>
<br></div><div>Thanks for your in-depth responses.</div><div><br><div class="gmail_quote">On Tue, Nov 13, 2012 at 2:54 AM, Gary Byers <span dir="ltr"><<a href="mailto:gb@clozure.com" target="_blank">gb@clozure.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">A lot of my motivation for trying to steer this towards basing coroutines<br>
on native threads is that it avoids reinventing a lot of wheels (thread-specific<br>
special bindings and CATCH/UNWIND-PROTECT context all involve the concept of<br>
thread-local data in CCL (and it's likely that that's true of many or all of<br>
those things in other implementations as well.) There's another set of things<br>
that I'll just call "GC integration" that basically involve how the GC views<br>
and interacts with threads and I think that this all makes me very leary of<br>
introducing some new kind of primitive non-native thread.<br>
<br>
You're right that the kind of context switch that'd occur when thread A releases<br>
a lock that thread B has been waiting for is likely to be much slower than a<br>
context switch that involves saving and restoring some registers. If it's<br>
the case that many lispy things depend native threads in subtle ways, then<br>
user-space context switch is infinitely slow (because it doesn't get you<br>
anywhere that you want to be.) The assumption that all threads are native<br>
threads isn't CCL- or Lisp-specific: a lot of C runtime functions may involve<br>
locking, and locking generally involves some notion of what the current (native)<br>
thread is.<br>
<br>
Unless you want to reimplement all of the things that depend on threads being<br>
native threads, I don't think that there's really much of an alternative to<br>
the general idea that I suggested earlier.<div class="im"><br>
<br>
On Tue, 13 Nov 2012, Andrew Lyon wrote:<br>
<br>
</div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="im">
A lot of what you said makes sense. My main goal is to replace CPS in an<br>
asynchronous application (without littering my code with cl-cont macros). So<br>
select() and poll() are what I'm using already, but transfer of control from<br>
one operation to the next around a non-blocking operation has to take place<br>
via a callback. One could build "coroutines" around real OS threads as<br>
you've laid out, but I'm guessing there would be a significant performance<br>
penalty associated (memory/context switching/etc). From my understanding,<br>
having a bunch of coroutines laying around is a lot cheaper than the same<br>
amount of OS threads in both memory and switch time. However, my<br>
understanding might be flawed...I really don't know what it would take to<br>
implement coroutines in lisp, so maybe there wouldn't be a significant<br>
amount of difference between that and using OS threads.<br>
Also, I'd like to echo as well that I don't really want "green threads"<br>
where the lisp is scheduling things for me, I'd much rather have explicit<br>
control.<br>
<br>
On Mon, Nov 12, 2012 at 10:57 PM, Gary Byers <<a href="mailto:gb@clozure.com" target="_blank">gb@clozure.com</a>> wrote:<br>
I've heard some people express interest before; I'd say that the<br>
interest<br></div>
seemed to be low-to-moderate, but non-zero. ?When it's come up,<div class="im"><br>
I think that<br>
my first reaction to hearing someone say "I want cooperative<br>
threads" is to<br>
say "no, you don't", but I may be failing to consider all<br>
aspects of the issue.<br>
<br>
One approach to layering cooperative threads on top of native<br>
threads is to<br>
use some kind of object very much like a lock; a cooperative<br>
thread is just<br>
a (native) thread that waits for that lock before doing<br>
anything, and yelding<br>
to another (unspecified) coooperative thread basically involves<br>
releasing that<br>
lock and then waiting to obtain it again.<br>
<br>
<br>
? (defvar *the-cooperative-thread-lock* (make-lock))<br>
*THE-COOPERATIVE-THREAD-LOCK*<br>
? (defun yin () (loop (with-lock-grabbed<br>
(*the-cooperative-thread-lock*<u></u>) (print "Yin!")) (sleep 1)))<br>
YIN<br>
? (defun yang () (loop (with-lock-grabbed<br>
(*the-cooperative-thread-lock*<u></u>) (print "Yang!")) (sleep 1)))<br>
YANG<br>
? (progn (process-run-function "yin" #'yin)<br>
(process-run-function "yang" #'yang))<br>
<br>
<br></div>
Yow. ?Are we COROUTINING yet ?<div class="im"><br>
<br>
That's a bit of a rhetorical question: the old stack-groups API<br>
that Scott<br>
referred to is a little richer than that and provides a clean<br>
way of transferring<br></div>
values between threads; that's left as an exercise. ?I wrote<div class="im"><br>
that in terms of<br>
WITH-LOCK-GRABBED and we might actually want to use GRAB-LOCK<br>
and RELEASE-LOCK<br>
directly, so:<br>
<br>
<br>
(defun yield-to-any-cooperative-<u></u>thread ()<br></div>
?(release-lock *the-cooperative-thread-lock*)<br>
?(grab-lock *the-cooperative-thread-lock*)<u></u>)<div class="im"><br>
<br>
That's almost suspiciously simple, but it's almost exactly what<br>
Apple did to<br>
implement traditional cooperative threads in Carbon; there are<br>
some classic<br>
problems for which coroutines provide a natural solution, and<br>
the mechanism<br>
above (augmented with some means of transferring values around)<br>
is probably<br>
adequate to address many such problems (Google for "samefringe<br>
problem" if<br>
you're looking for an example.)<br>
<br>
If we have problems for which we need more than two cooperative<br>
threads,<br>
then we may need to say "yield to some specific other<br>
cooperative thread",<br>
and that would be something like:<br>
<br>
(defun yield-to-specific-cooperative-<u></u>thread (other-guy)<br></div>
?(release-lock-and-transfer-<u></u>ownership-to<br>
*the-cooperative-thread-lock* other-guy)<br>
?(grab-lock *the-cooperative-thread-lock*)<u></u>)<div class="im"><br>
<br>
and the functionality that I'm calling<br>
RELEASE-LOCK-AND-TRANSFER-<u></u>OWNERSHIP-TO<br>
doesn't exist in CCL and is a bit hard to implement reliably.<br></div>
?(CCL locks<div class="im"><br>
generally don't keep track of which threads are waiting for them<br>
and a thread<br>
that's waiting for a lock can abandon that wait - via<br>
PROCESS-INTERRUPT - whenever<br>
it wants to, so the global lock in my example above may be<br>
something a little<br>
different from a CCL lock.)<br>
<br>
I haven't needed to solve the SAMEFRINGE problem elegantly in a<br>
long<br>
time and when I hear terms like "a blocking wrapper around<br>
non-blocking I/O" I wonder if or how that differs from things<br>
like<br></div>
#_select or #_poll. ?I'm willing to believe that there could be<div class="im"><br>
cases<br>
where coroutines (the ability to control the scheduling of a<br>
small<br></div>
number of threads relative to each other) could be useful, ?but<div><div class="h5"><br>
I think<br>
that that could be provided by fleshing out the interface that's<br>
sketched<br>
above.<br>
<br>
Lisp implememtations that provide(d) cooperative threads (I<br>
don't know<br>
of any implementations that still do so) typically provided a<br>
"lisp<br>
scheduler" on top of what I described above; that layer<br>
generally<br>
tried to do some sort of periodic preemption (so that a thread<br>
that<br>
hadn't yielded in a while was made to do so) and that layer was<br>
effectively spread all over the implementation (so that blocking<br>
operations were replaced with code which combined yielding and<br>
polling.)<br>
I would not want to see that kind of code reappear and if<br>
anyone's<br>
saying that they want that, I'm still very much at the "no, you<br>
don't"<br>
stage.<br>
<br>
<br>
On Mon, 12 Nov 2012, Andrew Lyon wrote:<br>
<br>
Hello, I'm an avid CCL user (have been for over a year<br>
now). This is my<br>
first post on the dev list, and I did a lot of research on<br>
this topic before<br>
deciding to post to make sure this hasn't been covered<br>
before.<br>
Is there any interest in the ClozureCL community in having<br>
lightweight/cooperative threading available in the<br>
implementation? I have a<br>
few problems that would be a perfect fit for this (for<br>
instance, creating a<br>
blocking interface over non-blocking IO) and I'd love to<br>
not only voice my<br>
support for the feature, but also know if anybody else<br>
would also like<br>
something like this.?<br>
<br>
I think the most ideal implementation would be where<br>
you?explicitly?give up<br>
control of the current "micro-thread" to another known thread<br>
(on top of<br>
this, something like "yield" could be built in the app itself,<br>
if needed).<br>
Matching this to the way OS threads currently work would be<br>
awesome...for<br>
instance, unwind-protect would only work for the coroutine it's<br>
wrapping<br>
around, so if you give control to another coroutine, that<br>
unwind-protect<br>
won't fire if there is an exception. Obviously this would be a<br>
big feature,<br>
and probably at least a few people would have opinions on how it<br>
would be<br>
implemented, not to mention there's probably a lot going on<br>
under the hood<br>
that I'm not aware about...but I'd like to at least open a<br>
discussion.<br>
<br>
I did try to implement coroutines outside of CCL via libpcl/CFFI<br>
(<a href="http://xmailserver.org/libpcl.html" target="_blank">http://xmailserver.org/<u></u>libpcl.html</a>) but was met with much<br>
resistance and<br>
many segfaults.<br>
<br>
Although I'm not familiar with the internals of CCL more than<br>
reading the<br>
"Internals" page and most of the docs, I'm more than happy to<br>
try getting my<br>
hands dirty and add support myself with some guidance from<br>
others (where do<br>
I start, what are the caveats, has anyone else tried this, etc).<br>
I'd also<br>
like to know if this is possible using the Virtual Instructions<br>
in the<br>
compiler.<br>
<br>
I'd love to hear anyone's thoughts on this, and thanks for the<br>
great<br>
implementation.<br>
<br>
Andrew<br>
<br>
<br>
<br>
<br>
<br>
</div></div></blockquote>
</blockquote></div><br></div>