Search code examples
goconcurrencyparallel-processingsynchronizationgoroutine

What problems might arise from ignoring this race condition?


I wish to represent a service that receives triggers to run a particular task, however it only runs that task once at a given time while ignoring concurrent triggers.

My brain came up with an unconventional approach to this problem (it does that sometimes...).

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    once := &sync.Once{}
    var trigger = make(chan int)
    done := make(chan bool)
    onceBody := func(v int) func() {
        return func() {
            trigger <- v
        }
    }
    go func() {
        for {
            select {
            case v := <-trigger:
                fmt.Println("Triggered: ", v)
                once = &sync.Once{}
            case <-done:
                return
            }
        }
    }()
    for i := 0; i < 100000; i++ {
        i := i
        wg.Add(1)
        go func() {
            defer wg.Done()
            once.Do(onceBody(i))
        }()
    }
    wg.Wait()
    done <- true
}

I do understand that there are better solutions to this problem. I only wish to understand what problems might arise from this implementation.

I understand that a type of data race is happening with the once variable, however, I think it might not matter for this particular use-case.

Will the closure' stale pointer be problematic for the gc? What problems might arise from this implementation?


Solution

  • One problem with race conditions is that if you violate the memory model, the effects will depend on the hardware you are running on.

    While most modern CPUs guarantee atomicity for writes to properly aligned words (*sync.Once), there is no guarantee in the language, so your program could read a corrupted, partial written pointer on some exotic platform.

    Even when running on modern CPUs the results of parallel writes and reads depend on the platform you are running on.

    So what you really get and whether your program crashes due to a corrupted pointer depends on where you run this code.