Monday, August 8, 2011

File Locks

New Feature

First a brief description of a new feature upcoming in PseudoTV.  I have 2 systems that run XBMC and PseudoTV in my house, one in my living room and one in my bedroom.  There have been several times that I've wanted to stop watching something in the living room and continue it upstairs.  That is now possible.  There is a new option to specify the directory to store PseudoTV playlists and settings.  If two systems share this directory, then they will both use the same set of channels and (with only minor differences) they will play them at the same times.


This leads to the second part of this post: File locks over a network when using multiple platforms.  What if I turn on TV 1 and it starts loading the channels.  I then turn on TV 2 and it starts loading.  If TV 1 modifies any of the playlists, TV 2 won't necessarily know and things will get screwed up.  Who is allowed to extend the channel lists?  Who is allowed to write to the settings?  This is where file locks come into play.

Locking a file on a local hard drive so that other programs can't access it is generally possible, as long as you're writing for that platform specifically.  It can even be done on networked drives, again, presuming that the systems are the same and you target them specifically.  XBMC runs on multiple platforms so I can't presume anything of that sort.  So how can I make sure that two instances don't start writing to the same file at the same time?  How do I lock files?

The code is actually pretty are the steps:

1. Create a general lock file master list (I use FileLock.dat).
2. In order to lock a file, the first step is for a system to rename the master list to some random number filename (87385.lock).  This is effectively the atomic operation needed for locking files.
3. In order to verify that it succeeded, it makes sure it can open the file for reading.  If it can, then it now has control over the master file lock list.
4. Look in the list to see if the file you wish to lock is already there.  If it is, then it's locked.  If it isn't, then add it to the list.
5. Rename the list back to the original name.

That's it.  The file is locked.


If only it where that simple.  The problems arise when things don't work like they should.  How does the system know that it needs to create the lock list, and not that it doesn't just need to wait for another system to rename it back?  What happens if a system crashes and doesn't release a lock file, is that file permanently locked?

I'll start with the first question.  If any given system can't get access the master list for some amount of time (10 seconds) then it needs to just create the list.  Since, once a system has control over the list, the operations themselves take almost no time at all then the timeout value is very reasonable.  Is this perfect?  No.  It will not scale very well.  I would not recommend using this method for 10 machines at once since it may cause problems.  For a small scale, though?  Great.

What about the second issue, where a file can become permanently  locked?  This is resolved by adding a random value to the same line as the locked file in the list.  For example:

918374, MyLockedFile.txt

Every few seconds, the owner of that file will re-lock the file with a new random number.  Whoever wants access to MyLockedFile.txt will just watch the list and make sure that number actually changes.  If it doesn't change for some amount of time (10 seconds) then it assumes that the lock is dead, and it rewrites it.  If it does change then the lock is valid.

There's nothing terribly difficult about this method, but it took me a while to figure out...hopefully others will benefit from this.  The python code where I use this is here.