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

x/net/websocket: using cgo call unnecessarily? #2490

Closed
gopherbot opened this issue Nov 22, 2011 · 10 comments
Closed

x/net/websocket: using cgo call unnecessarily? #2490

gopherbot opened this issue Nov 22, 2011 · 10 comments

Comments

@gopherbot
Copy link

by vladimir.webdev:

Complete code to reproduce:

package main

import (
       "fmt"
       "websocket"
)

func openConn(ch chan<- int) {
       ws, err := websocket.Dial("ws://localhost:9999/", "", "http://
localhost/")
       if err != nil {
               fmt.Sprintf("Dial: " + err.String())
       }
       defer ws.Close()

       ch <- 1
}

func main() {
       ch := make(chan int)

       for i := 0; i < 1000; i++ {
               go openConn(ch)
       }

       ch <- 1
}

It fails with:

runtime/cgo: pthread_create failed: Resource temporarily unavailable
SIGABRT: abort
PC=0xf97422

Discussion:
http://groups.google.com/group/golang-nuts/browse_thread/thread/520bdb93d99ee536/a1fdd8c8640cce46#a1fdd8c8640cce46

Quote from 0xe2.0x9a.0...@gmail.com:

The websocket.Dial function is (indirectly) using cgo. Each cgo call
behaves like a syscall. The websocket.Dial() function will try to
allocate C memory when performing address lookup - the memory
allocation (a cgo call) causes the Go runtime to create another OS
thread to handle goroutines. An OS thread (presumably, the newly
created one) calls websocket.Dial() from another goroutine created by
your program, and tries to allocate another piece of C memory, which
in turn creates another OS thread.
This ends up creating too many OS threads.
@rsc
Copy link
Contributor

rsc commented Nov 22, 2011

Comment 1:

I would like to know what call websocket is making, but in general
this is correct behavior: if you are trying to run 1000 system calls
simultaneously, you need 1000 threads to do that.  If your OS won't
let you have that, there is nothing Go can do about it.
Can you include some of the stack traces from the crash?

Status changed to Accepted.

@gopherbot
Copy link
Author

Comment 2 by vladimir.webdev:

Thanks for looking into this.
Goroutines are stated to be lightweight analog of threads. My code is trying to create
1000 lightweight goroutines, but instead I am getting > 1000 OS threads (as I understand
2-3k threads). I can't understand how this could be treated as correct behavior...
Probably you are looking for this (but I also attached complete output):
goroutine 203 [1]:
runtime.gosched+0x56 /archive/vladimir/Dropbox/workspace/go/src/pkg/runtime/proc.c:769
    runtime.gosched()
runtime.exitsyscall+0x7d
/archive/vladimir/Dropbox/workspace/go/src/pkg/runtime/proc.c:871
    runtime.exitsyscall()
runtime.cgocall+0x10a
/archive/vladimir/Dropbox/workspace/go/src/pkg/runtime/cgocall.c:136
    runtime.cgocall(0x8105ce2, 0x3ebc8c, 0x3)
runtime.cmalloc+0x4b /archive/vladimir/Dropbox/workspace/go/src/pkg/runtime/cgocall.c:180
    runtime.cmalloc(0xa, 0x804f084)
net._Cfunc_CString+0x2a
/archive/vladimir/Dropbox/workspace/go/src/pkg/net/_obj/_cgo_defun.c:31
    net._Cfunc_CString(0x81764b1, 0x9, 0x86643b0, 0x0)
net.cgoLookupIPCNAME+0x94
/archive/vladimir/Dropbox/workspace/go/src/pkg/net/_obj/cgo_linux.cgo1.go:76
    net.cgoLookupIPCNAME(0x81764b1, 0x9, 0x0, 0x0, 0x0, ...)
net.cgoLookupIP+0x3d
/archive/vladimir/Dropbox/workspace/go/src/pkg/net/_obj/cgo_linux.cgo1.go:120
    net.cgoLookupIP(0x81764b1, 0x9, 0x0, 0x0, 0x0, ...)
net.cgoLookupHost+0x40
/archive/vladimir/Dropbox/workspace/go/src/pkg/net/_obj/cgo_linux.cgo1.go:16
    net.cgoLookupHost(0x81764b1, 0x9, 0x0, 0x0, 0x0, ...)
net.LookupHost+0x3d /archive/vladimir/Dropbox/workspace/go/src/pkg/net/lookup_unix.go:14
    net.LookupHost(0x81764b1, 0x9, 0x0, 0x0, 0x0, ...)
net.hostPortToIP+0x14f /archive/vladimir/Dropbox/workspace/go/src/pkg/net/ipsock.go:128
    net.hostPortToIP(0x8162b9c, 0x3, 0x81764b1, 0xe, 0x0, ...)
net.ResolveTCPAddr+0x37 /archive/vladimir/Dropbox/workspace/go/src/pkg/net/tcpsock.go:35
    net.ResolveTCPAddr(0x8162b9c, 0x3, 0x81764b1, 0xe, 0x0, ...)
net.resolveNetAddr+0x32e /archive/vladimir/Dropbox/workspace/go/src/pkg/net/dial.go:15
    net.resolveNetAddr(0x81650cc, 0x4, 0x8162b9c, 0x3, 0x81764b1, ...)
net.Dial+0x54 /archive/vladimir/Dropbox/workspace/go/src/pkg/net/dial.go:48
    net.Dial(0x8162b9c, 0x3, 0x81764b1, 0xe, 0x0, ...)
websocket.Dial+0x130
/archive/vladimir/Dropbox/workspace/go/src/pkg/websocket/client.go:113
    websocket.Dial(0x81764ac, 0x14, 0x8161924, 0x0, 0x8171aa0, ...)
main.openConn+0x48 /home/vladimir/goplayground/testclient/test.go:9
    main.openConn(0x9774d8d0, 0x0)
runtime.goexit /archive/vladimir/Dropbox/workspace/go/src/pkg/runtime/proc.c:246
    runtime.goexit()
----- goroutine created by -----
main.main+0x5e /home/vladimir/goplayground/testclient/test.go:22

Attachments:

  1. o.txt (874836 bytes)

@rsc
Copy link
Contributor

rsc commented Nov 22, 2011

Comment 3:

Goroutines expand to full threads if they get blocked in a system call
(otherwise the program as a whole would stop running),
and for reasons I do not yet understand, this code is getting blocked
in 1000 system calls simultaneously.
Russ

@gopherbot
Copy link
Author

Comment 4 by vladimir.webdev:

Ah, it seems now I finally got it. But will runtime try to reduce the number of OS
threads when it gets the control from system call back? Because it is hard to imagine
the situation when so many threads are useful...

@rsc
Copy link
Contributor

rsc commented Nov 28, 2011

Comment 5:

It may be hard to imagine the situation, but the situation just happened!
If it happened once it is likely to happen again.
Russ

@gopherbot
Copy link
Author

Comment 6:

The best solution seems to be to limit the maximum number of concurrent requests issued
by the "net" package (patch in respect to Go release.r60.3):
diff -r c1702f36df03 src/pkg/net/ipsock.go
--- a/src/pkg/net/ipsock.go     Tue Oct 18 10:55:12 2011 +1100
+++ b/src/pkg/net/ipsock.go     Mon Nov 28 18:09:33 2011 +0100
@@ -101,8 +101,13 @@
        return host + ":" + port
 }
 
