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>