src/runtime/chan_test.go GO 1,221 lines View on github.com → Search inside
1// Copyright 2009 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package runtime_test67import (8	"internal/testenv"9	"math"10	"runtime"11	"sync"12	"sync/atomic"13	"testing"14	"time"15)1617func TestChan(t *testing.T) {18	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))19	N := 20020	if testing.Short() {21		N = 2022	}23	for chanCap := 0; chanCap < N; chanCap++ {24		{25			// Ensure that receive from empty chan blocks.26			c := make(chan int, chanCap)27			recv1 := false28			go func() {29				_ = <-c30				recv1 = true31			}()32			recv2 := false33			go func() {34				_, _ = <-c35				recv2 = true36			}()37			time.Sleep(time.Millisecond)38			if recv1 || recv2 {39				t.Fatalf("chan[%d]: receive from empty chan", chanCap)40			}41			// Ensure that non-blocking receive does not block.42			select {43			case _ = <-c:44				t.Fatalf("chan[%d]: receive from empty chan", chanCap)45			default:46			}47			select {48			case _, _ = <-c:49				t.Fatalf("chan[%d]: receive from empty chan", chanCap)50			default:51			}52			c <- 053			c <- 054		}5556		{57			// Ensure that send to full chan blocks.58			c := make(chan int, chanCap)59			for i := 0; i < chanCap; i++ {60				c <- i61			}62			sent := uint32(0)63			go func() {64				c <- 065				atomic.StoreUint32(&sent, 1)66			}()67			time.Sleep(time.Millisecond)68			if atomic.LoadUint32(&sent) != 0 {69				t.Fatalf("chan[%d]: send to full chan", chanCap)70			}71			// Ensure that non-blocking send does not block.72			select {73			case c <- 0:74				t.Fatalf("chan[%d]: send to full chan", chanCap)75			default:76			}77			<-c78		}7980		{81			// Ensure that we receive 0 from closed chan.82			c := make(chan int, chanCap)83			for i := 0; i < chanCap; i++ {84				c <- i85			}86			close(c)87			for i := 0; i < chanCap; i++ {88				v := <-c89				if v != i {90					t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, i)91				}92			}93			if v := <-c; v != 0 {94				t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, 0)95			}96			if v, ok := <-c; v != 0 || ok {97				t.Fatalf("chan[%d]: received %v/%v, expected %v/%v", chanCap, v, ok, 0, false)98			}99		}100101		{102			// Ensure that close unblocks receive.103			c := make(chan int, chanCap)104			done := make(chan bool)105			go func() {106				v, ok := <-c107				done <- v == 0 && ok == false108			}()109			time.Sleep(time.Millisecond)110			close(c)111			if !<-done {112				t.Fatalf("chan[%d]: received non zero from closed chan", chanCap)113			}114		}115116		{117			// Send 100 integers,118			// ensure that we receive them non-corrupted in FIFO order.119			c := make(chan int, chanCap)120			go func() {121				for i := 0; i < 100; i++ {122					c <- i123				}124			}()125			for i := 0; i < 100; i++ {126				v := <-c127				if v != i {128					t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, i)129				}130			}131132			// Same, but using recv2.133			go func() {134				for i := 0; i < 100; i++ {135					c <- i136				}137			}()138			for i := 0; i < 100; i++ {139				v, ok := <-c140				if !ok {141					t.Fatalf("chan[%d]: receive failed, expected %v", chanCap, i)142				}143				if v != i {144					t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, i)145				}146			}147148			// Send 1000 integers in 4 goroutines,149			// ensure that we receive what we send.150			const P = 4151			const L = 1000152			for p := 0; p < P; p++ {153				go func() {154					for i := 0; i < L; i++ {155						c <- i156					}157				}()158			}159			done := make(chan map[int]int)160			for p := 0; p < P; p++ {161				go func() {162					recv := make(map[int]int)163					for i := 0; i < L; i++ {164						v := <-c165						recv[v] = recv[v] + 1166					}167					done <- recv168				}()169			}170			recv := make(map[int]int)171			for p := 0; p < P; p++ {172				for k, v := range <-done {173					recv[k] = recv[k] + v174				}175			}176			if len(recv) != L {177				t.Fatalf("chan[%d]: received %v values, expected %v", chanCap, len(recv), L)178			}179			for _, v := range recv {180				if v != P {181					t.Fatalf("chan[%d]: received %v values, expected %v", chanCap, v, P)182				}183			}184		}185186		{187			// Test len/cap.188			c := make(chan int, chanCap)189			if len(c) != 0 || cap(c) != chanCap {190				t.Fatalf("chan[%d]: bad len/cap, expect %v/%v, got %v/%v", chanCap, 0, chanCap, len(c), cap(c))191			}192			for i := 0; i < chanCap; i++ {193				c <- i194			}195			if len(c) != chanCap || cap(c) != chanCap {196				t.Fatalf("chan[%d]: bad len/cap, expect %v/%v, got %v/%v", chanCap, chanCap, chanCap, len(c), cap(c))197			}198		}199200	}201}202203func TestNonblockRecvRace(t *testing.T) {204	n := 10000205	if testing.Short() {206		n = 100207	}208	for i := 0; i < n; i++ {209		c := make(chan int, 1)210		c <- 1211		go func() {212			select {213			case <-c:214			default:215				t.Error("chan is not ready")216			}217		}()218		close(c)219		<-c220		if t.Failed() {221			return222		}223	}224}225226// This test checks that select acts on the state of the channels at one227// moment in the execution, not over a smeared time window.228// In the test, one goroutine does:229//230//	create c1, c2231//	make c1 ready for receiving232//	create second goroutine233//	make c2 ready for receiving234//	make c1 no longer ready for receiving (if possible)235//236// The second goroutine does a non-blocking select receiving from c1 and c2.237// From the time the second goroutine is created, at least one of c1 and c2238// is always ready for receiving, so the select in the second goroutine must239// always receive from one or the other. It must never execute the default case.240func TestNonblockSelectRace(t *testing.T) {241	n := 100000242	if testing.Short() {243		n = 1000244	}245	done := make(chan bool, 1)246	for i := 0; i < n; i++ {247		c1 := make(chan int, 1)248		c2 := make(chan int, 1)249		c1 <- 1250		go func() {251			select {252			case <-c1:253			case <-c2:254			default:255				done <- false256				return257			}258			done <- true259		}()260		c2 <- 1261		select {262		case <-c1:263		default:264		}265		if !<-done {266			t.Fatal("no chan is ready")267		}268	}269}270271// Same as TestNonblockSelectRace, but close(c2) replaces c2 <- 1.272func TestNonblockSelectRace2(t *testing.T) {273	n := 100000274	if testing.Short() {275		n = 1000276	}277	done := make(chan bool, 1)278	for i := 0; i < n; i++ {279		c1 := make(chan int, 1)280		c2 := make(chan int)281		c1 <- 1282		go func() {283			select {284			case <-c1:285			case <-c2:286			default:287				done <- false288				return289			}290			done <- true291		}()292		close(c2)293		select {294		case <-c1:295		default:296		}297		if !<-done {298			t.Fatal("no chan is ready")299		}300	}301}302303func TestSelfSelect(t *testing.T) {304	// Ensure that send/recv on the same chan in select305	// does not crash nor deadlock.306	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))307	for _, chanCap := range []int{0, 10} {308		var wg sync.WaitGroup309		wg.Add(2)310		c := make(chan int, chanCap)311		for p := 0; p < 2; p++ {312			go func() {313				defer wg.Done()314				for i := 0; i < 1000; i++ {315					if p == 0 || i%2 == 0 {316						select {317						case c <- p:318						case v := <-c:319							if chanCap == 0 && v == p {320								t.Errorf("self receive")321								return322							}323						}324					} else {325						select {326						case v := <-c:327							if chanCap == 0 && v == p {328								t.Errorf("self receive")329								return330							}331						case c <- p:332						}333					}334				}335			}()336		}337		wg.Wait()338	}339}340341func TestSelectStress(t *testing.T) {342	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(10))343	var c [4]chan int344	c[0] = make(chan int)345	c[1] = make(chan int)346	c[2] = make(chan int, 2)347	c[3] = make(chan int, 3)348	N := int(1e5)349	if testing.Short() {350		N /= 10351	}352	// There are 4 goroutines that send N values on each of the chans,353	// + 4 goroutines that receive N values on each of the chans,354	// + 1 goroutine that sends N values on each of the chans in a single select,355	// + 1 goroutine that receives N values on each of the chans in a single select.356	// All these sends, receives and selects interact chaotically at runtime,357	// but we are careful that this whole construct does not deadlock.358	var wg sync.WaitGroup359	wg.Add(10)360	for k := 0; k < 4; k++ {361		go func() {362			for i := 0; i < N; i++ {363				c[k] <- 0364			}365			wg.Done()366		}()367		go func() {368			for i := 0; i < N; i++ {369				<-c[k]370			}371			wg.Done()372		}()373	}374	go func() {375		var n [4]int376		c1 := c377		for i := 0; i < 4*N; i++ {378			select {379			case c1[3] <- 0:380				n[3]++381				if n[3] == N {382					c1[3] = nil383				}384			case c1[2] <- 0:385				n[2]++386				if n[2] == N {387					c1[2] = nil388				}389			case c1[0] <- 0:390				n[0]++391				if n[0] == N {392					c1[0] = nil393				}394			case c1[1] <- 0:395				n[1]++396				if n[1] == N {397					c1[1] = nil398				}399			}400		}401		wg.Done()402	}()403	go func() {404		var n [4]int405		c1 := c406		for i := 0; i < 4*N; i++ {407			select {408			case <-c1[0]:409				n[0]++410				if n[0] == N {411					c1[0] = nil412				}413			case <-c1[1]:414				n[1]++415				if n[1] == N {416					c1[1] = nil417				}418			case <-c1[2]:419				n[2]++420				if n[2] == N {421					c1[2] = nil422				}423			case <-c1[3]:424				n[3]++425				if n[3] == N {426					c1[3] = nil427				}428			}429		}430		wg.Done()431	}()432	wg.Wait()433}434435func TestSelectFairness(t *testing.T) {436	const trials = 10000437	if runtime.GOOS == "linux" && runtime.GOARCH == "ppc64le" {438		testenv.SkipFlaky(t, 22047)439	}440	c1 := make(chan byte, trials+1)441	c2 := make(chan byte, trials+1)442	for i := 0; i < trials+1; i++ {443		c1 <- 1444		c2 <- 2445	}446	c3 := make(chan byte)447	c4 := make(chan byte)448	out := make(chan byte)449	done := make(chan byte)450	var wg sync.WaitGroup451	wg.Add(1)452	go func() {453		defer wg.Done()454		for {455			var b byte456			select {457			case b = <-c3:458			case b = <-c4:459			case b = <-c1:460			case b = <-c2:461			}462			select {463			case out <- b:464			case <-done:465				return466			}467		}468	}()469	cnt1, cnt2 := 0, 0470	for i := 0; i < trials; i++ {471		switch b := <-out; b {472		case 1:473			cnt1++474		case 2:475			cnt2++476		default:477			t.Fatalf("unexpected value %d on channel", b)478		}479	}480	// If the select in the goroutine is fair,481	// cnt1 and cnt2 should be about the same value.482	// See if we're more than 10 sigma away from the expected value.483	// 10 sigma is a lot, but we're ok with some systematic bias as484	// long as it isn't too severe.485	const mean = trials * 0.5486	const variance = trials * 0.5 * (1 - 0.5)487	stddev := math.Sqrt(variance)488	if math.Abs(float64(cnt1-mean)) > 10*stddev {489		t.Errorf("unfair select: in %d trials, results were %d, %d", trials, cnt1, cnt2)490	}491	close(done)492	wg.Wait()493}494495func TestChanSendInterface(t *testing.T) {496	type mt struct{}497	m := &mt{}498	c := make(chan any, 1)499	c <- m500	select {501	case c <- m:502	default:503	}504	select {505	case c <- m:506	case c <- &mt{}:507	default:508	}509}510511func TestPseudoRandomSend(t *testing.T) {512	n := 100513	for _, chanCap := range []int{0, n} {514		c := make(chan int, chanCap)515		l := make([]int, n)516		var m sync.Mutex517		m.Lock()518		go func() {519			for i := 0; i < n; i++ {520				runtime.Gosched()521				l[i] = <-c522			}523			m.Unlock()524		}()525		for i := 0; i < n; i++ {526			select {527			case c <- 1:528			case c <- 0:529			}530		}531		m.Lock() // wait532		n0 := 0533		n1 := 0534		for _, i := range l {535			n0 += (i + 1) % 2536			n1 += i537		}538		if n0 <= n/10 || n1 <= n/10 {539			t.Errorf("Want pseudorandom, got %d zeros and %d ones (chan cap %d)", n0, n1, chanCap)540		}541	}542}543544func TestMultiConsumer(t *testing.T) {545	const nwork = 23546	const niter = 271828547548	pn := []int{2, 3, 7, 11, 13, 17, 19, 23, 27, 31}549550	q := make(chan int, nwork*3)551	r := make(chan int, nwork*3)552553	// workers554	var wg sync.WaitGroup555	for i := 0; i < nwork; i++ {556		wg.Add(1)557		go func(w int) {558			for v := range q {559				// mess with the fifo-ish nature of range560				if pn[w%len(pn)] == v {561					runtime.Gosched()562				}563				r <- v564			}565			wg.Done()566		}(i)567	}568569	// feeder & closer570	expect := 0571	go func() {572		for i := 0; i < niter; i++ {573			v := pn[i%len(pn)]574			expect += v575			q <- v576		}577		close(q)  // no more work578		wg.Wait() // workers done579		close(r)  // ... so there can be no more results580	}()581582	// consume & check583	n := 0584	s := 0585	for v := range r {586		n++587		s += v588	}589	if n != niter || s != expect {590		t.Errorf("Expected sum %d (got %d) from %d iter (saw %d)",591			expect, s, niter, n)592	}593}594595func TestShrinkStackDuringBlockedSend(t *testing.T) {596	// make sure that channel operations still work when we are597	// blocked on a channel send and we shrink the stack.598	// NOTE: this test probably won't fail unless stack1.go:stackDebug599	// is set to >= 1.600	const n = 10601	c := make(chan int)602	done := make(chan struct{})603604	go func() {605		for i := 0; i < n; i++ {606			c <- i607			// use lots of stack, briefly.608			stackGrowthRecursive(20)609		}610		done <- struct{}{}611	}()612613	for i := 0; i < n; i++ {614		x := <-c615		if x != i {616			t.Errorf("bad channel read: want %d, got %d", i, x)617		}618		// Waste some time so sender can finish using lots of stack619		// and block in channel send.620		time.Sleep(1 * time.Millisecond)621		// trigger GC which will shrink the stack of the sender.622		runtime.GC()623	}624	<-done625}626627func TestNoShrinkStackWhileParking(t *testing.T) {628	if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm64" {629		testenv.SkipFlaky(t, 49382)630	}631	if runtime.GOOS == "openbsd" {632		testenv.SkipFlaky(t, 51482)633	}634635	// The goal of this test is to trigger a "racy sudog adjustment"636	// throw. Basically, there's a window between when a goroutine637	// becomes available for preemption for stack scanning (and thus,638	// stack shrinking) but before the goroutine has fully parked on a639	// channel. See issue 40641 for more details on the problem.640	//641	// The way we try to induce this failure is to set up two642	// goroutines: a sender and a receiver that communicate across643	// a channel. We try to set up a situation where the sender644	// grows its stack temporarily then *fully* blocks on a channel645	// often. Meanwhile a GC is triggered so that we try to get a646	// mark worker to shrink the sender's stack and race with the647	// sender parking.648	//649	// Unfortunately the race window here is so small that we650	// either need a ridiculous number of iterations, or we add651	// "usleep(1000)" to park_m, just before the unlockf call.652	const n = 10653	send := func(c chan<- int, done chan struct{}) {654		for i := 0; i < n; i++ {655			c <- i656			// Use lots of stack briefly so that657			// the GC is going to want to shrink us658			// when it scans us. Make sure not to659			// do any function calls otherwise660			// in order to avoid us shrinking ourselves661			// when we're preempted.662			stackGrowthRecursive(20)663		}664		done <- struct{}{}665	}666	recv := func(c <-chan int, done chan struct{}) {667		for i := 0; i < n; i++ {668			// Sleep here so that the sender always669			// fully blocks.670			time.Sleep(10 * time.Microsecond)671			<-c672		}673		done <- struct{}{}674	}675	for i := 0; i < n*20; i++ {676		c := make(chan int)677		done := make(chan struct{})678		go recv(c, done)679		go send(c, done)680		// Wait a little bit before triggering681		// the GC to make sure the sender and682		// receiver have gotten into their groove.683		time.Sleep(50 * time.Microsecond)684		runtime.GC()685		<-done686		<-done687	}688}689690func TestSelectDuplicateChannel(t *testing.T) {691	// This test makes sure we can queue a G on692	// the same channel multiple times.693	c := make(chan int)694	d := make(chan int)695	e := make(chan int)696697	// goroutine A698	go func() {699		select {700		case <-c:701		case <-c:702		case <-d:703		}704		e <- 9705	}()706	time.Sleep(time.Millisecond) // make sure goroutine A gets queued first on c707708	// goroutine B709	go func() {710		<-c711	}()712	time.Sleep(time.Millisecond) // make sure goroutine B gets queued on c before continuing713714	d <- 7 // wake up A, it dequeues itself from c.  This operation used to corrupt c.recvq.715	<-e    // A tells us it's done716	c <- 8 // wake up B.  This operation used to fail because c.recvq was corrupted (it tries to wake up an already running G instead of B)717}718719func TestSelectStackAdjust(t *testing.T) {720	// Test that channel receive slots that contain local stack721	// pointers are adjusted correctly by stack shrinking.722	c := make(chan *int)723	d := make(chan *int)724	ready1 := make(chan bool)725	ready2 := make(chan bool)726727	f := func(ready chan bool, dup bool) {728		// Temporarily grow the stack to 10K.729		stackGrowthRecursive((10 << 10) / (128 * 8))730731		// We're ready to trigger GC and stack shrink.732		ready <- true733734		val := 42735		var cx *int736		cx = &val737738		var c2 chan *int739		var d2 chan *int740		if dup {741			c2 = c742			d2 = d743		}744745		// Receive from d. cx won't be affected.746		select {747		case cx = <-c:748		case <-c2:749		case <-d:750		case <-d2:751		}752753		// Check that pointer in cx was adjusted correctly.754		if cx != &val {755			t.Error("cx no longer points to val")756		} else if val != 42 {757			t.Error("val changed")758		} else {759			*cx = 43760			if val != 43 {761				t.Error("changing *cx failed to change val")762			}763		}764		ready <- true765	}766767	go f(ready1, false)768	go f(ready2, true)769770	// Let the goroutines get into the select.771	<-ready1772	<-ready2773	time.Sleep(10 * time.Millisecond)774775	// Force concurrent GC to shrink the stacks.776	runtime.GC()777778	// Wake selects.779	close(d)780	<-ready1781	<-ready2782}783784type struct0 struct{}785786func BenchmarkMakeChan(b *testing.B) {787	b.Run("Byte", func(b *testing.B) {788		var x chan byte789		for i := 0; i < b.N; i++ {790			x = make(chan byte, 8)791		}792		close(x)793	})794	b.Run("Int", func(b *testing.B) {795		var x chan int796		for i := 0; i < b.N; i++ {797			x = make(chan int, 8)798		}799		close(x)800	})801	b.Run("Ptr", func(b *testing.B) {802		var x chan *byte803		for i := 0; i < b.N; i++ {804			x = make(chan *byte, 8)805		}806		close(x)807	})808	b.Run("Struct", func(b *testing.B) {809		b.Run("0", func(b *testing.B) {810			var x chan struct0811			for i := 0; i < b.N; i++ {812				x = make(chan struct0, 8)813			}814			close(x)815		})816		b.Run("32", func(b *testing.B) {817			var x chan struct32818			for i := 0; i < b.N; i++ {819				x = make(chan struct32, 8)820			}821			close(x)822		})823		b.Run("40", func(b *testing.B) {824			var x chan struct40825			for i := 0; i < b.N; i++ {826				x = make(chan struct40, 8)827			}828			close(x)829		})830	})831}832833func BenchmarkChanNonblocking(b *testing.B) {834	myc := make(chan int)835	b.RunParallel(func(pb *testing.PB) {836		for pb.Next() {837			select {838			case <-myc:839			default:840			}841		}842	})843}844845func BenchmarkSelectUncontended(b *testing.B) {846	b.RunParallel(func(pb *testing.PB) {847		myc1 := make(chan int, 1)848		myc2 := make(chan int, 1)849		myc1 <- 0850		for pb.Next() {851			select {852			case <-myc1:853				myc2 <- 0854			case <-myc2:855				myc1 <- 0856			}857		}858	})859}860861func BenchmarkSelectSyncContended(b *testing.B) {862	myc1 := make(chan int)863	myc2 := make(chan int)864	myc3 := make(chan int)865	done := make(chan int)866	b.RunParallel(func(pb *testing.PB) {867		go func() {868			for {869				select {870				case myc1 <- 0:871				case myc2 <- 0:872				case myc3 <- 0:873				case <-done:874					return875				}876			}877		}()878		for pb.Next() {879			select {880			case <-myc1:881			case <-myc2:882			case <-myc3:883			}884		}885	})886	close(done)887}888889func BenchmarkSelectAsyncContended(b *testing.B) {890	procs := runtime.GOMAXPROCS(0)891	myc1 := make(chan int, procs)892	myc2 := make(chan int, procs)893	b.RunParallel(func(pb *testing.PB) {894		myc1 <- 0895		for pb.Next() {896			select {897			case <-myc1:898				myc2 <- 0899			case <-myc2:900				myc1 <- 0901			}902		}903	})904}905906func BenchmarkSelectNonblock(b *testing.B) {907	myc1 := make(chan int)908	myc2 := make(chan int)909	myc3 := make(chan int, 1)910	myc4 := make(chan int, 1)911	b.RunParallel(func(pb *testing.PB) {912		for pb.Next() {913			select {914			case <-myc1:915			default:916			}917			select {918			case myc2 <- 0:919			default:920			}921			select {922			case <-myc3:923			default:924			}925			select {926			case myc4 <- 0:927			default:928			}929		}930	})931}932933func BenchmarkChanUncontended(b *testing.B) {934	const C = 100935	b.RunParallel(func(pb *testing.PB) {936		myc := make(chan int, C)937		for pb.Next() {938			for i := 0; i < C; i++ {939				myc <- 0940			}941			for i := 0; i < C; i++ {942				<-myc943			}944		}945	})946}947948func BenchmarkChanContended(b *testing.B) {949	const C = 100950	myc := make(chan int, C*runtime.GOMAXPROCS(0))951	b.RunParallel(func(pb *testing.PB) {952		for pb.Next() {953			for i := 0; i < C; i++ {954				myc <- 0955			}956			for i := 0; i < C; i++ {957				<-myc958			}959		}960	})961}962963func benchmarkChanSync(b *testing.B, work int) {964	const CallsPerSched = 1000965	procs := 2966	N := int32(b.N / CallsPerSched / procs * procs)967	c := make(chan bool, procs)968	myc := make(chan int)969	for p := 0; p < procs; p++ {970		go func() {971			for {972				i := atomic.AddInt32(&N, -1)973				if i < 0 {974					break975				}976				for g := 0; g < CallsPerSched; g++ {977					if i%2 == 0 {978						<-myc979						localWork(work)980						myc <- 0981						localWork(work)982					} else {983						myc <- 0984						localWork(work)985						<-myc986						localWork(work)987					}988				}989			}990			c <- true991		}()992	}993	for p := 0; p < procs; p++ {994		<-c995	}996}997998func BenchmarkChanSync(b *testing.B) {999	benchmarkChanSync(b, 0)1000}10011002func BenchmarkChanSyncWork(b *testing.B) {1003	benchmarkChanSync(b, 1000)1004}10051006func benchmarkChanProdCons(b *testing.B, chanSize, localWork int) {1007	const CallsPerSched = 10001008	procs := runtime.GOMAXPROCS(-1)1009	N := int32(b.N / CallsPerSched)1010	c := make(chan bool, 2*procs)1011	myc := make(chan int, chanSize)1012	for p := 0; p < procs; p++ {1013		go func() {1014			foo := 01015			for atomic.AddInt32(&N, -1) >= 0 {1016				for g := 0; g < CallsPerSched; g++ {1017					for i := 0; i < localWork; i++ {1018						foo *= 21019						foo /= 21020					}1021					myc <- 11022				}1023			}1024			myc <- 01025			c <- foo == 421026		}()1027		go func() {1028			foo := 01029			for {1030				v := <-myc1031				if v == 0 {1032					break1033				}1034				for i := 0; i < localWork; i++ {1035					foo *= 21036					foo /= 21037				}1038			}1039			c <- foo == 421040		}()1041	}1042	for p := 0; p < procs; p++ {1043		<-c1044		<-c1045	}1046}10471048func BenchmarkChanProdCons0(b *testing.B) {1049	benchmarkChanProdCons(b, 0, 0)1050}10511052func BenchmarkChanProdCons10(b *testing.B) {1053	benchmarkChanProdCons(b, 10, 0)1054}10551056func BenchmarkChanProdCons100(b *testing.B) {1057	benchmarkChanProdCons(b, 100, 0)1058}10591060func BenchmarkChanProdConsWork0(b *testing.B) {1061	benchmarkChanProdCons(b, 0, 100)1062}10631064func BenchmarkChanProdConsWork10(b *testing.B) {1065	benchmarkChanProdCons(b, 10, 100)1066}10671068func BenchmarkChanProdConsWork100(b *testing.B) {1069	benchmarkChanProdCons(b, 100, 100)1070}10711072func BenchmarkSelectProdCons(b *testing.B) {1073	const CallsPerSched = 10001074	procs := runtime.GOMAXPROCS(-1)1075	N := int32(b.N / CallsPerSched)1076	c := make(chan bool, 2*procs)1077	myc := make(chan int, 128)1078	myclose := make(chan bool)1079	for p := 0; p < procs; p++ {1080		go func() {1081			// Producer: sends to myc.1082			foo := 01083			// Intended to not fire during benchmarking.1084			mytimer := time.After(time.Hour)1085			for atomic.AddInt32(&N, -1) >= 0 {1086				for g := 0; g < CallsPerSched; g++ {1087					// Model some local work.1088					for i := 0; i < 100; i++ {1089						foo *= 21090						foo /= 21091					}1092					select {1093					case myc <- 1:1094					case <-mytimer:1095					case <-myclose:1096					}1097				}1098			}1099			myc <- 01100			c <- foo == 421101		}()1102		go func() {1103			// Consumer: receives from myc.1104			foo := 01105			// Intended to not fire during benchmarking.1106			mytimer := time.After(time.Hour)1107		loop:1108			for {1109				select {1110				case v := <-myc:1111					if v == 0 {1112						break loop1113					}1114				case <-mytimer:1115				case <-myclose:1116				}1117				// Model some local work.1118				for i := 0; i < 100; i++ {1119					foo *= 21120					foo /= 21121				}1122			}1123			c <- foo == 421124		}()1125	}1126	for p := 0; p < procs; p++ {1127		<-c1128		<-c1129	}1130}11311132func BenchmarkReceiveDataFromClosedChan(b *testing.B) {1133	count := b.N1134	ch := make(chan struct{}, count)1135	for i := 0; i < count; i++ {1136		ch <- struct{}{}1137	}1138	close(ch)11391140	b.ResetTimer()1141	for range ch {1142	}1143}11441145func BenchmarkChanCreation(b *testing.B) {1146	b.RunParallel(func(pb *testing.PB) {1147		for pb.Next() {1148			myc := make(chan int, 1)1149			myc <- 01150			<-myc1151		}1152	})1153}11541155func BenchmarkChanSem(b *testing.B) {1156	type Empty struct{}1157	myc := make(chan Empty, runtime.GOMAXPROCS(0))1158	b.RunParallel(func(pb *testing.PB) {1159		for pb.Next() {1160			myc <- Empty{}1161			<-myc1162		}1163	})1164}11651166func BenchmarkChanPopular(b *testing.B) {1167	const n = 10001168	c := make(chan bool)1169	var a []chan bool1170	var wg sync.WaitGroup1171	wg.Add(n)1172	for j := 0; j < n; j++ {1173		d := make(chan bool)1174		a = append(a, d)1175		go func() {1176			for i := 0; i < b.N; i++ {1177				select {1178				case <-c:1179				case <-d:1180				}1181			}1182			wg.Done()1183		}()1184	}1185	for i := 0; i < b.N; i++ {1186		for _, d := range a {1187			d <- true1188		}1189	}1190	wg.Wait()1191}11921193func BenchmarkChanClosed(b *testing.B) {1194	c := make(chan struct{})1195	close(c)1196	b.RunParallel(func(pb *testing.PB) {1197		for pb.Next() {1198			select {1199			case <-c:1200			default:1201				b.Error("Unreachable")1202			}1203		}1204	})1205}12061207var (1208	alwaysFalse = false1209	workSink    = 01210)12111212func localWork(w int) {1213	foo := 01214	for i := 0; i < w; i++ {1215		foo /= (foo + 1)1216	}1217	if alwaysFalse {1218		workSink += foo1219	}1220}

