PHP lock and thread-synchronization using WinCache and/or APC

PHP lock e semafori con WinCache e/o APC

Table of Contents

Raise your hand if you’re a PHP developer and felt at least once the urge to prevent simultaneous access to a property, function or class method from multiple threads/processes. If you do, you’re in good company, as it is a very common requirement for any maintenance-like script or task. For example, when you want to trigger the execution to a certain DB query during the first user access on each day, without relying to a dedicated cron job.

Truth being told, if you’re stumbled upon this post chances are that you are looking for something like that – a fast and affordable way to implement some thread-synchronization features in your application code, something like what we can achieve in some other programming languages such as C# using locks:

The above code gets straight to the point: we got an object who acts as lock, preventing all threads from going further until it gets released – which happens only when the first-entered thread completes the execution of that very same code block.

Such behaviour is not natively supported by PHP and its process-isolated architecture: a common workaround among developers is to resolve using flock(), which is a IO lock technique based upon the filesystem. While being a rather viable approach, it has at least two major issues: sub-optimal performances and the mandatory use of path, which can easily bring permissions/authorization issues.

Luckily enough there are easier ways to achieve the same results, assuming that the PHP distribution installed on your machine/host a user-caching extensions supporting some key features. The most common ones are:

Either one has its own method to acquire the lock status for the active process/thread: let’s see what it is and how we can implement it in our code.

Notice that OPCache, which is the most used choice for either Windows and Linux, isn’t being included in our list: that’s because – at least for now – the great opcache extension by Zend does not support any user cache we can use to achieve a viable lock. Meaning that, if you’re using OPCache and want to achieve this kind of lock, you’ll also need to install WinCache (in user-cache only mode) or APC and follow the methods explained below.

WinCache

WinCache features the wincache_lock method which, as the name suggests, does exactly what we need. In order to obtain the desired effect you need to do something similar to this:

We could easily shrink it to a couple lines, but unfortunately we cannot rely to a using statement similar to C# so we need to ensure that the wincache_unlock method is being called to avoid deadlocks, hence a try/catch statement with a safety-net unlock is strongly advised. Needless to say, the code will only work if the WinCache extension is installed with its user cache capability enabled.

For further informations on wincache_lock we suggest to take a look to its php.net official page, containing a useful paragraph explaining why you should pay close attention in order to use it avoiding deadlocks or other unwanted issues.

 APC

If you’re using APC you can exploit the mechanics of  apc_add, which – in contrast with apc_store – inserts a key/value pair using a cache-unique logic, meaning that if the key already exists it wouldn’t overwrite it, but it will return FALSE instead. This behaviour easily leads to the following implementation:

Another piece of code that could be easily reduced to a couple lines, shouldn’t the unlock/remove method being handled manually… sadly, we must do that by ourselves.

If you need further information about the APC extension and it’s apc_add / apc_remove methods you can take a look on its php.net official page.

RELATED POSTS

About Ryan

IT Project Manager, Web Interface Architect and Lead Developer for many high-traffic web sites & services hosted in Italy and Europe. Since 2010 it's also a lead designer for many App and games for Android, iOS and Windows Phone mobile devices for a number of italian companies.

View all posts by Ryan