+var concurrencyControl = make(chan byte, 10)
+
 // Convert "host:port" into IP address and port.
 func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
+       concurrencyControl <- 0
+       defer func(){<-concurrencyControl}()
+
        var (
                addr IP
                p, i int

@rsc
Copy link
Contributor

rsc commented Dec 9, 2011

Comment 7:

Labels changed: added priority-later, removed priority-medium.

@remyoudompheng
Copy link
Contributor

Comment 9:

Is this issue fixable in any way? is it about revisiting the way cgo is used to resolve
hostnames, or reducing the amount of OS threads used by the runtime?

@rsc
Copy link
Contributor

rsc commented Mar 12, 2013

Comment 11:

[The time for maybe has passed.]

@remyoudompheng
Copy link
Contributor

Comment 12:

This is essentially fixed by revision e255db359405 (coalesce duplicate lookups) and
revision 565e2d46bb68 (limit concurrent cgo DNS lookups).

Status changed to Fixed.

@mikioh mikioh added repo-net and removed repo-net labels Dec 23, 2014
@mikioh mikioh changed the title go.net/websocket: using cgo call unnecessarily? websocket: using cgo call unnecessarily? Jan 4, 2015
@mikioh mikioh changed the title websocket: using cgo call unnecessarily? x/net/websocket: using cgo call unnecessarily? Jul 30, 2015
@mikioh mikioh modified the milestone: Unreleased Jul 30, 2015
@golang golang locked and limited conversation to collaborators Aug 5, 2016
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants