the locksmith

Last night I finished refactoring the lock code and checked it in. I’m kinda surprised that its still working. Here’s the story.

The original code pretty much didn’t track the locks it handed out. It put them in the list of locks held in the volume’s DosList entry, and removed them when the locks were freed, but it never looked at them. It shouldn’t have been doing that anyway - that lock list is only for when the volume is taken offline (eg the disk was ejected) while locks are still open. In that case, any outstanding locks are added to the DosList. Later, if the volume comes online again, the handler detaches the locks and takes control of them. This is the mechanism by which the Amiga in days of old could request that you “please insert volume Work: in any drive”.

This list was being used incorrectly, so it had to change - I have a feeling its responsible for a bug on native where you insert a new disk and both the old and the new volume appears. A real list of all locks is needed by the handler, for a number of things:

I did consider just having a straight list of locks, but this meant a search through all the locks every time some shared attribute needed to change. So instead locks now have two parts: a shared part which contains the file’s name and protection bits, the location of its first data cluster, and the location of its directory entry, and a per-instance part which has the current seek position and the IO handle. Put another way, the shared part has stuff about the file itself, while the per-instance part has stuff about access to the file.

The shared part (that I call “global” locks) are held in a linked list attached to the superblock structure. Each global lock has a list of per-instance (just called filelocks) locks attached to it. Each one of those has a pointer to its global lock. The system just passes filelocks around as normal (and out to DOS and back), but goes to the global lock when it needs some file data.

Now that this is in place, all the above things can be implemented. Exclusive locks are already done - when an attempt is made to obtain a lock, the global lock list is checked. If the caller requested exclusive and a global lock was found, the operation fails. Renaming now should be trivial - if the new name won’t fit into the existing directory entries, then the existing ones are blanked, new ones created, and the entry attributes in the global lock are updated and seen by all filelocks).

The DosList stuff is on hold. I’ll get there, its just a few steps down on the my list. The next thing I want to do after renaming is done is to implement notifications. File change notifications are done by filename, not lock, and there can be more than one per file, so I need a place to store them even if the file isn’t open. This is now trivial - the notifications get stored in the global lock (which will be created if a notification is requested and the file isn’t open).

So now I’ve written this, and it still makes sense. Weird, it felt so hard at the time.