[Stackless] Stackless based replacement

Larry Dickson ldickson at cuttedge.com
Fri Oct 3 01:01:02 CEST 2008


On 10/2/08, Arnar Birgisson <arnarbi at gmail.com> wrote:
>
> Hi Larry,
>
> On Fri, Oct 3, 2008 at 00:06, Larry Dickson <ldickson at cuttedge.com> wrote:
> > If I'm following you right, asynchronous IO is not needed. All that is
> > needed is a loop on a non-blocking select
>
> Non-blocking select is one form of asynchronous IO, right? Maybe we
> differ in terminology here.


Possibly. It requires no drivers other than the regular ones. It takes
advantage of the fact (at least as determined by experiment in C and Linux)
that a timer has lower priority in a select than does an IO, in other words,
the IO gets checked first.

int response;
fd_set set;
struct timeval notime = {0,0};
FD_ZERO(&set);
FD_SET(fd,&set);
response = select(fd+1, &set, NULL, NULL, &notime);

It checks fd first, and returns positive if fd is ready. Otherwise, WITHOUT
WAITING, it returns 0.

> (or, to avoid busyness, a select
> > against the IO and one tick later than the last time noted by the loop).
> If
> > you can put code into the virtual machine, it can be even cleaner: an
> > unconditional swap out of the tasklet; whenever its turn comes up in the
> > round robin, an unblocking select on its IO; and whenever all tasklets
> are
> > waiting on IO, a blocking select on all outstanding IO.
> >
> > By dealing with the selects alone, you can emulate the occam ALT
> primitive
> > which says "swap out until any of this list come ready." Old Inmos
> > documentation shows how to get the state machine exactly right for that,
> > including timers.
>
> Instead of reinventing the wheel here, I'm in favour of using
> mechanism that already handles this kind of thing. libevent and its
> Python bindings pyevent is one. Regular files and network streams can
> be (rudimentarily) emulated with some simple wrappers on channels (not
> that this does timers too):
>
> import time
>
> import stackless
> import event
>
> def _loop():
>    while _loop.running:
>        event.loop(True)
>        if stackless.getruncount() == 1:
>            time.sleep(0.0001)
>        stackless.schedule()
> _loop.running = False
>
> def init(interval=0.0001):
>    """Initializes the libevent system and starts a tasklet that executes
>    the libevent dispatcher on every round."""
>    if _loop.running: return
>    _loop.running = True
>    stackless.tasklet(_loop)()
>
> def stop():
>    """Stops the libevent dispatcher tasklet."""
>    if not _loop.running: return
>    _loop.running = False
>
> def sleep(seconds):
>    """Sleeps the current tasklet for the specified number of seconds"""
>    ch = stackless.channel()
>    event.timeout(seconds, ch.send, None)
>    ch.receive()
>
> class ssfile(file):
>
>    def __init__(self, *args, **kwargs):
>        self.rch = stackless.channel()
>        self.wch = stackless.channel()
>        super(ssfile, self).__init__(*args, **kwargs)
>
>    def read(self, *args):
>        def cb():
>            self.rch.send(super(ssfile, self).read(*args))
>        event.read(self.fileno(), cb)
>        return self.rch.receive()
>
>    def write(self, *args):
>        def cb():
>            self.wch.send(super(ssfile, self).write(*args))
>        event.write(self.fileno(), cb)
>        return self.wch.receive()
>
> stdfile = file
> file = ssfile
>
> import socket as stdsocket
> class ssocket(stdsocket.socket):
>
>    def __init__(self, *args, **kwargs):
>        self.sendch = stackless.channel()
>        self.recvch = stackless.channel()
>        super(ssocket, self).__init__(*args, **kwargs)
>
>    def send(self, *args, **kwargs):
>        def cb():
>            self.sendch.send(super(ssocket, self).send(*args, **kwargs))
>        event.write(self.fileno(), cb)
>        return self.sendch.receive()
>
>    def recv(self, *args, **kwargs):
>        def cb():
>            self.recvch.send(super(ssocket, self).recv(*args, **kwargs))
>        event.read(self.fileno(), cb)
>        return self.recvch.receive()
>
>    def accept(self, *args, **kwargs):
>        def cb():
>            self.recvch.send(super(ssocket, self).accept(*args, **kwargs))
>        event.read(self.fileno(), cb)
>        return self.recvch.receive()
>
> cheers,
> Arnar
>

Wow! If that's your idea of a simple solution, I'd hate to see a complicated
one ;-)

Give me a week or so and I'll figure out how that works. What I do notice is
the time.sleep(0.0001) [by the way, I believe the minimum sleep is actually
1 ms in older PCs and 2.5 ms in newer PCs]. Unless C/Linux select is
designed horribly (and it could be), my second select approach gets an
instant response as soon as the hard interrupt handler returns control to
the program. So my notion is not really reinventing the wheel - it is an
improvement. And it seems to me it is an awful lot simpler. The only danger
- like any select - would be huge numbers of simultaneously waiting IOs.

Larry Dickson
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.stackless.com/pipermail/stackless/attachments/20081002/d4f86d42/attachment.htm>


More information about the Stackless mailing list