Data Structure Storage Techniques: Copy-On-Write Snapshotting in C

My blog has been leaning more towards data structures and algorithms lately and I'd like to continue with that for now. The last couple posts have been about graph algorithms. I've also been working on storage and retrieval of data structures (hashtrees and graphs).

When researching different ways to store data structures to disk, I came across a technique called copy-on-write snapshotting. The basic idea is you have a data structure in virtual memory that you want to store on disk. You fork(2) a child process and write the data structure to disk. There are a few things you have to do in order to get copy-on-write snapshotting.

The first thing is to use a private, anonymous mmap for storing your data structure in virtual memory.

unsigned long mapsize;
unsigned char *pmap;

/* 1MB */
mapsize = (1 << 20);
pmap = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

If you're on OS X you probably need to use MAP_ANON instead of MAP_ANONYMOUS.

Let's say you have a binary tree T stored in virtual memory and it's time to take a snapshot of it. When you're using private virtual memory in one process and issue a fork() call, the parent process's VM pages aren't copied over to the child process. Instead the pages are shared until one of them writes to a shared page. If one process writes to a shared page, a copy of that page is made for the process doing the updating. The changes are not visible to the other process.

Copy-on-write is useful for when you're still issuing updates to the data structure in the parent process while snapshotting a current (as of the fork call) copy of it to disk in the child process. To take a snapshot, first make a call to fork():

pid_t pid = fork();
if (pid == 0) {
    /* in the child process */
    take_snapshot(T);
} else {
    /* in the parent process */
    ...
}

The take_snapshot() function is where you write a snapshot of the tree to disk:

#define RWRR    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH

static void take_snapshot(struct tree_s *T) {
    int fd = open("snapshot.dat", O_CREAT | O_TRUNC | O_RDWR, RWRR);

    /* Seek to EOF minus one byte and write a single
     * byte at the end to create a valid file map */
    lseek(fd, T->mapsize-1, SEEK_SET);
    write(fd, "", 1);

    /* create file mmap */
    unsigned char *fmap = mmap(0, T->mapsize, PROT_WRITE, MAP_SHARED, fd, 0);
    /* copy bytes from snapshot memory to file map */
    memcpy(fmap, T->pmap, T->mapsize);

    /* transfer all data to disk */
    fsync(fd);

    munmap(fmap, T->mapsize);
    munmap(T->pmap, T->mapsize);
    close(fd);

    /* free the child's copy of the tree */
    free(T);
    /* exit the child process */
    exit(EXIT_SUCCESS);
}

The code above can be thought of as pseudo-code because I haven't defined `struct tree_s` above and it won't work as-is. To see an actual working example, take a look at my GitHub gist on copy-on-write snapshotting of a binary search tree.

I'm also working on Multiversion Concurrency Control so look forward to a blog post about that topic.

Hacking on Wikipedia's Link Graph

This was taken from my WikiGraph GitHub project's README file.

I've been meaning to hack on Wikipedia's link data so this is kind of the result of that. It's an ongoing process since there's so much data and many different things to do with it.

Note: I'm running this project on a Thinkpad T410 Intel i5 with 8GB of RAM. If you have less RAM, it's possible that the final graph data won't fit in memory. I'm hoping to spend some time researching & developing a kind of in-memory and on-disk graph structure. A graph database would probably be valuable for this dataset.

Also note: If you don't want to parse and generate gigabytes of data, I uploaded the core graph data that this project uses. You can download the files here.

You'll need both graph.tar.bz2 and keys.tar.bz2 and they need to be extracted to the wikigraph/data/ directory. If you just use those two files, the only script you need to use is graph.py.

Starting From Scratch

This project uses the April 2011 pages-articles.xml.bz2 database dump from http://en.wikipedia.org/wiki/Wikipedia:Database_download

After extracting the XML file, the first thing to do is replace the <mediawiki></mediawiki> tags with <xml></xml> tags so the SAX parser can do its job properly. This is how I did it:

