Saturday, 21 April 2012

Why, oh why, iPad/iPod?

I've never really been a fan of either my iPad or iPod. Each of them gets one use. My iPad is used for reading ebooks, and the iPod for daily Mandarin revision with a flashcard application. They're clunky, arcane devices, hidden behind the almost unusable and arbitrary experience that is iTunes. 

Something else I have been using them for is to take advantage of free internet bandwidth. Internet in New Zealand is metered, and a gigabyte can cost something like $2 NZ. It all adds up quickly. So when someone offers free internet, instead of using their limit of 100 MB a day, I can use 300 MB a day with my three devices. I can download a podcast over a couple of days, resuming the download on additional days.

Or I could, if Apple's iTunes downloading wasn't incomprehensibly badly designed or broken. Pause a download? Chances are that it will start downloading from the beginning again when you resume it. Download get interrupted? Chances are that it will start downloading from the beginning again when you resume it. This is basic functionality - I've downloaded iOS updates several times over, again and again, when it happens on the PC iTunes application. Search the internet, people have been complaining about it for years..

I look forward to the day I can replace these devices with something more user friendly.

Edit: And that day may come sooner!  I dropped my iPod on the pavement and the screen shattered - replacing the screen costs a good fraction of an Android phone..

Saturday, 3 March 2012

pyuv experimentation

The appeal of libuv, and therefore it's Python binding pyuv, is that it provides asynchronous access to a wide range of functionality.  I don't think there is any documentation for libuv itself, if you want to understand the nuances of how to use it, you probably need to jump back and forwards between C source files interpreting the use which has been made of various macros.  The pyuv documentation however, provides a decent overview of the functionality available and how it can be used for Python.

Unfortunately, there is still a degree of libuv source code reading involved, working out what happens in various situations. In this situation, I was curious what happened when the server disconnected a client that was trying to read from its accepted connection.

Server Code

The code starts by locating or creating a loop, which is responsible for managing the events and callback dispatching related to the IO that is performed with respect to that loop. Next a timer is created, this in theory every 20th of a second calls a function. In reality, it is a hack to work around the fact that you can't directly specify a maximum timeout to the run_once method. Instead, the timeout enables the run_once to exit rather than blocking indefinitely.

Next, a TCP connection is created. It is set to be allowed to reuse the same port without waiting, bound to a listening address and then the loop is run until the callback indicating the listen operation occurs.
import pyuv
loop = pyuv.Loop.default_loop()
timer = pyuv.Timer(loop)
timer.start(lambda *args: None, 0.0, 0.05)
listen_socket = pyuv.TCP(loop)
listen_socket.nodelay(True)
listen_socket.bind(("0.0.0.0", 3000))
had_listen_callback = False
def listen_callback(*args):
    global had_listen_callback
    print "listen_callback", args
    had_listen_callback = True

listen_socket.listen(listen_callback, 5)
while not had_listen_callback:
    timer.again()
    loop.run_once()
The listen callback is called everytime there is an incoming connection so that it can be accepted and handled however. The code now proceeds to accept the connection, write token data to it, and then to wait until the data has been sent.
incoming_socket = pyuv.TCP(loop)
listen_socket.accept(incoming_socket)
had_write_callback = False
def write_callback(*args):
    global had_write_callback
    print "write_callback", args
    had_write_callback = True

incoming_socket.write("DATA", write_callback)

while not had_write_callback:
    timer.again()
    loop.run_once()
And finally, once the data has been sent, the socket is closed.
had_close_callback = False
def close_callback(*args):
    global had_close_callback
    had_close_callback = True

incoming_socket.close(close_callback)
while not had_close_callback:
    timer.again()
    loop.run_once()
Client Code

The client code has the same boilerplate to begin with, but then tries to connect to the listening address, and waits for the connection to be established.
import pyuv
loop = pyuv.Loop.default_loop()
timer = pyuv.Timer(loop)
timer.start(lambda *args: None, 0.0, 0.05)
client_socket = pyuv.TCP(loop)
had_connect_callback = False
def connect_callback(*args):
    global had_connect_callback
    print "connect_callback", args
    had_connect_callback = True

client_socket.connect(("127.0.0.1", 3000), connect_callback)
while not had_connect_callback:
    timer.again()
    loop.run_once()
Next the client starts reading, and prints out the arguments everytime it receives data in it's callback.
had_read_callback = False
def read_callback(*args):
    global had_read_callback
    print "read_callback", args
    had_read_callback = True

client_socket.start_read(read_callback)
while True:
    timer.again()
    loop.run_once()
Results

The answer to the question of what happens when the client is disconnected mid-read, is that it receives a data value of None and an error value of UV_EOF. Pretty simple actually.

My main reason for playing with pyuv, is to hopefully replace the monkey-patching framework that is stacklesslib, with one that can cover a wider variety of thread blocking functionality. The existing Stackless-compatible socket module is based on asyncore and by extension it wraps the existing Python networking functionality in an asynchronous manner, and it can handle the more complicated functionality like makefile and ioctl because of this.

Unfortunately, libuv and by extension pyuv, abstracts away or does not provide access to much of the standard API. This means a lot more work if those aspects are to be emulated. Sure, it might be an easier approach to simply not implement them, but that loses a lot of the benefit of writing a monkey-patched module. If the replacement socket module provides makefile, then it is compatible with a wider range of other standard library modules.

I've written the most straightforward part of a socket module for Stackless based on pyuv, and it can be seen within a Google hosted project here.

Monday, 20 February 2012

LPMuds.net: VM to Demo LPC Muds

Link: VM to Demo LPC Muds

This is more a response to Drakkos' post on the subject, as I can't work out how you register to post comments there.  Drakkos, someone actually started a project like this back in August 2008 (see link above).  I think it would be a good idea for it to be revived and actively mantained, although how much work or what is involved.. I do not know.