Often, when you are developing some application you wish it to run one instance at a time. If you are writing a native application, there’s no problem because the platform you are aiming on most probably already has everything for this kind of magic. When it comes to cross-platform approach it’s vitally important to choose something neutral. This post is about my experience in this area.
As you might know, I am on the BlogBridge development team. The BlogBridge application could allow several instances at a time if only its database allowed sharing. The HSQLDB is used as a database backend and it requires an exclusive access to the database files. So, that’s why we have to detect the other BlogBridge instance presence and warn a user.
We had some some list of strategies to choose from, not so long though:
- Network-based locking
- Socket-based locking
- File-based locking
The network-based locking works very simple. It notifies some service on the net during the startup and continues to ping it periodically during the whole run time. When starting, however, it asks service if it is alone. Service checks the date of the last ping and decides whether it happened long ago enough. If it was the permission to start is granted. If not the application displays some message and quits. This approach appeared to be the most unsuitable in our context. While it works pretty good for protecting some network resource, it completely failing to help in our case when the resource (database files) is located locally and a user may try to start another application copy when offline.
The socket-based locking works through installing a simple listener on the local port for telling “hi”. When the application starts it makes an attempt to connect to this well-known port and if it finds someone sitting there it knows that the other instance is running. Of course, sometimes a port collision may occur and there will be another software product using the same port. For this purpose the ports range can be used instead of a single port. When starting, the application scans a given ports range for the listeners telling some code word. If none found, installs its own listener in the first free cell and continues normal startup. If found one, reports and exits.
One important drawback of this approach is that it installs a listener on a local port which is for unknown ports is an action considered to be offensive by the most anti-virus products. They think that you are installing some trojan horse to get an unauthorized access to system resources. That’s the reason why we had to stop using this model in our application and switch to the next option.
The file-based locking is the most obvious way, working good only for designing local singletons. Originally, the idea was to create some lock file and delete it on exit. The presence of this file tells us that there’s another instance already running. Despite its simplicity the approach has serious pitfall. What will happen if an application terminates unexpectedly and will not be able to delete the lock file? Yes, it will never start again without the lock removed manually. However, everything is not that gloomy.
I’ve seen this approach in HSQLDB first and I loved it. It doesn’t mean that they are the farthers of it, it just means that it was me who have seen it for first time in my life. They applied some modifications to the original file-based locking to protect it from the mentioned pitfall. During the application run it updates the modification time of the lock file periodically. When the application starts it checks this modification time and compares to the local time. If the difference is bigger than the period (or 2 periods for better safety) of updates it starts, if not — warns and exits. The period can be 3-5 seconds to make it convenient for a user. Why for a user? Because if a user terminates an application abnormally and then tries to start it again, the application will not start immediately. The time of the last lock file touching was not long ago enough and the warning message will be shown. That’s why it’s better to have short periods of touching lock file.
Of course, there can be lots of other ways of establishing the same instance locking effect, but for now we stopped on this last one, file-based, approach. Let’s see how it goes.