$ perl -pi -e 's/<mediawiki[^>]+>/<xml>/' enwiki.xml
$ perl -pi -e 's/<\/mediawiki>/<\/xml>/' enwiki.xml

enwiki.xml is just a renamed pages-articles.xml file.

The next step is to run the SAX parser on the XML file. Since it's massive, it'll take quite awhile. It takes my T410 Intel i5 laptop about 40 minutes or so. The RAM footprint is negligible since it's a SAX parser. I was running at 100% CPU with 0.1% RAM used.

You'll have to modify the filename at the bottom of saxparser.py since it's hardcoded for my path at the moment.

Once it's done parsing, there will be a file called "pages_links". That's the raw graph data formatted like so:

"page0","link00","link01","link02",...,"link0N"\n
"page1","link10","link11","link12",...,"link1N"\n
...

Each page has a list of links following it from that page. This is a page's adjacency list.

Parse Page Titles

The next script to run is keys.py. It will parse pages_links for page titles by using csv.reader(f, delimiter=',', quotechar='"') and reading line by line. The first element in a given row/line is the page title. Each page title is cleaned up a bit and some pages (Files:/Wikipedia:/Category:) are ignored completely. The page title is then used as a key into a defaultdict(int) object so I only have unique keys. I then sort all the keys and write them to disk in the file "data/keys".

Parse Adjacent Pages/Links

You then need to run values.py to parse each page's links and generate a usable link graph. This script starts by loading the data/keys file so I can do vertex lookups by page title. Each vertex has an adjacency set because there will be some duplicates and a set is an easy way to manage unique indexes.

The script then parses the pages_links file for each page's links and adds them to the appropriate vertex's adjacency set. The adjacency sets use integer indexes looked up from the keytable by page title. This helps keep the memory footprint to a more manageable level. Only links that are stored in the keytable are allowed to be added to an adjacency set for a given vertex.

Each vertex's adjacency set is then written to disk as "data/graph". A single line in the graph file is formatted like so:

v1 v2 v3 v4\n

Each adjacent vertex is an integer and separated by a space. A newline char indicates the end of that adjacency list. Each line maps to a vertex in the graph: line 0 in the graph file maps to vertex 0 in the graph. That means that line 0 contains vertex 0's adjacency list.

Running Graph Algorithms

You can either run graph.py directly:

$ python graph.py

or you can run it in the Python interpretor:

$ python
>>> from graph import WikiGraph
>>> g = WikiGraph()
>>> src = g.keytable["nazism"]
>>> dst = g.keytable["philosophy"]
>>> g.bfs(src)
>>> g.path(src, dst)

Some ideas for future work

1. Determine backlinks of each wikipedia page.

2. Web-based interface for running graph algorithms on the dataset.

3. Some kind of graph database. I calculated that pre-computing all possible paths in the link graph would require storage on the order of 300 TBs. That means for now I have to run algorithms on-the-fly and that can be slow if I'm trying to service web requests. Not to mention that each time a BFS is run, it has to allocate storage for predecessor trees. A few requests in a row would kill a server.

4. ???

5. PROFIT!

 

Applied Single-Source Longest-Path Algorithms

Or, determine the maximum sum of numbers from top to bottom of a triangle by following a path defined by adjacent numbers on the line below.

Problem

Given a triangle like below, you need to write an algorithm that maximizes the cost of traveling from point A to point B, where point A is the top of the triangle and point B is any one of the nodes at the bottom of the triangle.

Sample triangle:

         5*
        / \
       /   \
      9*    6
     / \   / \
    /   \ /   \
   4     6*    8
  / \   / \   / \
 /   \ /   \ /   \
0     7*    1     5

The maximum sum of the path taken above is 5+9+6+7 = 27.

The triangle above is stored in a text file with the following format:

5
9 6
4 6 8
0 7 1 5

Each line is just separated by a '\n' and each number on a line is separated by a space. Nothing too fancy.

Where to start?

One of the tricks to solving this problem is recognizing that it's a graph problem. Some hints in the problem statement make this pretty clear: adjacent numbers, maximum sum/cost, moving from the top number to a bottom number, etc. The first thing you need to do is parse the text file and build the graph representation from the data. The way I do this in Python is:

lines = open(filename).readlines()
lines = [line.strip() for line in lines]

# `lines` is now a list of space-delimited strings of numbers:
# ['5', '9 6', '4 6 8', '0 7 1 5']

# create a graph from the lines:
G = TriangleGraph(lines)

Implied in the problem, the graph is a Directed, Weighted Graph

The next order of business is taking the `lines` list and turning it into a usable directed, weighted graph representation. I created a TriangleGraph class that takes the `lines` list as an init argument and handles all of the graph stuff. Here's the relevant part of the TriangleGraph initialization:

class TriangleGraph(object):

    def __init__(self, lines=[]):
        self.lines = lines
        self.graph = []
        self.numNodes = 0

        # each node has some data associated with it, such as
        # its value, the distance from the source vertex, and
        # its immediate predecessor. A vertex's index i will map
        # to graph, values, distances, and predecessors.
        self.values = []
        self.distances = []
        self.predecessors = []

        # I use adjacency lists to represent the triangle graph
        # and its built during initialization.
        self.build_adjacency_lists()

    def build_adjacency_lists(self):
        """
        Build a graph representation from self.lines using adjacency lists.
        """
        # account for root node first because we need to track the other
        # node's indexes as they're added to the graph.
        self.numNodes = 1

        # add root node to self.values first because the loop below
        # only adds next lines.
        self.values.append(int(self.lines[0]))

        for i in xrange(len(self.lines)-1):
            nextline = self.lines[i+1].split()

            # add next line of values
            self.values.extend([int(n) for n in nextline])

            for j in xrange(len(nextline[:-1])):
                # add adjacent numbers on the next line to the graph
                # for the current vertex.
                self.graph.append({self.numNodes: int(nextline[j]),
                                   self.numNodes+1: int(nextline[j+1])})

                # last adjacent vertex added to current vertex's adjacency
                # list will be the _first_ vertex added to the next vertex's
                # adjacency list, so increment numNodes by only 1.
                self.numNodes += 1

            # A new line is about to be added to the graph, so make sure
            # the first adjacent vertex added to current vertex is indexed
            # as one more than the last adjacent vertex added to the previous
            # vertex. Otherwise, the last vertex on this line will be adjacent
            # to the first vertex on this line.
            self.numNodes += 1

        # The last line of the triangle contains a list of vertices
        # that don't have adjacent vertices, so add empty dictionaries.
        last = self.lines[-1].split()
        self.graph.extend([{} for i in xrange(len(last))])

The graph now has a usable representation in the form of adjacency lists. For the example triangle referenced at the beginning, its graph representation is now this:

# vertex u has adjacent vertices v1 and v2 with values w1 and w2:
# adjacency dict is formatted as u -> {v1: w1, v2: w2}
0 -> {1: 9, 2: 6}
1 -> {3: 4, 4: 6}
2 -> {4: 6, 5: 8}
3 -> {6: 0, 7: 7}
4 -> {8: 1, 7: 7}
5 -> {8: 1, 9: 5}
6 -> {}
7 -> {}
8 -> {}
9 -> {}

With a directed, weighted graph, you need to include each vertex's value as part of the adjacency list. If it wasn't a weighted graph, vertex 0, for example, would have an adjacency list like so: 0 -> [1, 2] because vertex 1 and 2 are adjacent to vertex 0. Using a dict to represent weighted vertices makes sense. The `self.values` list is also a lookup table keyed by vertex index. I use that in my `weight` function to determine the cost of moving from one vertex to another.

Finding the shortest path through a directed, weighted graph

