// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package race_test

import (
	"runtime"
	"testing"
)

func TestNoRaceSelect1(t *testing.T) {
	var x int
	compl := make(chan bool)
	c := make(chan bool)
	c1 := make(chan bool)

	go func() {
		x = 1
		// At least two channels are needed because
		// otherwise the compiler optimizes select out.
		// See comment in runtime/chan.c:^selectgo.
		select {
		case c <- true:
		case c1 <- true:
		}
		compl <- true
	}()
	select {
	case <-c:
	case c1 <- true:
	}
	x = 2
	<-compl
}

func TestNoRaceSelect2(t *testing.T) {
	var x int
	compl := make(chan bool)
	c := make(chan bool)
	c1 := make(chan bool)
	go func() {
		select {
		case <-c:
		case <-c1:
		}
		x = 1
		compl <- true
	}()
	x = 2
	close(c)
	runtime.Gosched()
	<-compl
}

func TestNoRaceSelect3(t *testing.T) {
	var x int
	compl := make(chan bool)
	c := make(chan bool, 10)
	c1 := make(chan bool)
	go func() {
		x = 1
		select {
		case c <- true:
		case <-c1:
		}
		compl <- true
	}()
	<-c
	x = 2
	<-compl
}

func TestNoRaceSelect4(t *testing.T) {
	type Task struct {
		f    func()
		done chan bool
	}

	queue := make(chan Task)
	dummy := make(chan bool)

	go func() {
		for {
			select {
			case t := <-queue:
				t.f()
				t.done <- true
			}
		}
	}()

	doit := func(f func()) {
		done := make(chan bool, 1)
		select {
		case queue <- Task{f, done}:
		case <-dummy:
		}
		select {
		case <-done:
		case <-dummy:
		}
	}

	var x int
	doit(func() {
		x = 1
	})
	_ = x
}

func TestNoRaceSelect5(t *testing.T) {
	test := func(sel, needSched bool) {
		var x int
		ch := make(chan bool)
		c1 := make(chan bool)

		done := make(chan bool, 2)
		go func() {
			if needSched {
				runtime.Gosched()
			}
			// println(1)
			x = 1
			if sel {
				select {
				case ch <- true:
				case <-c1:
				}
			} else {
				ch <- true
			}
			done <- true
		}()

		go func() {
			// println(2)
			if sel {
				select {
				case <-ch:
				case <-c1:
				}
			} else {
				<-ch
			}
			x = 1
			done <- true
		}()
		<-done
		<-done
	}

	test(true, true)
	test(true, false)
	test(false, true)
	test(false, false)
}

func TestRaceSelect1(t *testing.T) {
	var x int
	compl := make(chan bool, 2)
	c := make(chan bool)
	c1 := make(chan bool)

	go func() {
		<-c
		<-c
	}()
	f := func() {
		select {
		case c <- true:
		case c1 <- true:
		}
		x = 1
		compl <- true
	}
	go f()
	go f()
	<-compl
	<-compl
}

func TestRaceSelect2(t *testing.T) {
	var x int
	compl := make(chan bool)
	c := make(chan bool)
	c1 := make(chan bool)
	go func() {
		x = 1
		select {
		case <-c:
		case <-c1:
		}
		compl <- true
	}()
	close(c)
	x = 2
	<-compl
}

func TestRaceSelect3(t *testing.T) {
	var x int
	compl := make(chan bool)
	c := make(chan bool)
	c1 := make(chan bool)
	go func() {
		x = 1
		select {
		case c <- true:
		case c1 <- true:
		}
		compl <- true
	}()
	x = 2
	select {
	case <-c:
	}
	<-compl
}

func TestRaceSelect4(t *testing.T) {
	done := make(chan bool, 1)
	var x int
	go func() {
		select {
		default:
			x = 2
		}
		done <- true
	}()
	_ = x
	<-done
}

// The idea behind this test:
// there are two variables, access to one
// of them is synchronized, access to the other
// is not.
// Select must (unconditionaly) choose the non-synchronized variable
// thus causing exactly one race.
// Currently this test doesn't look like it accomplishes
// this goal.
func TestRaceSelect5(t *testing.T) {
	done := make(chan bool, 1)
	c1 := make(chan bool, 1)
	c2 := make(chan bool)
	var x, y int
	go func() {
		select {
		case c1 <- true:
			x = 1
		case c2 <- true:
			y = 1
		}
		done <- true
	}()
	_ = x
	_ = y
	<-done
}

// select statements may introduce
// flakiness: whether this test contains
// a race depends on the scheduling
// (some may argue that the code contains
// this race by definition)
/*
func TestFlakyDefault(t *testing.T) {
	var x int
	c := make(chan bool, 1)
	done := make(chan bool, 1)
	go func() {
		select {
		case <-c:
			x = 2
		default:
			x = 3
		}
		done <- true
	}()
	x = 1
	c <- true
	_ = x
	<-done
}
*/
