Home > Software engineering >  Channel race condition
Channel race condition

Time:12-22

This locks like a rather very basic problem regarding to channels. I have read that unbuffered channels block on send, so why this code has a race-condition?

Sometimes the output is

<- CREATE;
<- INSERT;
END
[CREATE; INSERT;]

Sometime the "INSERT;" is missing from the output, but it's written to the channel.

<- CREATE;
<- INSERT;
END
[CREATE; INSERT;]

How can the SplitChan func return, if it blocks on cmd := <-c from Slit?

package main

import "fmt"

func main() {
    batch := `CREATE;INSERT;`
    res := Split(batch)
    fmt.Print(res)
}

func SplitChan(batch string, outc chan<- string) {
    b := 0
    for i, c := range batch {
        switch c {
        case ';':
            cmd := batch[b : i 1]
            fmt.Println("<- "   cmd)
            b = i   1
            outc <- cmd
        }
    }

    fmt.Println("END")
}

func Split(batch string) []string {
    var res []string
    c := make(chan string)
    go func() {
        for {
            cmd := <-c
            res = append(res, cmd)
        }
    }()

    SplitChan(batch, c)
    close(c)
    return res
}

Playground Link: https://go.dev/play/p/WmO5OtmgETl

I am expected the same output on every run:

<- CREATE;
<- INSERT;
END
[CREATE; INSERT;]

What am I missing here? Thank you

CodePudding user response:

When SplitChan writes to the channel, the append that's happening at the goroutine and the statement return res are concurrent. return res may see the slice with one element or two elements.

You have to make sure that before returning, the append operation is completed:

    wg:=sync.WaitGroup{}
    wg.Add(1)
    go func() {
        defer wg.Done()
        for cmd :=  range c {
            res = append(res, cmd)
        }
    }()

    SplitChan(batch, c)
    close(c)
    wg.Wait()
    return res

The WaitGroup ensures that the function returns after the goroutine is done.

  • Related