Now I'm not a computer scientist, so I had to reference my copy of Introduction to Algorithms by Cormen, et al. I initially hacked up a Breadth-First Search and was able to solve the first triangle by simply picking the largest adjacent number on the line below. The BFS failed miserably on more complex triangles. I then remembered reading about Single-Source Shortest-Path algorithms on directed, weighted graphs. Dijkstra's algorithm came to mind. There are two important pieces of Dijkstra's algorithm that need to be modified in order to transform it to a Single-Source Longest-Path algorithm.

A normal implementation of Dijkstra's algorithm consists of an initialization function and a relax function. The initialization function initializes the distances and predecessors arrays so they can be used in the actual algorithm.

# I use the heapq library in standard Python because
# Dijkstra calls for a priority queue.
import heapq

def dijkstra(G, s):
    """
    Note: this is valid Python, but it's also pseudo-code
    to describe the algorithm. The actual code is inside a class.
    """
    # initialization
    for v in xrange(len(G)):
        d[v] = float("inf")
        p[v] = -1
    d[s] = 0

    S = set()
    Q = [i for i in xrange(len(G))]
    heapq.heapify(Q)

    while Q:
        u = heapq.heappop(Q)
        S.add(u)
        for v in G[u].keys():
            w = weight(u, v)
            relax(u, v, w)

 

NOTE: Functions below are defined for Single-Source Shortest-Path.

def initialize_single_source(G, s):
    # G is the graph and s is the index of the source vertex
    for v in xrange(len(G)):
        d[v] = float("inf") # infinity
        p[v] = -1
    d[s] = 0

`d[v]` is the distances and `p[v]` is the predecessors. The relax function depends on the `d[v]` values and the edge weight. I define my weight function w(u, v) as `values[u] + values[v]`. That is, the cost of traveling from vertex 0 to vertex 1 is 5+9, because values[0] == 5 and values[1] == 9.

Relaxing an edge just means that if the algorithm can improve the shortest path to some vertex `v` by going through vertex `u`, it will update the distances and predecessors array for vertex `v` to go through `u` instead. The definition of the relax function for shortest-path is:

def relax(u, v, w):
    # `w` is w(u, v) already computed
    if d[v] > d[u] + w:
        d[v] = d[u] + w
        p[v] = u

Neat, but how do I determine the longest path?

There are two simple changes you need to make to the `initialize_single_source` and `relax` functions. First off, rather than initializing the distances to infinity, initialize them to -1. Now you need to modify the relax function to test if the path can be improved by increasing the cost associated with traveling from vertex v to u. The modification is just `if d[v] < d[u] + w`, then reset v's path to go through vertex u.

NOTE: Functions below are modified for Single-Source Longest-Path 

def initialize_single_source(G, s):
    # G is the graph and s is the index of the source vertex
    for v in xrange(len(G)):
        d[v] = -1
        p[v] = -1
    d[s] = 0 
def relax(u, v, w):
    # `w` is w(u, v) already computed
    if d[v] < d[u] + w:
        d[v] = d[u] + w
        p[v] = u

Since there are a number of vertices at the bottom of the triangle, you need to find paths to all of them from the source vertex to determine the longest path. After running `dijkstra(G, 0)` (since vertex 0 is the top of the triangle), you can determine the longest path similar to below:

def path(s, v):
    """
    If there's a valid path from s to v, sum the cost of that path.

    `path(s, v)` follows the path backwards by traversing the
    predecessors of v until it hits the source vertex.
    """
    if s == v:
        return values[s]
    elif p[v] == -1:
        # no path
        return 0
    else:
        return path(s, p[v]) + values[v]

def longest_path():
    total = 0
    # len(G)-len(lines) is the first vertex on the last line,
    # so iterate from that vertex to the last vertex at len(G)
    for i in xrange(len(G)-len(lines), len(G)):
        t = path(0, i)
        if t > total:
            total = t
    return total

If you'd like to see my actual working implementation, it's hosted on Github: Solution to Triangle Graph Problem

Something a little different

