Sushmita Bhattacharya
Sushmita Bhattacharya

Reputation: 463

Multiple go routines waiting for a shared Timer causing race

I have a requirement to update timer of a shared object in multiple go routines. But it end up with race condition. I cannot use lock for waiting on the channel because all the other routines will have to wait.

package main
import( 
    "time"
    "math/rand"
)
type R struct {
    timer *time.Timer
    //other fields
}

func f(done chan bool,r *R){
    r.timer =time.NewTimer(time.Millisecond * time.Duration(1000 + rand.Intn(2)))
    //some code simultaneously accessing other fields of shared object r, cannot put a lock here
    <-r.timer.C

    done <- true
}

func main(){
    done := make(chan bool , 5)
    var r *R
    var t *time.Timer
    r = &R{timer:t}
    for i:=0;i<5;i++{
        go f(done,r)
    }
    for i:=0;i<5;i++{
        <-done
    }
}

when I run using

 go run -race thread.go

it gives

==================

WARNING: DATA RACE
Write by goroutine 5:
  main.f()
      usr/local/gocode/thread.go:12 +0x69

Previous write by goroutine 4:
  main.f()
      usr/local/gocode/thread.go:12 +0x69

Goroutine 5 (running) created at:
  main.main()
      usr/local/gocode/thread.go:25 +0xd3

Goroutine 4 (running) created at:
  main.main()
      usr/local/gocode/thread.go:25 +0xd3
==================

and hangs

any help would be useful

Upvotes: 1

Views: 870

Answers (1)

Not_a_Golfer
Not_a_Golfer

Reputation: 49265

There is a design issue here - you have one R object, and it has a shared instance, but every goroutine creates a new timer that is local. Seems to me like you need a local timer per goroutine, and not share that timer between all of them, it just doesn't make sense.

If you rewrite your code like so:

type R struct {
    //other fields
    Foo string
    Bar interface{}
}

func f(done chan bool, r *R) {
    timer := time.NewTimer(time.Millisecond * time.Duration(1000+rand.Intn(2)))
    //some code simultaneously accessing other fields of shared object r, cannot put a lock here
    <-timer.C

    done <- true
}

the timer becomes local to the goroutine as it should be, and you have no race condition, at least for the timer access.

Note that still, ever access to the shared object's other fields must be protected by a mutex or you'll get the same issue.

Upvotes: 3

Related Questions