Dining Philosophers In Kamaelia

December 23, 2007 at 11:36 PM | categories: python, oldblog | View Comments

After playing around with the STM code, and spotting the dining philosopher's code in the MASCOT example/manual that I found, I've now implemented the traditional dining philosophers example using Kamaelia. This is largely about acquiring and releasing resources - something we largely do in a really simplistic way right now, and have explicitly excluded threaded code from directly accessing.

Implementing STM makes it possible to extend this to threaded code as well, and the dining philosophers example is a pretty canonical example.
#!/usr/bin/python

# First some imports...

import random
import time
import Axon
from Axon.STM import Store

# And a single support function to make code clearer :-)

def all(aList, value):
    for i in aList:
        if value != i:
            return False
    return True


# We can then define a dining philosopher as follows:

class Philosopher(Axon.ThreadedComponent.threadedcomponent):
    forks = ["fork.1", "fork.2"] # default for testing :-)
    def getforks(self):
        """
        Essentially what this says is:
           * I have a number of forks I'm expected to try and pick up.
           * So I grab a copy of the global state.
           * Check to see they're not owned by anyone:
              X[fork].value == None
           * If they're not, then setting their owner to "me":
               X[fork].value = self.name
           * Then try to commit that change of ownership
        """
        gotforks = False
        while not gotforks:
            try:
                X = self.store.using(*self.forks)
                if all([ X[fork].value for fork in self.forks], None):
                    for fork in self.forks:
                        X[fork].value = self.name
                    X.commit()
                    gotforks = True
                else:
                    time.sleep(random.random())
            except Axon.STM.ConcurrentUpdate:
                time.sleep(random.random())
        print "Got forks!", self.name, self.forks
        return X

    def releaseforks(self,X):
        """
        If that works, then I have the forks, otherwise I sleep and retry.
        I then later release the forks by setting their values to None and
        committing back.
        """
        print "releasing forks", self.name
        for fork in self.forks:
            X[fork].value = None
        X.commit()

    def main(self):
        while 1:
            X = self.getforks()
            time.sleep(0.2)
            self.releaseforks(X)
            time.sleep(0.3+random.random())

# The final bit of the code simply sets the system in motion:
S = Store()
N = 5
for i in range(1,N):
    Philosopher(store=S,forks=["fork.%d" % i ,"fork.%d" % (i+1)]).activate()

Philosopher(store=S,forks=["fork.%d" % N ,"fork.%d" % 1]).run()

The upshot of this is, personally, I find this relatively clear and relatively simple. It is also the sort of thing that you can wrap up neatly into a relatively reusable piece of code (which is of course the sort of thing I like to do :)

One thing I'm tempted to do is to change this to emit when a philosopher picks up/puts down a fork since you could animate this in a fun way I suspect :-)

blog comments powered by Disqus