Like a lot of siblings, my brother and I fought all the time when we were kids. It was mostly me egging him on in the shadows and him retaliating. He was pretty vocal about his feelings as a child. Neighbors would regularly tell my parents that my 5 year old brother was calling me an "asshole".

His mouth got him in trouble on more than one occasion. He was smarter than most other little kids, so the ones who couldn't fight him intellectually would have to resort to violence. Even though we fought against each other all the time, I still defended him against the world.

One day, after waging war against my brother, our mother sat us down to have a little chat. Being a caring mom, she hated us fighting so much and wanted us to get along. She talked about how important family was. If I remember correctly, this was also the time I realized my parents wouldn't be around forever.

She essentially pounded into my head that, someday in the future, my brother and I will only have each other. That lesson rooted itself deep into my brain. I tend to put most things out of my head, but I've never forgotten that lesson. I believe my brother ended up learning the same thing that day.

I made it a goal of mine to always be there for my brother. When he was down on his luck, I spotted him food and shelter. I had absolutely no problem with providing him the things he needed. Even if it meant I had to go without.

Over the years I've never really gotten used to not seeing my brother as often as I wanted. Sometimes it'd be me leaving for school or moving to a new city for work. More recently it's been him moving away or taking care of his own business.

A few years ago he joined the United States Army.

During the first year I think we talked maybe once in six months. It was really hard on me and especially our mother. Even though he was state-side, it seemed like he was in another world. He was eventually stationed close to where I was living at the time. We decided it'd be fun to catch up on lost time and we ended up living together for about 6 months or so.

He was still serving during that time, but I think we got closer than ever.

I've recently realized that our mom was only half right about my brother and I only having each other. The other half of that equation is that we'd eventually have our own families to love.

On the 28th of this month, my brother's daughter will be 10 months old. Aside from having darker skin than him and having my hair, she's a spitting image of him. I also recently found out that I'm going to be a dad at the end of this summer. Both of us now have our own families to look after.

This morning, Monday the 24th of January, my brother and I hugged and said our goodbyes. He's been ordered to Afghanistan. We knew for awhile that he'd be going this year but it's still a shock. He's currently in operational lockdown before he flies out across the country for preparations.

It isn't just dumb kids fighting for this country. It's fathers and mothers as well. My brother knows what he's doing and he isn't just another soldier. Not only is he more political and better informed than me, he's an expert marksman with all his weapons.

You can fight for your country and support your soldiers without supporting everything your government does.

I'll do my best to help where I'm needed. I'll see you soon, little brother. Check your corners!

On Being Famous for Blogging About Being Famous

or, how many social media experts does it take to screw in a light bulb?

Remember the time when the internet was just for geeks? The internet landscape has changed. The web took over and has become a breeding ground for all sorts of idiots promoting themselves. There's a huge echo chamber full of people who blog about building an online presence. They blog about how you too can get as famous as they are. Except these people are "famous" for simply blogging about how to get famous on the web.

I was doing some research on these so-called "digital crusaders" and I can honestly say I've never heard of a single one of them. Maybe that just means I'm out of the loop. What's interesting is that these social media experts all know of each other. There's a feedback loop for these digital crusaders. There are tons of blogs all interlinking to each other and all promoting each other as if they were really important.

This sets these crusaders up for thinking they're actually creating some kind of value, when all they're really doing is talking. One digital crusader actually lists a few of his "accomplishments". These accomplishments are really nothing more than being featured on other peoples' blogs talking about how famous he is. These other blogs are only known by people in this circle jerk and definitely not "famous". There's something wrong when your accomplishments are centered around telling other people how to get famous on the web. Please, point me to something that you _actually_ did so I can understand. Because what you're really selling can't simply be yourself and how you got famous by blogging about being famous. Or is that really all you're offering?

I can't help but think that there is actually a market for people like this. A market where some normal person actually benefits from the teachings of a digital crusader. Perhaps by simply blogging about them I'm contributing to this digital crusader "brand". There's no such thing as bad press. Even the bad press about something tends to promote that _thing_.

