Thread-safe Code Through TDD

Alessandro Mautone
Feb 15 · 4 min read
Photo by aisvri on Unsplash

Today I am going to write about something I came across recently: making some already existing code thread-safe.

What does thread-safe mean? 🤓

Looking on Google we can find many similar definitions:

Thread-safety or thread-safe code in Java refers to code which can safely be used or shared in concurrent or multi-threading environment and they will behave as expected. any code, class, or object which can behave differently from its contract on the concurrent environment is not thread-safe.

So, to put it simply, a piece of code can be defined as thread-safe if is warranted that executing it concurrently or in multiple threads is going to behave as expected.

The project 🔑

I was working on a project recently where I came across this simple code in a repository class:

Analyzing the code, we can see that there is a refreshToken function that:

  • Requests a new token to the backend passing the old one
  • Stores the received information into some sort of storage class (in Android the implementation of this class could be the sharedPreferences or really anything suitable for this job, it is irrelevant for what we want to understand here)

The API states that to refresh the token correctly we need to invoke it passing the latest token we had received from that same API.

There were tests as well for this that were verifying the classes were called as expected (there were tests for failing scenarios too, I am skipping them here as they are not relevant for the topic in discussion):

The problem 💥

As the project grew, this code started being called from multiple places, potentially at the same time, or indeed, concurrently.

This lead to situations where the refreshToken function was called twice roughly at the same time, the first was received by the backend first (as you would expect) and the second as the second but the response of the first, especially in bad/slow connectivity situations, was received after the second one was invoked, so the second invocation of the refreshToken function was still using the old token, generating an error response from the backend.

The solution 💡

After some investigation, I discovered that the refreshToken function was not thread-safe, so the solution that was pretty obvious: we need to make this thread-safe. This will warranty the order of execution, meaning the function would never be called before a previous one completed.

The TDD approach 🔴 ✅

Making the refreshToken function thread-safe is quite easy in Kotlin: we can just add the @Syncrhonized annotation and we are done 🙌
But replicating the scenario were multiple calls were done with an unstable connection is pretty hard, we were actually able to reproduce this bug roughly once every 20 attempts, and those attempts were quite time-consuming.

So I started looking for how to write a test where I could invoke refreshToken concurrently and have a failing test 🔴
In that way I could have solid and fast feedback to work towards, and once I had it green ✅ I could be sure my solution was working.

Looking at coroutines, I found I could launch concurrent code by doing something like:

val coroutinesDispatcher = Executors.newFixedThreadPool(2).asCoroutineDispatcher()val concurrentCalls = listOf(
async(
context = coroutinesDispatcher,
start = CoroutineStart.LAZY
) { // Code we want to execute concurrently },
async(
context = coroutinesDispatcher,
start = CoroutineStart.LAZY
) { // Code we want to execute concurrently },
etc
)
concurrentCalls.awaitAll()

So I came up with the following test:

The setUpConcurrentCalls() function is just what I showed in the previous example, but with the code I needed to execute concurrently:

If we run this test… it fails! 🔴

In the inOrder block I verify how the different steps in the refreshToken should be called, and since the code is not thread-safe (yet), when executing this test storing the token (storeToken(secondReceivedToken)) of the second invocation concurrent invocation of refreshToken happens before the first one storeToken(firstReceivedToken).
Now we are sure we replicated the bug, and we can start working on the solution.

Implementing the solution 🚀

As I mentioned earlier, we can easily add thread-safety by adding

@Synchronized

and indeed, adding that to the refreshToken function makes the tests pass! ✅
So the new implementation will be:

As simple as that!

Alternatives solutions 📚

Synchronized block

@Synchronized is not the only way to go.
Kotlin has a synchronized block as well, in that case the code will become:

This is particularly useful when we need only a certain portion of the function to be thread-safe. In our specific case we didn’t really need it, and I find the @Synchronized more elegant and less intrusive so I went with that.

Others

Other data structures that help in implementing thread-safe code, like Locks, Atomic Primitives, Semaphores, etc. We are not covering those in this article, and you can find more info online 🤓

Example project

The small project with the final result of this exercise can be found at https://github.com/Alexs784/thread-safe-tdd-example

The Startup

Get smarter at building your thing. Join The Startup’s +794K followers.

Sign up for Top 10 Stories

By The Startup

Get smarter at building your thing. Subscribe to receive The Startup's top 10 most read stories — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Alessandro Mautone

Written by

Senior Android Engineer @WeTransfer 🤖 🇻🇪🇮🇹 Paraglider, Runner, Kayaker.

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +794K followers.

Alessandro Mautone

Written by

Senior Android Engineer @WeTransfer 🤖 🇻🇪🇮🇹 Paraglider, Runner, Kayaker.

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +794K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store