Dining philosophers using OSGi (Equinox) data service locations

Dining philosophers using OSGi (Equinox) data service locations

In p2, if you’ve ever tried to write to the same artifact.xml (artifact repository) from two different processes, you will quickly learn that this fails. In fact, both processes will start corrupting each others data and you will likely end up with an unusable artifact repo. This limitation is of particular concern if you wish to use proper bundle pooling (a single bundle pool for all your Eclipse installs). Of course, you could implement your own bundle pooling technology, but I’ve set out to fix this at the p2 level.

Java synchronization obviously doesn’t work in this case because we are talking about multiple processes (multiple VMs). Instead, we need some mechanism to communicate the locking between Java VMs. File locking is one of the most used techniques in this case, and lucky for us, the reference implementation of OSGi (Equinox) provides a Data Location service which does exactly this.

Equinox Data Location, among other things, allows you to create a file location and acquire / manage / release a lock on that location. Equinox takes care of the platform oddities around doing all that (how do you create a file lock using Java Foundation 1.1 on Windows?). To demonstrate how this works, I’ve implemented the classic Dining Philosophers problem using Equinox’s Location service.

For those of you unfamiliar with the problem, essentially there are N philosophers and N forks around a table. Philosophers think and eat. When they want to eat, they first must first acquire both the fork on their left and right. When they’ve successfully done this, they can eat. Once they are done eating, then put the forks down and resume thinking.

In my solution, I represent each fork as a ‚Location‘. A location can be created as follows:

private  Location getLockLocation(URI baseLocation, int locationNumber)
  throws IllegalStateException, IOException {
    Location anyLoc = (Location)  Activator.getService(Location.class.getName());
    URI locationURI = URIUtil.append(baseLocation, "" + locationNumber);
    Location location = anyLoc.createLocation(null, URIUtil.toURL(locationURI), false); //$NON-NLS-1$
    location.set(URIUtil.toURL(locationURI),false);
    return location;
}

This will create a location at ${baseLocation}/locationNumber

Each philosopher can create a location for the fork on their left and right. From here, implementing the solution is fairly easy:

try {
  thinking: while (foodLeft > 0) {
    // Wait and do some thinking
    Thread.sleep(getThinkingTime());
    while (true) {
      // Continue until you can acquire both forks.  We could 
      // sleep for a bit if we don't acquire both forks, but
      // we are pretty hungry here, and sleeping is never good
      // on an empty stomach 
      boolean leftFork = false;
      boolean rightFork = false;
      try {
        leftFork = acquire(leftForkLocation);
        rightFork = rightForkLocation.lock();
        if (leftFork && rightFork) {
          eat();
          continue thinking;
        } 
      } catch (IOException e) {
        // do nothing, we will release everything below
      } finally {
        if (leftFork)
          release(leftForkLocation);
        if (rightFork)
          release(rightForkLocation);
      }
    }
  }
} catch (Exception e) {
  e.printStackTrace();
}

In this solution, we wait until we acquire the fork on the left. However, we quickly release both locks if we cannot acquire the fork on the right.

/**
 * Blocks until a particular location can be locked
 */
private boolean acquire(Location l) throws IOException, InterruptedException {
  boolean lock = l.lock();
  if (lock)
    return true;
  while (true) {
    Thread.sleep(10);
    lock = l.lock();
    if (lock)
      return true;
    }
}
/**
 * Releases the lock on a particular location
 */
private void release(Location l) {
  try {
    l.release();
  } catch (Exception e) {
    // Do nothing
  }
}

I’ve put my solution to this problem on Github. Feel free to fork it (no pun intended).
https://github.com/irbull/com.ianbull.dining
In my solution, I’m actually not using multiple processes (and I’m sure there are other things that can be improved), but it should give you an idea of how to use the service.