Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Importing net/http prevents goroutine deadlock error in particular case #40489

Closed
yasuoza opened this issue Jul 30, 2020 · 3 comments
Closed

Importing net/http prevents goroutine deadlock error in particular case #40489

yasuoza opened this issue Jul 30, 2020 · 3 comments

Comments

@yasuoza
Copy link

@yasuoza yasuoza commented Jul 30, 2020

What version of Go are you using (go version)?

$ go version

go version go1.14.5 darwin/amd64 # brew install go
go version go1.14.6 linux/amd64  # docker run golang:buster

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
go version go1.14.6 linux/amd64
root@7238b87c6989:/code# go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/code/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build052282268=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Following Expected code raises fatal error: all goroutines are asleep - deadlock!.
And this is expected code, because errc is not buffered.

Working as expected code
package main

import (
	"fmt"
	"log"
	"sync"
)

// var _ http.Client

func main() {
	done := make(chan struct{})
	defer close(done)

	idc, errc := doJob(done)

	c := make(chan int)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		for id := range idc {
			c <- id
		}
	}()
	go func() {
		wg.Wait()
		close(c)
	}()

	for b := range c {
		fmt.Println(b)
	}

	if err := <-errc; err != nil {
		log.Fatal(err)
	}
}

func doJob(done <-chan struct{}) (<-chan int, <-chan error) {
	idc := make(chan int)
	errc := make(chan error) // not buffered. Should be buffered  as make(chan error, 1)

	go func() {
		defer close(idc)
		for i := 1; i < 10; i++ {
			select {
			case idc <- i:
			case <-done:
				return
			}
		}
		errc <- nil
	}()

	return idc, errc
}

But, following code importing net/http does not raise error, just hangs.

importing 'net/http' code
package main

import (
	"fmt"
	"log"
	"sync"
)

// This is TRICK
var _ http.Client

func main() {
	done := make(chan struct{})
	defer close(done)

	idc, errc := doJob(done)

	c := make(chan int)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		for id := range idc {
			c <- id
		}
	}()
	go func() {
		wg.Wait()
		close(c)
	}()

	for b := range c {
		fmt.Println(b)
	}

	if err := <-errc; err != nil {
		log.Fatal(err)
	}
}

func doJob(done <-chan struct{}) (<-chan int, <-chan error) {
	idc := make(chan int)
	errc := make(chan error) // not buffered. Should be buffered  as make(chan error, 1)

	go func() {
		defer close(idc)
		for i := 1; i < 10; i++ {
			select {
			case idc <- i:
			case <-done:
				return
			}
		}
		errc <- nil
	}()

	return idc, errc
}

What did you expect to see?

Both of above codes should raise fatal error: all goroutines are asleep - deadlock!

What did you see instead?

First code: fatal error: all goroutines are asleep - deadlock! 👍
Second code: program stops(not finish) 🤔

@davecheney
Copy link
Contributor

@davecheney davecheney commented Jul 30, 2020

Thank you for raising this issue. The deadlock detector is intended to be a best effort approach — it’s pretty easy to detect a deadlock if there are no runnable goroutines — but it is equally detestable with a small goroutine waiting on a timer.

Variations of this issue have been discussed in the past. I would appreciate it if you would review the closed issues and if you feel this is a duplicate of either an open or closed issue on this topic would you consider closing it as a duplicate.

Thank you

@yasuoza
Copy link
Author

@yasuoza yasuoza commented Jul 30, 2020

Thank you for the information. I now understand deadlock is best effort, and for this reason it could not to be detected sometimes.

I will close this issue. Thank you again.

@yasuoza yasuoza closed this Jul 30, 2020
@davecheney
Copy link
Contributor

@davecheney davecheney commented Jul 30, 2020

Thank you for understanding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.