<div>Hi Arnar,</div>
<div>&nbsp;</div>
<div>Am I right, that your answer to (1) is that Tasklet L is the one with &quot;event.loop(TRUE)&quot; and that that is the call that continually checks readiness for other tasklets?<br>&nbsp;</div>
<div>The &quot;elaborate handler when select comes out of the block&quot; is really pretty trivial, and was done decades ago by Inmos - who recorded a bit-by-bit description of their absolutely robust implementation. They actually solved a tougher problem, which was to let you code a multi-way branch in a single process&nbsp;(not just select the winning process with one stimulus to each, as we have been discussing), and included timers too.</div>

<div>&nbsp;</div>
<div>Your implementation of time.sleep looks like it&#39;s interrupt-based, so I guess you have a fast clock on your motherboard, unless C select is coded pretty weirdly.</div>
<div>&nbsp;</div>
<div>This particular kind of interrupts and locking code is not as hard as you think, because it only has to serialize the queue manipulation for something you completely control - the Stackless virtual machine. I did it in DOS ages ago, and the Inmos documentation exhaustively lists what is needed, which isn&#39;t much&nbsp;(currently the &quot;Transterpreter&quot; project works off that). It&#39;s your call, of course, but I think readiness queueing is much cleaner without requiring a timer poll loop.</div>

<div>&nbsp;</div>
<div>Larry<br>&nbsp;</div>
<div><span class="gmail_quote">On 10/3/08, <b class="gmail_sendername">Arnar Birgisson</b> &lt;<a href="mailto:arnarbi@gmail.com">arnarbi@gmail.com</a>&gt; wrote:</span>
<blockquote class="gmail_quote" style="PADDING-LEFT: 1ex; MARGIN: 0px 0px 0px 0.8ex; BORDER-LEFT: #ccc 1px solid">Hi Larry,<br><br>On Fri, Oct 3, 2008 at 19:52, Larry Dickson &lt;<a href="mailto:ldickson@cuttedge.com">ldickson@cuttedge.com</a>&gt; wrote:<br>
&gt; Two questions:<br>&gt;<br>&gt; (1) Why does it wait for the first round the dispatcher tasklet runs? Why<br>&gt; doesn&#39;t the callback just get chained off the interrupt that responds to the<br>&gt; readiness of the IO? The main tasklet got PAST the call to<br>
&gt; event.notify_when_ready_for_op and is blocked on the receive, so is there<br>&gt; another tasklet that just keeps trying for readiness every time the round<br>&gt; robin comes back?<br><br>You cheated, that&#39;s three questions right there :D<br>
<br>Interrupts are just as hard to manage as preemptive scheduling. You<br>need all kinds of locking primitives etc. in your code if you rely on<br>interrupts, in which case you shouldn&#39;t be using libevent at all (it<br>
serves an entirely different purpose). That is why libevent must rely<br>on a loop that is either continuously running (and you do everything<br>else in event handlers) or your application must take care of calling<br>the dispatcher regularly. In my code, there is a dedicated tasklet for<br>
this, I guess that qualifies as the tasklet you refer to that checks<br>for readiness.<br><br>I don&#39;t really know how to explain this more clearly than in my last<br>message. I&#39;ll give it one more go, otherwise someone more elaborate<br>
than me needs to step in.<br><br>Say tasklet A wants to read from a FD, and tasklet L is running the<br>libevent distpatcher regularly.<br><br>* Tasklet A asks libevent to call a specific function when the FD is ready.<br>
<br>* Tasklet A defines this function and has it perform the read and<br>write the output on a channel. The function is not executed by tasklet<br>A.<br><br>* Tasklet A does a .receive() on said channel, and thus blocks.<br>
<br>* Tasklet L now runs, but there are no IO events available, if there<br>are other tasklets it simply puts itself at the back of the queue.<br><br>* Other tasklets run, not A, because it is still blocked<br><br>* Tasklet L runs again, now there is an event ready with an attached callback<br>
<br>* Tasklet L (specifically the libeven dispatcher) calls the callback,<br>so it is really tasklet L that performs the actual read and sends the<br>result on the channel, marking tasklet A as runnable<br><br>* Tasklet A runs and picks the value off the channel, returning it to<br>
the calling code.<br><br>I think you need to study conventional asynchronous I/O, such as<br>asyncore, Twisted, epoll &amp; friends etc. Then my code should be pretty<br>easy to understand.<br><br>&gt; (2) If libevent has nothing to do with Stackless, how can its callback<br>
&gt; perform a Stackless function (the channel send)?<br><br>I don&#39;t follow. The callback is just a regular python function and can<br>do whatever python functions can do, including sending stuff on<br>channels.<br><br>
&gt; A non-blocking select is just a blocking select that always happens to be<br>&gt; ready (because of the 0 timeout). The blocking select does not have a<br>&gt; timeout (unless that is explicitly coded), but it selects on ALL the<br>
&gt; currently outstanding IOs. Note that a select does not actually do the IO,<br>&gt; but it returns when at least one of them is ready. In your case, the<br>&gt; blocking select on both FD1 and FD2 would be won by FD1, and Tasklet A would<br>
&gt; wake up able to read the memory-mapped file immediately.<br><br>Ah, in that case you need an elaborate handler when the select comes<br>out of the block, to resume the correct tasklet depending on which<br>operation should be performed etc. This is one of the things libevent<br>
handles completely transparently.<br><br>&gt; No, I mean milliseconds. It would seem that either your time.sleep is using<br>&gt; some other mechanism than a timer interrupt, or you have faster timer ticks<br>&gt; (10 usec?) than my motherboard. You might want to try C/Linux nanosleep or<br>
&gt; usleep and see if you get the same result. And/or do a longer Stackless test<br>&gt; and watch CPU usage, just in case Stackless is using a busy loop.<br><br>I millisecond is pretty long for a computer. Unfortunately I don&#39;t<br>
have time to verify this, but if you do I&#39;d be interested to see the<br>results. Here is the implementation of time.sleep for linux (in fact,<br>any platform that has select so this probably includes OS-X also):<br><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct timeval t;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; double frac;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frac = fmod(secs, 1.0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; secs = floor(secs);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.tv_sec = (long)secs;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.tv_usec = (long)(frac*1000000.0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Py_BEGIN_ALLOW_THREADS<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &amp;t) != 0) {<br>#ifdef EINTR<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (errno != EINTR) {<br>#else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (1) {<br>#endif<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Py_BLOCK_THREADS<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PyErr_SetFromErrno(PyExc_IOError);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Py_END_ALLOW_THREADS<br><br>You can see other platforms in [1], the function is called &quot;floatsleep&quot;.<br>
<br>[1] <a href="http://svn.python.org/view/python/trunk/Modules/timemodule.c?rev=64745&amp;view=auto">http://svn.python.org/view/python/trunk/Modules/timemodule.c?rev=64745&amp;view=auto</a><br><br>cheers,<br>Arnar<br></blockquote>
</div><br>