// Copyright 2011 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"
	"time"
)

func TestNoRaceChanSync(t *testing.T) {
	v := 0
	c := make(chan int)
	go func() {
		v = 1
		c <- 0
	}()
	<-c
	v = 2
}

func TestNoRaceChanSyncRev(t *testing.T) {
	v := 0
	c := make(chan int)
	go func() {
		c <- 0
		v = 2
	}()
	v = 1
	<-c
}

func TestNoRaceChanAsync(t *testing.T) {
	v := 0
	c := make(chan int, 10)
	go func() {
		v = 1
		c <- 0
	}()
	<-c
	v = 2
}

func TestRaceChanAsyncRev(t *testing.T) {
	v := 0
	c := make(chan int, 10)
	go func() {
		c <- 0
		v = 1
	}()
	v = 2
	<-c
}

func TestNoRaceChanAsyncCloseRecv(t *testing.T) {
	v := 0
	c := make(chan int, 10)
	go func() {
		v = 1
		close(c)
	}()
	func() {
		defer func() {
			recover()
			v = 2
		}()
		<-c
	}()
}

func TestNoRaceChanAsyncCloseRecv2(t *testing.T) {
	v := 0
	c := make(chan int, 10)
	go func() {
		v = 1
		close(c)
	}()
	_, _ = <-c
	v = 2
}

func TestNoRaceChanAsyncCloseRecv3(t *testing.T) {
	v := 0
	c := make(chan int, 10)
	go func() {
		v = 1
		close(c)
	}()
	for _ = range c {
	}
	v = 2
}

func TestNoRaceChanSyncCloseRecv(t *testing.T) {
	v := 0
	c := make(chan int)
	go func() {
		v = 1
		close(c)
	}()
	func() {
		defer func() {
			recover()
			v = 2
		}()
		<-c
	}()
}

func TestNoRaceChanSyncCloseRecv2(t *testing.T) {
	v := 0
	c := make(chan int)
	go func() {
		v = 1
		close(c)
	}()
	_, _ = <-c
	v = 2
}

func TestNoRaceChanSyncCloseRecv3(t *testing.T) {
	v := 0
	c := make(chan int)
	go func() {
		v = 1
		close(c)
	}()
	for _ = range c {
	}
	v = 2
}

func TestRaceChanSyncCloseSend(t *testing.T) {
	v := 0
	c := make(chan int)
	go func() {
		v = 1
		close(c)
	}()
	func() {
		defer func() {
			recover()
		}()
		c <- 0
	}()
	v = 2
}

func TestRaceChanAsyncCloseSend(t *testing.T) {
	v := 0
	c := make(chan int, 10)
	go func() {
		v = 1
		close(c)
	}()
	func() {
		defer func() {
			recover()
		}()
		for {
			c <- 0
		}
	}()
	v = 2
}

func TestRaceChanCloseClose(t *testing.T) {
	compl := make(chan bool, 2)
	v1 := 0
	v2 := 0
	c := make(chan int)
	go func() {
		defer func() {
			if recover() != nil {
				v2 = 2
			}
			compl <- true
		}()
		v1 = 1
		close(c)
	}()
	go func() {
		defer func() {
			if recover() != nil {
				v1 = 2
			}
			compl <- true
		}()
		v2 = 1
		close(c)
	}()
	<-compl
	<-compl
}

func TestRaceChanSendLen(t *testing.T) {
	v := 0
	c := make(chan int, 10)
	go func() {
		v = 1
		c <- 1
	}()
	for len(c) == 0 {
		runtime.Gosched()
	}
	v = 2
}

func TestRaceChanRecvLen(t *testing.T) {
	v := 0
	c := make(chan int, 10)
	c <- 1
	go func() {
		v = 1
		<-c
	}()
	for len(c) != 0 {
		runtime.Gosched()
	}
	v = 2
}

func TestRaceChanSendSend(t *testing.T) {
	compl := make(chan bool, 2)
	v1 := 0
	v2 := 0
	c := make(chan int, 1)
	go func() {
		v1 = 1
		select {
		case c <- 1:
		default:
			v2 = 2
		}
		compl <- true
	}()
	go func() {
		v2 = 1
		select {
		case c <- 1:
		default:
			v1 = 2
		}
		compl <- true
	}()
	<-compl
	<-compl
}

