Race Condition in Golang
Tags: go, codenewbie, beginners, opensource
Published: 2022-02-26 19:19:50

> This article was originally posted at [ioscript.org](https://ioscript.org/race-condition-in-golang)

Writing a concurrent code is often a difficult task. often programmers working with concurrent code, face similar kinds of bugs. One of them is inconsistency while manipulating the data using concurrent goroutines.

Let's see an example. 

```
package main

import (
	"fmt"
	"sync"
)

var msg string
var wg sync.WaitGroup

func foo() {
	defer wg.Done()
	msg = "Hello Universe"
}

func bar() {
	defer wg.Done()
	msg = "Hello Cosmos"
}

func main() {

	msg = "Hello world"

	wg.Add(2)
	go foo()
	go bar()

	wg.Wait()
	fmt.Println(msg)

}

```
> Note: If you are not familiar with Goroutines and  WaitGroups, I recommend checking out our course on [Concurrency in Golang](https://ioscript.org/series/go-concurrency)


In the above code example, we are deploying two goroutines named `foo` and `bar`. Both goroutines are updating a variable named message to `"Hello Universe"` and `"Hello Cosmos"` accordingly. When we run this code, It will deploy the two goroutines. These goroutines eventually update the value of msg and join back to the `main` goroutine. Since two goroutines are updating the same variable in the above code, we cannot determine the output of the `fmt.Println(msg)`, i.e, It will either print `"Hello Universe"` or `"Hello Cosmos"`. Since we cannot determine the value stored in the `msg` variable, this will be a serious bug in our code that can break our software.


Let's see another example with a real-life scenario. Michael and his wife have a joint bank account, they use the same account to make purchases. One fine day, Michael went to have coffee at Starbucks 🥤🥤. Meanwhile, her wife went shopping and purchased a beautiful dress 👗👗 for herself. They paid their bills simultaneously, But the bank software was not implemented correctly which resulted in an inconsistency in their balance amount at the bank.


Let's have a look at the bank software's code and see what went wrong.

```
package main

import (
	"fmt"
	"sync"
)

var bankBalance int
var wg sync.WaitGroup

func Purchase(purchaseAmount int) {
	defer wg.Done()

	value := bankBalance
	value = value - purchaseAmount
	bankBalance = value
}

func main() {

	//initially Michael and his wife had $1000 USD in their bank account
	bankBalance = 1000

	//they made bill payment simultaneously
	wg.Add(2)

	go Purchase(5)
	go Purchase(157)

	wg.Wait()

	//final amount in there bank account
	fmt.Println("Final amount : ", bankBalance)
}


```

When we run the above code, It can result in `995`, `838` or `843`. Final values completely depend on the order of execution of the `Purchase` goroutine. 

The above case is called Race condition in programming.

## Race Condition

A race condition occurs when two or more concurrent goroutines try to access and modify the same data. For example, if one goroutine tries to read a variable, meanwhile other goroutines are trying to update the value of the same variable.

Race condition mostly occurs, if the developer thinks the concurrent program is executing sequentially. In the above example of bank software code, the developer might have assumed `go Purchase(5)` will complete its execution first than `go Purchase(157)`.

**How to detect race conditions in GO ?**

Go provides built-in support to tackle the Race condition issue. We can use the `-race` flag while compiling or building our code to detect the race conditions in our program.

Let's run our code above with the `-race` flag to detect where the race condition is actually happening.

```
sonukumarsaw@legion-5PRO:concurrency$ go run -race main.go
==================
WARNING: DATA RACE
Read at 0x0000005d4400 by goroutine 7:
  main.Purchase()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:14 +0x74
  main.main·dwrap·2()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:27 +0x39

Previous write at 0x0000005d4400 by goroutine 8:
  main.Purchase()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:16 +0x8c
  main.main·dwrap·3()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:28 +0x39

Goroutine 7 (running) created at:
  main.main()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:27 +0x90

Goroutine 8 (finished) created at:
  main.main()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:28 +0xd2
==================
Final amount : 838
Found 1 data race(s)
exit status 66
```

Well, there's a lot of things going on here. Let's go through it step by step. First of all, we **Found 1 data race**. Now let's see the most important lines 
```
Goroutine 7 (running) created at:
  main.main()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:27 +0x90

Goroutine 8 (finished) created at:
  main.main()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:28 +0xd2
```

Above two message shows, two goroutines are deployed named **`Goroutine 7`** at line 27, i.e, `go Purchase(5)` and **`Goroutine 8`** at line 28, i.e, `go Purchase(157)`. Also, `Goroutine 8` has already completed its execution while `Goroutine 7` is still running.

Now let's see the next part. 

```
Read at 0x0000005d4400 by goroutine 7:
  main.Purchase()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:14 +0x74
  main.main·dwrap·2()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:27 +0x39

Previous write at 0x0000005d4400 by goroutine 8:
  main.Purchase()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:16 +0x8c
  main.main·dwrap·3()
      /home/sonukumarsaw/alpha/ioScript/concurrency/main.go:28 +0x39
```
It clearly states that **`Read at 0x0000005d4400 by goroutine 7:`** and **`Previous write at 0x0000005d4400 by goroutine 8:`**. This means Goroutine 7 is trying to read the same memory address `0x0000005d4400` which was earlier written by Goroutine 8. 

Even though, we could detect the race condition and its cause. We can still see the correct amount in the output i.e, `Final amount : 838`. 

Let's make some changes in our code to make sure, our code returns the incorrect amount. 


```
package main

import (
	"fmt"
	"sync"
)

var bankBalance int
var wg sync.WaitGroup

func Purchase(purchaseAmount int) {
	defer wg.Done()

	value := bankBalance
	time.Sleep(1 * time.Second)                 // some delay in processing the calculation
	value = value - purchaseAmount
	bankBalance = value
}


func main() {

	//initially Michael and his wife had $1000 USD in their bank account
	bankBalance = 1000

	//they made bill payment simultaneously
	wg.Add(2)

	go Purchase(5)
	go Purchase(157)

	wg.Wait()

	//final amount in there bank account
	fmt.Println("Final amount : ", bankBalance)
}


```

We have added `time.Sleep()` to imitate a processing delay. Now when we run this program, we will never get the correct amount in the output. 

**How to fix the race condition ?**

**To avoid race conditions in our program, any operation in a shared variable, must be executed atomically**. Shared variables are those variables that are shared among the goroutines.

We can execute a program atomically by locking the critical section, a section of the code where shared variables are being manipulated. Once, the execution of the critical section is completed by one goroutine, other goroutines can acquire the lock and complete its execution as well.

We can use go's built-in `Mutex` lock. Mutex lock is a part of the `sync` package. We will see how to fix our code using Mutex lock in our next article. 

## Before You Leave

If you found this article valuable, you can support us by dropping a like and sharing this article with your friends.

You can sign up for our newsletter to get notified whenever we post awesome content on Golang.



**Reference**

- [Go docs](https://go.dev/) 

- [Go in Action](https://g.co/kgs/ye6b6C)

- [GoByExample](https://gobyexample.com/)

- [Concurrency in Go](https://g.co/kgs/8DKFH5)



![Frame 1.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1645039088124/j9q9kQHgH.png)