Friday, 20 November 2015

APC and Me

Fig1. An APC logo.

When it was decided that Zend's Optimizer Plus would be merged into PHP, APC was already in a pretty poor state, there hadn't been a stable release for quite some time.

We were moving towards having a built in (abandoned in php-src/ext) opcode caching solution but it was not obvious that APC was going to keep being maintained.

I think all of us assumed that the bugs that were being experienced were entirely down to the opcode caching parts of APC, since they were the most complex and most frequently altered parts of APC.

So, one day (few nights), I stripped APC of opcode caching.

One of the things I decided I would tidy up was the implementation and usage of locking.

The supported kinds of locks are:
  • File locking
  • Mutex
  • Read/Write Locks
  • Spin Locks
Brief explanations follow.

File Locking

I'm going to assume that everybody reading knows what this is, and even without experience, can sense that it is probably the most inferior in the list.

I sincerely hope that nobody uses file locking today.

The reason it exists is because people deploy PHP in all kinds of places, places we don't get to hear about until something goes wrong. Those places might not have support for anything other than file locking, so it stays.


This is your most basic kind of synchronization. Mutex means Mutual Exclusion, so we know that this kind of synchronization is exclusive.

This was the default locking for APC

Read/Write Locks

Read/Write locking allows a shared lock to be acquired for reading, this means many readers can be supported without exclusion. An exclusive lock is only required for writing.

This is the default for APCu

Spin Locks

Speaking as a programmer who spends a lot of time writing multi-threaded code, in various languages: A spin lock is about the worst kind of synchronization imaginable, it is basically a predicated busy wait loop.

This remains in APCu for the same reason file locking does, and I actually have heard of people using it and can't convince them to do otherwise.

Scary Things

I didn't know the APC code base before that first night, had never read any of it before.

Whenever a programmer reads code from a prospective project that another programmer wrote, they have criticisms. Some of them are just our ego talking, some of them are wrong, some of them are probably a mechanism to motivate us to keep working. Hardly any of them are worth mentioning, or doing anything about.

The task at hand is to get the thing working, not fix every problem that nobody ever had.

So I cleaned up, I worked on it for a few days and pushed it to pecl.


So, this blog post started out as something completely different.

APCu was demonstrably unstable, and I thought this was because it broke a basic rule. It seemed to acquire a read lock, when it should be acquiring a write lock.

There seemed to be a clear path to race conditions, in other words.

While trying to find the code in the APC code base that caused the problem I made this search: apc_cache_release

The APCu version omits atomicity, it omits safety; In my haste to clean up, I have made a horrible mistake.

There was even a pull request, from one of the elders of PHP, that I chose not to merge, for three years.

The worst thing about being wrong is that I inevitably feel dumb as rocks.

The best thing about being wrong is things that didn't make sense before, like this, or this, and many other bugs besides, start to make sense.

So, I've found the reason that APCu was unstable, it was me.

This probably caused a problem for a fairly large number of people.

Sorry about that, I'll get to fixing it ...

I'm afraid I don't have time to blog now, too much work to do :(