func TestNoRaceChanPtr(t *testing.T) {
	type msg struct {
		x int
	}
	c := make(chan *msg)
	go func() {
		c <- &msg{1}
	}()
	m := <-c
	m.x = 2
}

func TestRaceChanWrongSend(t *testing.T) {
	v1 := 0
	v2 := 0
	c := make(chan int, 2)
	go func() {
		v1 = 1
		c <- 1
	}()
	go func() {
		v2 = 2
		c <- 2
	}()
	time.Sleep(1e7)
	if <-c == 1 {
		v2 = 3
	} else {
		v1 = 3
	}
}

func TestRaceChanWrongClose(t *testing.T) {
	v1 := 0
	v2 := 0
	c := make(chan int, 1)
	go func() {
		defer func() {
			recover()
		}()
		v1 = 1
		c <- 1
	}()
	go func() {
		time.Sleep(1e7)
		v2 = 2
		close(c)
	}()
	time.Sleep(2e7)
	if _, who := <-c; who {
		v2 = 2
	} else {
		v1 = 2
	}
}

func TestRaceChanSendClose(t *testing.T) {
	compl := make(chan bool, 2)
	c := make(chan int, 1)
	go func() {
		defer func() {
			recover()
			compl <- true
		}()
		c <- 1
	}()
	go func() {
		time.Sleep(10 * time.Millisecond)
		close(c)
		compl <- true
	}()
	<-compl
	<-compl
}

func TestRaceChanSendSelectClose(t *testing.T) {
	compl := make(chan bool, 2)
	c := make(chan int, 1)
	c1 := make(chan int)
	go func() {
		defer func() {
			recover()
			compl <- true
		}()
		time.Sleep(10 * time.Millisecond)
		select {
		case c <- 1:
		case <-c1:
		}
	}()
	go func() {
		close(c)
		compl <- true
	}()
	<-compl
	<-compl
}

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

	queue := make(chan Task)

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

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

	x := 0
	doit(func() {
		x = 1
	})
	_ = x
}

func TestRaceChanItselfSend(t *testing.T) {
	compl := make(chan bool, 1)
	c := make(chan int, 10)
	go func() {
		c <- 0
		compl <- true
	}()
	c = make(chan int, 20)
	<-compl
}

func TestRaceChanItselfRecv(t *testing.T) {
	compl := make(chan bool, 1)
	c := make(chan int, 10)
	c <- 1
	go func() {
		<-c
		compl <- true
	}()
	time.Sleep(1e7)
	c = make(chan int, 20)
	<-compl
}

func TestRaceChanItselfNil(t *testing.T) {
	c := make(chan int, 10)
	go func() {
		c <- 0
	}()
	time.Sleep(1e7)
	c = nil
	_ = c
}

func TestRaceChanItselfClose(t *testing.T) {
	compl := make(chan bool, 1)
	c := make(chan int)
	go func() {
		close(c)
		compl <- true
	}()
	c = make(chan int)
	<-compl
}

func TestRaceChanItselfLen(t *testing.T) {
	compl := make(chan bool, 1)
	c := make(chan int)
	go func() {
		_ = len(c)
		compl <- true
	}()
	c = make(chan int)
	<-compl
}

func TestRaceChanItselfCap(t *testing.T) {
	compl := make(chan bool, 1)
	c := make(chan int)
	go func() {
		_ = cap(c)
		compl <- true
	}()
	c = make(chan int)
	<-compl
}

func TestRaceChanCloseLen(t *testing.T) {
	v := 0
	c := make(chan int, 10)
	c <- 0
	go func() {
		v = 1
		close(c)
	}()
	time.Sleep(1e7)
	_ = len(c)
	v = 2
}

func TestRaceChanSameCell(t *testing.T) {
	c := make(chan int, 1)
	v := 0
	go func() {
		v = 1
		c <- 42
		<-c
	}()
	time.Sleep(1e7)
	c <- 43
	<-c
	_ = v
}

func TestRaceChanCloseSend(t *testing.T) {
	compl := make(chan bool, 1)
	c := make(chan int, 10)
	go func() {
		close(c)
		compl <- true
	}()
	c <- 0
	<-compl
}