It's actually pretty disgusting when you get right down to it. There are some people who continue to value talking over doing. Digital crusaders talk about doing, but what they're doing revolves around talking.

There are also some people who have personal brands online who have created tons of value. These people don't go around calling themselves "social media experts". Some of them are people who were successful before the web and used the web to take their businesses to the next level. They don't just talk about how to become internet famous. They don't just create a personal brand for the sake of branding. These are hustlers building a following for the businesses they create.

So, create your personal brand. If your accomplishments are nothing more than other people talking about your blogs on how to get internet famous, you're probably not creating value. If you're selling ebooks on how to get famous by selling ebooks, you're an asshole. So create more value than you capture. Get out there and fucking hustle.

 

Epoll Egg Released for Chicken Scheme

Quite a few people suggested I turn the core technology that powers Rooster into a Chicken Egg. Eggs are Chicken Scheme's version of third-party libraries/extensions. Rooster used epoll on Linux by interfacing the C sys/epoll functionality with Chicken Scheme. I spent some time getting feedback in the IRC channel (#chicken on freenode) yesterday and today. I'm happy to announce that I've released a brand new egg that brings epoll support to Chicken Scheme. There are a couple places you can get the epoll egg:

  • $ git clone git://github.com/davidreynolds/epoll.git
  • $ sudo chicken-install epoll

If you cloned the git repo:

$ cd epoll
$ sudo chicken-install epoll.setup

I also rewrote Rooster as a Chicken Scheme module and made it use the epoll egg. I had some difficulty coming up with a good way to return a vector of pairs from C into Scheme. I didn't exactly want to use my old way of calling back into Chicken from C because a user would then have to provide their own Scheme callback. That's basically what I ended up doing, however.

I wrapped C epoll_wait so that it calls into the epoll egg with the vector of pairs. Where this differs from the old Rooster code is that the interface to Chicken epoll-wait that a programmer has access to actually takes a callback function as an argument. This callback function is defined by the programmer and accepts a list of (fd . events) pairs. The epoll egg callback, SCM_epoll_wait_cb converts the vector of pairs into a list of pairs and passes it to the user-defined callback function.

It seems to work pretty well. As for the future of Rooster, I'm considering breaking it into a couple different projects. I'd like to create a web server with the epoll egg. I also need to work on getting some documentation up for the epoll egg and for Rooster.

To the Chicken Scheme Community

Thanks for the great feedback you've been providing and for helping me get my first Chicken Egg released. Hacking on Chicken is pretty neat and it's definitely a great Scheme implementation. I'm looking forward to producing some more eggs and interesting software in Chicken.

Rooster: An epoll Server written in Chicken Scheme and C

My last few blog posts have been sort of leading up to this point: writing an actual server in Chicken Scheme that uses epoll. The server is written in such a way that lets the programmer determine what to do with incoming requests. All the server does is manage sockets and pass input to a programmer-defined request handler. Here's a simple echo server example that uses Rooster:

