development

Concurrent Modification Exceptions on Android’s Shared Preferences

Last month during our quarterly bug bash, I was digging through our crash reports in Fabric and saw this common exception: ConcurrentModificationException (CME). Fixing this bug should be trivial, or so I thought. The problem with this exception is that it’s pretty open ended and identifying where it’s happening can be tricky. Sometimes it’s as simple as fixing an iterator while we’re modifying a collection but in our case it ended up being a little more complex.

Screen Shot 2016-01-28 at 10.32.18 AM

Just to take a step back, the name itself is a little misleading, in particular the whole “concurrent” part. As instinct kicks in, we go off and search for a case where two threads are simultaneously modifying a shared resource, but in reality, the problem at hand is much simpler. Most of us have hit this issue at least one point in our programming lives without ever touching concurrency.

For these cases, we are modifying the length of the ArrayList as it’s being iterated through. It throws this error because we are accessing properties of the collection but also changing these properties within the same iteration; hence, there’s a read/write issue. To fix this, we want to use iterator.remote() or loop through these items in reverse.

 

Pretty simple fix but understanding this case helped us tackle a more complex bug that we recently found in mCent.

We were accessing a string set from a shared preference in Android.

Nothing fancy, we’re just iterating through the set and reading it. This code by itself is fine and works as expected.

But what if another thread comes in, overlaps execution, and modifies the same exact string set. Then we have a potential CME. This time, our initial instinct is correct; this issue has to do with two threads modifying the same resource. One is reading while the other is writing. It’s essentially putting a ticking time bomb in our code.

Looking at the Android docs, we noticed our first mistake – we are modifying a reference to the string set and as the docs tell us, we shouldn’t trust that the changes persist.

About SharedPreferences getStringSet method:

“Note that you must not modify the set instance returned by this call. The consistency of the stored data is not guaranteed if you do, nor is your ability to modify the instance at all.”

So if we can’t modify the instance returned by this call (assuming it’s a reference), how do we add or remove values to this set? The simple solution is to return a copy of the items. The read operation on thread 1 will read on a separate instance than the one that’s being modified on thread 2. Then we save the newly updated instance into shared preferences.

So we’re done? Not quite. As you probably noticed, this uses unnecessary memory because we have to create a copy of the instance each time we access or modify it. Also, let’s think about the case where we would have more than one thread writing to the set concurrently. We have to be careful about overwriting or losing data.

Let’s say for example that shared preferences (SP) contains  = {“Dog”, “Mouse”, “Rabbit”} initially and that both thread 1 and thread 2 are going to write to this string set in shared preferences.

  1. Thread 1:
    • Reads the shared pref. Create copy called T1 = {“Dog”, “Mouse”, “Rabbit”}
  2. Thread 2:
    • Reads the shared pref. Create copy called T2 = {“Dog”, “Mouse”, “Rabbit”}
  3. Thread 1:
    • Adds an item “Wallie” to T1= {“Dog”, “Mouse”, “Rabbit”, “Wallie”}
  4. Thread 2:
    • Adds an item “Boogie Monster” to T2 = {“Dog”, “Mouse”, “Rabbit”, “Boogie Monster”}
  5. Thread 1:
    • Saves T1 into the shared preferences SP = {“Dog”, “Mouse”, “Rabbit”, “Wallie”}
  6. Thread 2:
    • Saves T2 into shared preferences SP = {“Dog”, “Mouse”, “Rabbit”, “Boogie Monster”}

In the end, shared preferences contains {“Dog”, “Mouse”, “Rabbit”, “Boogie Monster”}. Wallie is sadly left behind.

A more elegant solution is to use a thread-safe data type that will handle concurrency without too much overhead: ConcurrentSkipListSet. According to the documentation:

Insertion, removal, and access operations safely execute concurrently by multiple threads. Iterators are weakly consistent, returning elements reflecting the state of the set at some point at or since the creation of the iterator. They do not throw a ConcurrentModificationException, and may proceed concurrently with other operations.

In that case, we create a shared resource which all the threads can use.

As you can see, we create the copy only once (to get away from referencing the set from shared preferences directly) and then we can read and write to it without fear. If multiple threads modify the set simultaneously, then all the changes will get saved.

Screen Shot 2016-01-28 at 2.50.44 PM

And as a bonus, it’s always fun to close an issue on Fabric.

Love this stuff? Join us!

Discussion

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s