Code quality findings 49

Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
Blank identifier discarding results; verify intentional ignoring of return values
warning correctness blank-identifier-discard
_ = <-c
Blank identifier discarding results; verify intentional ignoring of return values
warning correctness blank-identifier-discard
_, _ = <-c
Blank identifier discarding results; verify intentional ignoring of return values
warning correctness blank-identifier-discard
case _ = <-c:
Blank identifier discarding results; verify intentional ignoring of return values
warning correctness blank-identifier-discard
case _, _ = <-c:
Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer wg.Done()
Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer wg.Done()
Manual scheduling hint; usually unnecessary and indicates deeper design issues
info correctness manual-scheduling
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
Fixed delays can mask issues; prefer timers or channels for synchronization
info correctness time-sleep
time.Sleep(time.Millisecond)
Fixed delays can mask issues; prefer timers or channels for synchronization
info correctness time-sleep
time.Sleep(time.Millisecond)
Fixed delays can mask issues; prefer timers or channels for synchronization
info correctness time-sleep
time.Sleep(time.Millisecond)
Map created without size hint before being populated in a loop; provide capacity hint to reduce allocations
info performance map-without-size-hint
recv := make(map[int]int)
Map created without size hint before being populated in a loop; provide capacity hint to reduce allocations
info performance map-without-size-hint
recv := make(map[int]int)
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for k, v := range <-done {
Manual scheduling hint; usually unnecessary and indicates deeper design issues
info correctness manual-scheduling
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for _, chanCap := range []int{0, 10} {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for p := 0; p < 2; p++ {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for i := 0; i < 1000; i++ {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if p == 0 || i%2 == 0 {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if chanCap == 0 && v == p {
Manual scheduling hint; usually unnecessary and indicates deeper design issues
info correctness manual-scheduling
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(10))
Infinite loop detected; ensure it has a proper exit condition (e.g., break, return) to avoid unintentional resource consumption or hangs
info correctness infinite-loop
for {
Manual scheduling hint; usually unnecessary and indicates deeper design issues
info correctness manual-scheduling
runtime.Gosched()
Manual scheduling hint; usually unnecessary and indicates deeper design issues
info correctness manual-scheduling
runtime.Gosched()
Fixed delays can mask issues; prefer timers or channels for synchronization
info correctness time-sleep
time.Sleep(1 * time.Millisecond)
Fixed delays can mask issues; prefer timers or channels for synchronization
info correctness time-sleep
time.Sleep(10 * time.Microsecond)
Fixed delays can mask issues; prefer timers or channels for synchronization
info correctness time-sleep
time.Sleep(50 * time.Microsecond)
Fixed delays can mask issues; prefer timers or channels for synchronization
info correctness time-sleep
time.Sleep(time.Millisecond) // make sure goroutine A gets queued first on c
Fixed delays can mask issues; prefer timers or channels for synchronization
info correctness time-sleep
time.Sleep(time.Millisecond) // make sure goroutine B gets queued on c before continuing
Fixed delays can mask issues; prefer timers or channels for synchronization
info correctness time-sleep
time.Sleep(10 * time.Millisecond)
Manual scheduling hint; usually unnecessary and indicates deeper design issues
info correctness manual-scheduling
procs := runtime.GOMAXPROCS(0)
Manual scheduling hint; usually unnecessary and indicates deeper design issues
info correctness manual-scheduling
myc := make(chan int, C*runtime.GOMAXPROCS(0))
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if i < 0 {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for g := 0; g < CallsPerSched; g++ {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if i%2 == 0 {
Manual scheduling hint; usually unnecessary and indicates deeper design issues
info correctness manual-scheduling
procs := runtime.GOMAXPROCS(-1)
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for p := 0; p < procs; p++ {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for atomic.AddInt32(&N, -1) >= 0 {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for g := 0; g < CallsPerSched; g++ {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for i := 0; i < localWork; i++ {
Manual scheduling hint; usually unnecessary and indicates deeper design issues
info correctness manual-scheduling
procs := runtime.GOMAXPROCS(-1)
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for p := 0; p < procs; p++ {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for atomic.AddInt32(&N, -1) >= 0 {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for g := 0; g < CallsPerSched; g++ {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for i := 0; i < 100; i++ {
Manual scheduling hint; usually unnecessary and indicates deeper design issues
info correctness manual-scheduling
myc := make(chan Empty, runtime.GOMAXPROCS(0))
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for i := 0; i < b.N; i++ {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.