<div>Hi Arnar,</div>
<div> </div>
<div>Am I right, that your answer to (1) is that Tasklet L is the one with "event.loop(TRUE)" and that that is the call that continually checks readiness for other tasklets?<br> </div>
<div>The "elaborate handler when select comes out of the block" 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 (not just select the winning process with one stimulus to each, as we have been discussing), and included timers too.</div>
<div> </div>
<div>Your implementation of time.sleep looks like it's interrupt-based, so I guess you have a fast clock on your motherboard, unless C select is coded pretty weirdly.</div>
<div> </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't much (currently the "Transterpreter" project works off that). It's your call, of course, but I think readiness queueing is much cleaner without requiring a timer poll loop.</div>
<div> </div>
<div>Larry<br> </div>
<div><span class="gmail_quote">On 10/3/08, <b class="gmail_sendername">Arnar Birgisson</b> <<a href="mailto:arnarbi@gmail.com">arnarbi@gmail.com</a>> 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 <<a href="mailto:ldickson@cuttedge.com">ldickson@cuttedge.com</a>> wrote:<br>
> Two questions:<br>><br>> (1) Why does it wait for the first round the dispatcher tasklet runs? Why<br>> doesn't the callback just get chained off the interrupt that responds to the<br>> readiness of the IO? The main tasklet got PAST the call to<br>
> event.notify_when_ready_for_op and is blocked on the receive, so is there<br>> another tasklet that just keeps trying for readiness every time the round<br>> robin comes back?<br><br>You cheated, that'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'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't really know how to explain this more clearly than in my last<br>message. I'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 & friends etc. Then my code should be pretty<br>easy to understand.<br><br>> (2) If libevent has nothing to do with Stackless, how can its callback<br>
> perform a Stackless function (the channel send)?<br><br>I don'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>
> A non-blocking select is just a blocking select that always happens to be<br>> ready (because of the 0 timeout). The blocking select does not have a<br>> timeout (unless that is explicitly coded), but it selects on ALL the<br>
> currently outstanding IOs. Note that a select does not actually do the IO,<br>> but it returns when at least one of them is ready. In your case, the<br>> blocking select on both FD1 and FD2 would be won by FD1, and Tasklet A would<br>
> 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>> No, I mean milliseconds. It would seem that either your time.sleep is using<br>> some other mechanism than a timer interrupt, or you have faster timer ticks<br>> (10 usec?) than my motherboard. You might want to try C/Linux nanosleep or<br>
> usleep and see if you get the same result. And/or do a longer Stackless test<br>> 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't<br>
have time to verify this, but if you do I'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>
struct timeval t;<br> double frac;<br> frac = fmod(secs, 1.0);<br> secs = floor(secs);<br> t.tv_sec = (long)secs;<br> t.tv_usec = (long)(frac*1000000.0);<br> Py_BEGIN_ALLOW_THREADS<br>
if (select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t) != 0) {<br>#ifdef EINTR<br> if (errno != EINTR) {<br>#else<br> if (1) {<br>#endif<br> Py_BLOCK_THREADS<br>
PyErr_SetFromErrno(PyExc_IOError);<br> return -1;<br> }<br> }<br> Py_END_ALLOW_THREADS<br><br>You can see other platforms in [1], the function is called "floatsleep".<br>
<br>[1] <a href="http://svn.python.org/view/python/trunk/Modules/timemodule.c?rev=64745&view=auto">http://svn.python.org/view/python/trunk/Modules/timemodule.c?rev=64745&view=auto</a><br><br>cheers,<br>Arnar<br></blockquote>
</div><br>