(require 'rooster)

(define (handler fd rbuf)
  (send-to-client fd rbuf))

(run-rooster handler)

The function `send-to-client` is defined in server.scm and exported so it can be used in an external program. send-to-client writes the buffer to the file descriptor. Note that send-to-client doesn't actually _send_ a buffer to fd. It fills the write buffer for that fd and sends the write buffer when epoll tells the server it's ready to write on that socket.

Rooster requires epoll and Linux for now, but I'd like to fall back to standard poll if epoll isn't available. The main idea I had for Rooster was to create some kind of MUD with it and treat Rooster itself as a sort of game engine.

It'd be interesting to see a new web server come from this as well. I don't see why Rooster couldn't provide the core of any kind of server. New servers could be written on top of Rooster and be completely different from each other.

I'm expecting to be making a ton of changes to Rooster and its API, so it's not at a point where you can rely on the APIs provided unless you feel like locking down to one build of Rooster. I put the code for Rooster on Github earlier today so please take a look. The README should get you started building it and writing your own socket handlers.

Using epoll with Chicken Scheme

Late last week I wrote a blog entry on how to interface Chicken Scheme and C. It was a short and sweet introduction to writing Scheme that calls into C that in turn calls back into Scheme. I spent some of the weekend adapting the vector_of_pairs code to implement a working echo server using epoll. There's a little more C code involved and a lot more Scheme code than before. If you've seen Tornado's epoll.c wrappers, you'll quickly recognize the similarities between their code and mine, with mine being adapted to Scheme instead of Python.

First, here's the epoll.c gist:

The _epoll_wait function is almost a direct copy of the vector_of_pairs implementation from my last blog post. The basic idea of epoll is to initialize an epoll file descriptor (_epoll_create), add events to epoll (_epoll_ctl), and wait for epoll to tell you of events as they occur (_epoll_wait).

In order to actually implement an echo server using epoll, you need to write some standard boilerplate to set up a server socket, bind it to an address:port, and start accepting connections. Thankfully, the tcp unit in Chicken Scheme handles quite a bit of that.

Here's the test_epoll.scm file:

The first step to setting up a server socket is by calling tcp-listen. That function returns a server socket listener.

(define listener (tcp-listen 6666))

Next, you initialize epoll. To do this, you define a foreign-lambda to bind the foreign function _epoll_create to a Scheme function:

(define ##epoll#epoll_create (foreign-lambda int "_epoll_create"))
(define epfd (##epoll#epoll_create))

Those two lines define the FFI and creates the epoll file descriptor. All events are tied to the epfd. At the bottom of the test_epoll.scm file, there's the main loop. It takes as an argument the socket listener created above and extracts the listener's file descriptor (tcp-listener-fileno). ev-main-loop then adds the server socket to epoll and watches for the read event. An infinite loop is then entered and epoll_wait is called with a timeout of 200 milliseconds.

The function SCM_epoll_wait_cb is the callback that the C _epoll_wait function calls. Right now _epoll_wait always calls back into Chicken, so next time I'll make it dependent on there actually being events ready to operate on. SCM_epoll_wait_cb converts the vec object into a list of pairs and passes that list to the event handler (fd-event-list-handler).

fd-event-list-handler is where the magic happens. If there aren't any fd's to operate on, fd-event-list-handler evaluates to an empty list. When a server processes sockets, it has to differentiate between new connections and old connections. It does this by checking if the socket being operated on is the same as the server socket, which would mean we have a new connection. A new connection has to be passed to the accept function to create the client socket. Old connections are operated on normally.

There are two event conditions that the handler recognizes when dealing with old connections: read and write. I set up two hash tables for handling a client socket's I/O: fd-write-table and fd-read-table. The lookups are based on the client's file descriptor and initialized to empty strings once the client socket is accepted.

When the server reads from a socket, it writes that data into a temporary buffer and writes the modified string to the client's output buffer (using send-to-client). Modified as in splits on the newline character and only writes the string up to and including that point. send-to-client doesn't actually do any writing. It just appends strings to the output buffer. When epoll tells us that a socket is ready to write on, that's when we write the output buffer to the socket. Both buffers are emptied by this point.

After each read/write, the file descriptor is updated in epoll to reflect the new event. After a read takes place, the socket is ready for writing to. After a write, the socket is ready for reading from.

Like the last entry, this code requires Linux and Chicken Scheme. Compile the two files above to get a working server: csc epoll.c test_epoll.scm -o server

Once the server is running you can telnet to localhost on port 6666.

Fun with Interfacing Chicken Scheme and C

In my last blog entry I briefly mentioned that I've been hacking a little bit with Chicken Scheme. I have a few more things in store, but for now I'd like to share a small bit of code that does one thing: generates a Scheme vector of pairs in C and passes that object back to Chicken Scheme. This code is relatively simple and will be used eventually for returning a vector of file descriptors and events associated with them. This code is a stepping stone into wrapping epoll_wait.

The documentation for Chicken Scheme is rather good, but there are quite a few blank spots in the "interfacing with C" category. The C implementation is pretty readable, so I was able to dig around there for a little bit and learn more about how to write a decent C interface to Chicken. If you're in the mood, I'd encourage you to take a look at the implementation and specifically runtime.c in the Chicken source directory.

This is the C file that defines a function for generating a vector of pairs:

and here's the Scheme file that interfaces with vector_of_pairs.c:

Compile these with: $ csc vector_of_pairs.c test.scm -o test and run the resulting executable. Oh, I should mention that this is on Linux, so your experience setting this up will be different unless you're on Linux as well. For information on how to install Chicken Scheme, visit this link.

Thoughts on Web Development: Automation, Code Generation, and Scheme

Every so often I become frustrated with my tools. I mostly spend my time building web applications and it's painfully obvious how much boilerplate code I write in Python to put live apps on the web. Python isn't alone in this respect. Any language will have some amount of boilerplate. Lately I've been spending some time on automating deployment of web applications. Any web developer worth his or her salt will have some process in place for automated deployments. Another thing worth automating is your initial project generation. The more popular web frameworks have something like this in place. Pylons, Django, Rails, etc are all capable of generating a starting point for your web app.

My own web framework, blueberry, initially had an automated starting point for web apps. It also had some code for managing database migrations. Over time, these features became somewhat constraining so I ripped them out completely. The last few web applications I've worked on used Tornado, which has zero code generation for a starting point. Not having a web app completely generated for me is a big bonus in my opinion. I'd rather build tools for automating web app generation to fit my style, rather than fitting my style to the web framework.

Another topic worth exploring is web form generation. There are tradeoffs to be made in this area and many ways of doing the same thing: building web forms, validating them, and storing the result in a database. Python has some really cool form generation libraries (ToscaWidgets, for example). There's also formencode for validating and inserting defaults/errors into your forms. Using ToscaWidgets and formencode together is pretty cool if you can get it working perfectly. ToscaWidgets is especially complex. At the time I was using it the docs were a little light, so I spent most of my time inside of the code learning how it worked.

Then there's the other side of the coin: Using straight HTML to build your forms. You can 100% use formencode for validating your hand-rolled forms, and it works really well. By the time you get into displaying errors, inserting defaults, etc, you'll start wishing you went with a form generator. Hand-rolled HTML for anything complex is A Bad Idea. I mean, unless you like feeling unproductive when you write your thousandth signup form.

There's also this new-ish trend of using jQuery to generate all of your markup. I'm not sure how I feel about this. Using jQuery is awesome and it's a great piece of technology, but it's possible to make your jQuery markup generation code really complex and it can even take up more of your editor space than your normal markup. That tradeoff may or may not be worth it to you.

Focusing back on server-side technologies, I think there is a lot to be learned from Arc and Scheme in general. I keep coming back to using scheme for my web projects, but a big problem I have is that there's no clear way to do it. With LISP there's Hunchentoot, but even then you have to install all kinds of libraries and it may not even install properly. I used to use PLT Scheme (now Racket) and even wrote a few articles on using it for web development. PLT is in a world of its own. It's an advanced Scheme that has tons of libraries. It even comes with its own web server.

Using languages like Scheme, if done correctly, your web app becomes a set of high-level, short functions that were built from the ground up to limit the amount of boilerplate you have to write. When I'm writing Python I try to incorporate this way of thinking into my own software. To do that really well, you almost have to create an entirely new programming language on top of the language you're using. That's what Arc does. Python doesn't make it easy to do that, but it can be done.

Lately I've been spending some time looking at other Schemes and toying around with Chicken Scheme. I'm hoping to get some web apps running with it one of these days. Just last night I wrote some C code and used SWIG to interface Chicken and libevent. It wasn't a complete interface to libevent though. I made a super simple timing event and called it from Chicken. I'll probably end up writing some simple servers in libevent and using them in Chicken. Maybe even a new web server or tying Chicken Scheme into Mongrel2.