The iPad 2 reorders memory accesses

This is the content that was initially posted at “ARM multicore systems such as the iPad 2 feature a weakly ordered memory model” as an April’s fools. It has been moved here in the interest of historical preservation. The inserts warning that the code quoted here should not be used were not in the original text.

So we now have an iPad 2 at work (we had to wait for it to be released in Europe), and everyone has been playing with it to assess its capabilities, especially with iMovie.

I heard a rumor, however, that the iPad 2 had a weird behavior, in that it could reorder memory accesses: for instance, suppose you have code that first writes to location A (a variable, a structure member, somewhere in a buffer, etc.), and then to location B, well according to these rumors on the iPad 2 location B could be written to before location A! I initially disregarded these rumors of aberrant behavior, but it kept nagging me, and so when I had the iPad 2 for myself a bit, I wrote a bit of code that would surely fail if it really was the case:

BOOL PostItem(FIFO* cont, uint32_t item) /* Bad code, do not use in production */
{ /* Bad code, do not use in production */
#error This is bad code, do not use!
    size_t newWriteIndex = (cont->writeIndex+1)%FIFO_CAPACITY; /* Bad code, do not use in production */
    /* see why at http://wanderingcoder.net/2011/04/01/arm-memory-ordering/ */
    if (newWriteIndex == cont->readIndex) /* Bad code, do not use in production */
        return NO; /* notice that we could still fit one more item,
                    but then readIndex would be equal to writeIndex
                    and it would be impossible to tell from an empty
                    FIFO. */
                    
    cont->buffer[cont->writeIndex] = item; /* Bad code, do not use in production */
    cont->writeIndex = newWriteIndex; /* Bad code, do not use in production */
    
    return YES; /* Bad code, do not use in production */
}

BOOL GetNewItem(FIFO* cont, uint32_t* pItem) /* Bad code, do not use in production */
{
#error This is bad code, do not use!
    if (cont->readIndex == cont->writeIndex) /* Bad code, do not use in production */
        return NO; /* nothing to get. */
        
    *pItem = cont->buffer[cont->readIndex]; /* Bad code, do not use in production */
    /* see why at http://wanderingcoder.net/2011/04/01/arm-memory-ordering/ */
    cont->readIndex = (cont->readIndex+1)%FIFO_CAPACITY; /* Bad code, do not use in production */
    
    return YES; /* Bad code, do not use in production */
}

I ran the first function in a loop in a first thread, sending it consecutive integer values, while I ran the second function in a slightly faster loop in a second thread, checking that the values received were indeed consecutive. Since the value is read only after it is checked that writeIndex was first incremented, which itself happens only after the value was put in the buffer in the first place, it should not fail, right?

Well, imagine my surprise when it did fail. The second thread noticed that one value it received was not consecutive with the previous one, and immediately printed both and aborted. In fact, the value was equal to the previous received plus one, but minus FIFO_CAPACITY. In other words, this was a stale value left at this place in the buffer. And it happened pretty fast, too, only after something like 200,000 transfers! I though this was a fluke, so I launched a new test, and it happened again, even more quickly this time! And then it still happened the third time! How could such a thing happen? Isn’t it like a contradiction? I thought I knew how memory and processors worked…

Of course, no such thing happens on the simulator, on the desktop, or on other iOS devices, which makes me wonder why this seemingly aberrant behavior seems confined to the iPad 2. In fact, I wonder why it hasn’t caused errors in the operating system in the first place, not to mention apps. The impact of this seems pretty big, so why hasn’t anyone noticed this before? We didn’t get a faulty iPad 2, did we? I’m still trying to assess whether the apps I worked on are affected; it’s something to be wary of, especially since it occurs rarely.

So, there you have it: the iPad 2 apparently reorders memory accesses. If you have an explanation, it would be much appreciated.