diff --git a/src/cmd/dist/deps.go b/src/cmd/dist/deps.go index c1c8b8fff3a6a..641fe5d8968dd 100644 --- a/src/cmd/dist/deps.go +++ b/src/cmd/dist/deps.go @@ -5,68 +5,69 @@ package main var builddeps = map[string][]string{ "bufio": {"bytes", "errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "unicode", "unicode/utf8"}, "bytes": {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "unicode", "unicode/utf8"}, - "cmd/go/internal/base": {"bufio", "bytes", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/bug": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/envcmd", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/web", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/buildid": {"bufio", "bytes", "cmd/go/internal/cfg", "compress/flate", "compress/zlib", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/cfg": {"bufio", "bytes", "errors", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/clean": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/doc": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/envcmd": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/fix": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "compress/flate", "compress/zlib", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/fmtcmd": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "compress/flate", "compress/zlib", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/generate": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/get": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/web", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding", "encoding/base64", "encoding/binary", "encoding/json", "encoding/xml", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/singleflight", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/help": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/list": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding", "encoding/base64", "encoding/binary", "encoding/json", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/load": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/str", "compress/flate", "compress/zlib", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/run": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/str": {"bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/test": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/tool": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/version": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "cmd/go/internal/vet": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/base": {"bufio", "bytes", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/bug": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/envcmd", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/web", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/buildid": {"bufio", "bytes", "cmd/go/internal/cfg", "compress/flate", "compress/zlib", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/cfg": {"bufio", "bytes", "errors", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/clean": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/doc": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/envcmd": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/fix": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "compress/flate", "compress/zlib", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/fmtcmd": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "compress/flate", "compress/zlib", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/generate": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/get": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/web", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding", "encoding/base64", "encoding/binary", "encoding/json", "encoding/xml", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/singleflight", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/help": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/list": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding", "encoding/base64", "encoding/binary", "encoding/json", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/load": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/str", "compress/flate", "compress/zlib", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/run": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/str": {"bytes", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/test": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/tool": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/version": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/cfg", "cmd/go/internal/str", "context", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/vet": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "cmd/go/internal/web": {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"}, - "cmd/go/internal/work": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "compress/flate": {"bufio", "bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "compress/zlib": {"bufio", "bytes", "compress/flate", "errors", "fmt", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go/internal/work": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/load", "cmd/go/internal/str", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding/binary", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "compress/flate": {"bufio", "bytes", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "compress/zlib": {"bufio", "bytes", "compress/flate", "errors", "fmt", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "container/heap": {"errors", "internal/race", "math", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "unicode/utf8"}, - "context": {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, + "context": {"errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, "crypto": {"errors", "hash", "internal/race", "io", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"}, "crypto/sha1": {"crypto", "errors", "hash", "internal/race", "io", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"}, - "debug/dwarf": {"encoding/binary", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "debug/elf": {"bufio", "bytes", "compress/flate", "compress/zlib", "debug/dwarf", "encoding/binary", "errors", "fmt", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "debug/macho": {"bytes", "debug/dwarf", "encoding/binary", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "debug/dwarf": {"encoding/binary", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "debug/elf": {"bufio", "bytes", "compress/flate", "compress/zlib", "debug/dwarf", "encoding/binary", "errors", "fmt", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "debug/macho": {"bytes", "debug/dwarf", "encoding/binary", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "encoding": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"}, "encoding/base64": {"errors", "internal/race", "io", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"}, "encoding/binary": {"errors", "internal/race", "io", "math", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"}, - "encoding/json": {"bytes", "encoding", "encoding/base64", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "encoding/xml": {"bufio", "bytes", "encoding", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "encoding/json": {"bytes", "encoding", "encoding/base64", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "encoding/xml": {"bufio", "bytes", "encoding", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "errors": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"}, - "flag": {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, - "fmt": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, - "go/ast": {"bytes", "errors", "fmt", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "go/build": {"bufio", "bytes", "errors", "fmt", "go/ast", "go/doc", "go/parser", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "go/doc": {"bytes", "errors", "fmt", "go/ast", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "go/parser": {"bytes", "errors", "fmt", "go/ast", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "go/scanner": {"bytes", "errors", "fmt", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "go/token": {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, + "flag": {"errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, + "fmt": {"errors", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, + "go/ast": {"bytes", "errors", "fmt", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "go/build": {"bufio", "bytes", "errors", "fmt", "go/ast", "go/doc", "go/parser", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "go/doc": {"bytes", "errors", "fmt", "go/ast", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "go/parser": {"bytes", "errors", "fmt", "go/ast", "go/scanner", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "go/scanner": {"bytes", "errors", "fmt", "go/token", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "go/token": {"errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, "hash": {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"}, "hash/adler32": {"errors", "hash", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"}, + "internal/poll": {"errors", "internal/race", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "time", "unicode/utf16"}, "internal/race": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"}, "internal/singleflight": {"internal/race", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"}, "internal/syscall/windows": {"errors", "internal/race", "internal/syscall/windows/sysdll", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "unicode/utf16"}, "internal/syscall/windows/registry": {"errors", "internal/race", "internal/syscall/windows/sysdll", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "unicode/utf16"}, "internal/syscall/windows/sysdll": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"}, "io": {"errors", "internal/race", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"}, - "io/ioutil": {"bytes", "errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "log": {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, + "io/ioutil": {"bytes", "errors", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "log": {"errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, "math": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"}, - "net/url": {"bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "os": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, - "os/exec": {"bytes", "context", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "os/signal": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "os", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, + "net/url": {"bytes", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "os": {"errors", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, + "os/exec": {"bytes", "context", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "os/signal": {"errors", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "os", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"}, "path": {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strings", "sync", "sync/atomic", "unicode", "unicode/utf8"}, - "path/filepath": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "path/filepath": {"errors", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "reflect": {"errors", "internal/race", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"}, "regexp": {"bytes", "errors", "internal/race", "io", "math", "reflect", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "unicode", "unicode/utf8"}, "regexp/syntax": {"bytes", "errors", "internal/race", "io", "math", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "unicode", "unicode/utf8"}, @@ -79,11 +80,11 @@ var builddeps = map[string][]string{ "sync": {"internal/race", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync/atomic"}, "sync/atomic": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"}, "syscall": {"errors", "internal/race", "internal/syscall/windows/sysdll", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "unicode/utf16"}, - "text/template": {"bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "math", "net/url", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, - "text/template/parse": {"bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "text/template": {"bytes", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "math", "net/url", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "text/template/parse": {"bytes", "errors", "fmt", "internal/poll", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"}, "time": {"errors", "internal/race", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "unicode/utf16"}, "unicode": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"}, "unicode/utf16": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"}, "unicode/utf8": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"}, - "cmd/go": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/bug", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/clean", "cmd/go/internal/doc", "cmd/go/internal/envcmd", "cmd/go/internal/fix", "cmd/go/internal/fmtcmd", "cmd/go/internal/generate", "cmd/go/internal/get", "cmd/go/internal/help", "cmd/go/internal/list", "cmd/go/internal/load", "cmd/go/internal/run", "cmd/go/internal/str", "cmd/go/internal/test", "cmd/go/internal/tool", "cmd/go/internal/version", "cmd/go/internal/vet", "cmd/go/internal/web", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding", "encoding/base64", "encoding/binary", "encoding/json", "encoding/xml", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/singleflight", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, + "cmd/go": {"bufio", "bytes", "cmd/go/internal/base", "cmd/go/internal/bug", "cmd/go/internal/buildid", "cmd/go/internal/cfg", "cmd/go/internal/clean", "cmd/go/internal/doc", "cmd/go/internal/envcmd", "cmd/go/internal/fix", "cmd/go/internal/fmtcmd", "cmd/go/internal/generate", "cmd/go/internal/get", "cmd/go/internal/help", "cmd/go/internal/list", "cmd/go/internal/load", "cmd/go/internal/run", "cmd/go/internal/str", "cmd/go/internal/test", "cmd/go/internal/tool", "cmd/go/internal/version", "cmd/go/internal/vet", "cmd/go/internal/web", "cmd/go/internal/work", "compress/flate", "compress/zlib", "container/heap", "context", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding", "encoding/base64", "encoding/binary", "encoding/json", "encoding/xml", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/poll", "internal/race", "internal/singleflight", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"}, } diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index 2ed4d6b6ec28a..a395176986d3c 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -2180,7 +2180,7 @@ func (gcToolchain) gc(b *Builder, p *load.Package, archive, obj string, asmhdr b extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) if p.Standard { switch p.ImportPath { - case "bytes", "net", "os", "runtime/pprof", "sync", "time": + case "bytes", "internal/poll", "net", "os", "runtime/pprof", "sync", "time": extFiles++ } } diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 1ee23eda69eb7..e6804e7ef506a 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -150,7 +150,8 @@ var pkgDeps = map[string][]string{ "syscall", }, - "os": {"L1", "os", "syscall", "time", "internal/syscall/windows"}, + "internal/poll": {"L0", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8"}, + "os": {"L1", "os", "syscall", "time", "internal/poll", "internal/syscall/windows"}, "path/filepath": {"L2", "os", "syscall"}, "io/ioutil": {"L2", "os", "path/filepath", "time"}, "os/exec": {"L2", "os", "context", "path/filepath", "syscall"}, @@ -300,7 +301,7 @@ var pkgDeps = map[string][]string{ "net": { "L0", "CGO", "context", "math/rand", "os", "sort", "syscall", "time", - "internal/nettrace", + "internal/nettrace", "internal/poll", "internal/syscall/windows", "internal/singleflight", "internal/race", "golang_org/x/net/lif", "golang_org/x/net/route", }, diff --git a/src/internal/poll/export_test.go b/src/internal/poll/export_test.go new file mode 100644 index 0000000000000..ec658fd357659 --- /dev/null +++ b/src/internal/poll/export_test.go @@ -0,0 +1,39 @@ +// Copyright 2010 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. + +// Export guts for testing. +// Since testing imports os and os imports internal/poll, +// the internal/poll tests can not be in package poll. + +package poll + +var Consume = consume + +type FDMutex struct { + fdMutex +} + +func (mu *FDMutex) Incref() bool { + return mu.incref() +} + +func (mu *FDMutex) IncrefAndClose() bool { + return mu.increfAndClose() +} + +func (mu *FDMutex) Decref() bool { + return mu.decref() +} + +func (mu *FDMutex) RWLock(read bool) bool { + return mu.rwlock(read) +} + +func (mu *FDMutex) RWUnlock(read bool) bool { + return mu.rwunlock(read) +} + +func (fd *FD) EOFError(n int, err error) error { + return fd.eofError(n, err) +} diff --git a/src/internal/poll/fd.go b/src/internal/poll/fd.go new file mode 100644 index 0000000000000..0752876be2db2 --- /dev/null +++ b/src/internal/poll/fd.go @@ -0,0 +1,44 @@ +// Copyright 2017 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 poll supports non-blocking I/O on file descriptors with polling. +// This supports I/O operations that block only a goroutine, not a thread. +// This is used by the net and os packages. +// It uses a poller built into the runtime, with support from the +// runtime scheduler. +package poll + +import ( + "errors" +) + +// ErrClosing is returned when a descriptor is used after it has been closed. +var ErrClosing = errors.New("use of closed file or network connection") + +// ErrTimeout is returned for an expired deadline. +var ErrTimeout error = &TimeoutError{} + +// TimeoutError is returned for an expired deadline. +type TimeoutError struct{} + +// Implement the net.Error interface. +func (e *TimeoutError) Error() string { return "i/o timeout" } +func (e *TimeoutError) Timeout() bool { return true } +func (e *TimeoutError) Temporary() bool { return true } + +// consume removes data from a slice of byte slices, for writev. +func consume(v *[][]byte, n int64) { + for len(*v) > 0 { + ln0 := int64(len((*v)[0])) + if ln0 > n { + (*v)[0] = (*v)[0][n:] + return + } + n -= ln0 + *v = (*v)[1:] + } +} + +// TestHookDidWritev is a hook for testing writev. +var TestHookDidWritev = func(wrote int) {} diff --git a/src/net/fd_io_plan9.go b/src/internal/poll/fd_io_plan9.go similarity index 91% rename from src/net/fd_io_plan9.go rename to src/internal/poll/fd_io_plan9.go index 76da0c546cfc7..287d11bd8c29f 100644 --- a/src/net/fd_io_plan9.go +++ b/src/internal/poll/fd_io_plan9.go @@ -2,10 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package net +package poll import ( - "os" "runtime" "sync" "syscall" @@ -49,7 +48,7 @@ func newAsyncIO(fn func([]byte) (int, error), b []byte) *asyncIO { // Go runtime. runtime.LockOSThread() runtime_ignoreHangup() - aio.pid = os.Getpid() + aio.pid = syscall.Getpid() aio.mu.Unlock() n, err := fn(b) @@ -64,8 +63,6 @@ func newAsyncIO(fn func([]byte) (int, error), b []byte) *asyncIO { return aio } -var hangupNote os.Signal = syscall.Note("hangup") - // Cancel interrupts the I/O operation, causing // the Wait function to return. func (aio *asyncIO) Cancel() { @@ -74,11 +71,12 @@ func (aio *asyncIO) Cancel() { if aio.pid == -1 { return } - proc, err := os.FindProcess(aio.pid) - if err != nil { + f, e := syscall.Open("/proc/"+itoa(aio.pid)+"/note", syscall.O_WRONLY) + if e != nil { return } - proc.Signal(hangupNote) + syscall.Write(f, []byte("hangup")) + syscall.Close(f) } // Wait for the I/O operation to complete. diff --git a/src/net/fd_mutex.go b/src/internal/poll/fd_mutex.go similarity index 93% rename from src/net/fd_mutex.go rename to src/internal/poll/fd_mutex.go index 4591fd1cac8e9..2b7605337022f 100644 --- a/src/net/fd_mutex.go +++ b/src/internal/poll/fd_mutex.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package net +package poll import "sync/atomic" // fdMutex is a specialized synchronization primitive that manages // lifetime of an fd and serializes access to Read, Write and Close -// methods on netFD. +// methods on FD. type fdMutex struct { state uint64 rsema uint32 @@ -16,7 +16,7 @@ type fdMutex struct { } // fdMutex.state is organized as follows: -// 1 bit - whether netFD is closed, if set all subsequent lock operations will fail. +// 1 bit - whether FD is closed, if set all subsequent lock operations will fail. // 1 bit - lock for read operations. // 1 bit - lock for write operations. // 20 bits - total number of references (read+write+misc). @@ -196,9 +196,9 @@ func runtime_Semrelease(sema *uint32) // incref adds a reference to fd. // It returns an error when fd cannot be used. -func (fd *netFD) incref() error { +func (fd *FD) incref() error { if !fd.fdmu.incref() { - return errClosing + return ErrClosing } return nil } @@ -206,17 +206,18 @@ func (fd *netFD) incref() error { // decref removes a reference from fd. // It also closes fd when the state of fd is set to closed and there // is no remaining reference. -func (fd *netFD) decref() { +func (fd *FD) decref() error { if fd.fdmu.decref() { - fd.destroy() + return fd.destroy() } + return nil } // readLock adds a reference to fd and locks fd for reading. // It returns an error when fd cannot be used for reading. -func (fd *netFD) readLock() error { +func (fd *FD) readLock() error { if !fd.fdmu.rwlock(true) { - return errClosing + return ErrClosing } return nil } @@ -224,7 +225,7 @@ func (fd *netFD) readLock() error { // readUnlock removes a reference from fd and unlocks fd for reading. // It also closes fd when the state of fd is set to closed and there // is no remaining reference. -func (fd *netFD) readUnlock() { +func (fd *FD) readUnlock() { if fd.fdmu.rwunlock(true) { fd.destroy() } @@ -232,9 +233,9 @@ func (fd *netFD) readUnlock() { // writeLock adds a reference to fd and locks fd for writing. // It returns an error when fd cannot be used for writing. -func (fd *netFD) writeLock() error { +func (fd *FD) writeLock() error { if !fd.fdmu.rwlock(false) { - return errClosing + return ErrClosing } return nil } @@ -242,7 +243,7 @@ func (fd *netFD) writeLock() error { // writeUnlock removes a reference from fd and unlocks fd for writing. // It also closes fd when the state of fd is set to closed and there // is no remaining reference. -func (fd *netFD) writeUnlock() { +func (fd *FD) writeUnlock() { if fd.fdmu.rwunlock(false) { fd.destroy() } diff --git a/src/net/fd_mutex_test.go b/src/internal/poll/fd_mutex_test.go similarity index 67% rename from src/net/fd_mutex_test.go rename to src/internal/poll/fd_mutex_test.go index 3542c70f9d107..bab81c6dfee53 100644 --- a/src/net/fd_mutex_test.go +++ b/src/internal/poll/fd_mutex_test.go @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package net +package poll_test import ( + . "internal/poll" "math/rand" "runtime" "testing" @@ -12,57 +13,57 @@ import ( ) func TestMutexLock(t *testing.T) { - var mu fdMutex + var mu FDMutex - if !mu.incref() { + if !mu.Incref() { t.Fatal("broken") } - if mu.decref() { + if mu.Decref() { t.Fatal("broken") } - if !mu.rwlock(true) { + if !mu.RWLock(true) { t.Fatal("broken") } - if mu.rwunlock(true) { + if mu.RWUnlock(true) { t.Fatal("broken") } - if !mu.rwlock(false) { + if !mu.RWLock(false) { t.Fatal("broken") } - if mu.rwunlock(false) { + if mu.RWUnlock(false) { t.Fatal("broken") } } func TestMutexClose(t *testing.T) { - var mu fdMutex - if !mu.increfAndClose() { + var mu FDMutex + if !mu.IncrefAndClose() { t.Fatal("broken") } - if mu.incref() { + if mu.Incref() { t.Fatal("broken") } - if mu.rwlock(true) { + if mu.RWLock(true) { t.Fatal("broken") } - if mu.rwlock(false) { + if mu.RWLock(false) { t.Fatal("broken") } - if mu.increfAndClose() { + if mu.IncrefAndClose() { t.Fatal("broken") } } func TestMutexCloseUnblock(t *testing.T) { c := make(chan bool) - var mu fdMutex - mu.rwlock(true) + var mu FDMutex + mu.RWLock(true) for i := 0; i < 4; i++ { go func() { - if mu.rwlock(true) { + if mu.RWLock(true) { t.Error("broken") return } @@ -76,7 +77,7 @@ func TestMutexCloseUnblock(t *testing.T) { t.Fatal("broken") default: } - mu.increfAndClose() // Must unblock the readers. + mu.IncrefAndClose() // Must unblock the readers. for i := 0; i < 4; i++ { select { case <-c: @@ -84,10 +85,10 @@ func TestMutexCloseUnblock(t *testing.T) { t.Fatal("broken") } } - if mu.decref() { + if mu.Decref() { t.Fatal("broken") } - if !mu.rwunlock(true) { + if !mu.RWUnlock(true) { t.Fatal("broken") } } @@ -102,22 +103,22 @@ func TestMutexPanic(t *testing.T) { f() } - var mu fdMutex - ensurePanics(func() { mu.decref() }) - ensurePanics(func() { mu.rwunlock(true) }) - ensurePanics(func() { mu.rwunlock(false) }) + var mu FDMutex + ensurePanics(func() { mu.Decref() }) + ensurePanics(func() { mu.RWUnlock(true) }) + ensurePanics(func() { mu.RWUnlock(false) }) - ensurePanics(func() { mu.incref(); mu.decref(); mu.decref() }) - ensurePanics(func() { mu.rwlock(true); mu.rwunlock(true); mu.rwunlock(true) }) - ensurePanics(func() { mu.rwlock(false); mu.rwunlock(false); mu.rwunlock(false) }) + ensurePanics(func() { mu.Incref(); mu.Decref(); mu.Decref() }) + ensurePanics(func() { mu.RWLock(true); mu.RWUnlock(true); mu.RWUnlock(true) }) + ensurePanics(func() { mu.RWLock(false); mu.RWUnlock(false); mu.RWUnlock(false) }) // ensure that it's still not broken - mu.incref() - mu.decref() - mu.rwlock(true) - mu.rwunlock(true) - mu.rwlock(false) - mu.rwunlock(false) + mu.Incref() + mu.Decref() + mu.RWLock(true) + mu.RWUnlock(true) + mu.RWLock(false) + mu.RWUnlock(false) } func TestMutexStress(t *testing.T) { @@ -129,7 +130,7 @@ func TestMutexStress(t *testing.T) { } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P)) done := make(chan bool) - var mu fdMutex + var mu FDMutex var readState [2]uint64 var writeState [2]uint64 for p := 0; p < P; p++ { @@ -138,16 +139,16 @@ func TestMutexStress(t *testing.T) { for i := 0; i < N; i++ { switch r.Intn(3) { case 0: - if !mu.incref() { + if !mu.Incref() { t.Error("broken") return } - if mu.decref() { + if mu.Decref() { t.Error("broken") return } case 1: - if !mu.rwlock(true) { + if !mu.RWLock(true) { t.Error("broken") return } @@ -158,12 +159,12 @@ func TestMutexStress(t *testing.T) { } readState[0]++ readState[1]++ - if mu.rwunlock(true) { + if mu.RWUnlock(true) { t.Error("broken") return } case 2: - if !mu.rwlock(false) { + if !mu.RWLock(false) { t.Error("broken") return } @@ -174,7 +175,7 @@ func TestMutexStress(t *testing.T) { } writeState[0]++ writeState[1]++ - if mu.rwunlock(false) { + if mu.RWUnlock(false) { t.Error("broken") return } @@ -186,10 +187,10 @@ func TestMutexStress(t *testing.T) { for p := 0; p < P; p++ { <-done } - if !mu.increfAndClose() { + if !mu.IncrefAndClose() { t.Fatal("broken") } - if !mu.decref() { + if !mu.Decref() { t.Fatal("broken") } } diff --git a/src/internal/poll/fd_plan9.go b/src/internal/poll/fd_plan9.go new file mode 100644 index 0000000000000..fa9e566fa13c2 --- /dev/null +++ b/src/internal/poll/fd_plan9.go @@ -0,0 +1,180 @@ +// Copyright 2009 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 poll + +import ( + "io" + "sync/atomic" + "time" +) + +type atomicBool int32 + +func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } +func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } +func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } + +type FD struct { + // Lock sysfd and serialize access to Read and Write methods. + fdmu fdMutex + + Destroy func() + + // deadlines + raio *asyncIO + waio *asyncIO + rtimer *time.Timer + wtimer *time.Timer + rtimedout atomicBool // set true when read deadline has been reached + wtimedout atomicBool // set true when write deadline has been reached +} + +// We need this to close out a file descriptor when it is unlocked, +// but the real implementation has to live in the net package because +// it uses os.File's. +func (fd *FD) destroy() error { + if fd.Destroy != nil { + fd.Destroy() + } + return nil +} + +// Close handles the locking for closing an FD. The real operation +// is in the net package. +func (fd *FD) Close() error { + if !fd.fdmu.increfAndClose() { + return ErrClosing + } + return nil +} + +func (fd *FD) Read(fn func([]byte) (int, error), b []byte) (n int, err error) { + if fd.rtimedout.isSet() { + return 0, ErrTimeout + } + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + if len(b) == 0 { + return 0, nil + } + fd.raio = newAsyncIO(fn, b) + n, err = fd.raio.Wait() + fd.raio = nil + if isHangup(err) { + err = io.EOF + } + if isInterrupted(err) { + err = ErrTimeout + } + return +} + +func (fd *FD) Write(fn func([]byte) (int, error), b []byte) (n int, err error) { + if fd.wtimedout.isSet() { + return 0, ErrTimeout + } + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + fd.waio = newAsyncIO(fn, b) + n, err = fd.waio.Wait() + fd.waio = nil + if isInterrupted(err) { + err = ErrTimeout + } + return +} + +func (fd *FD) SetDeadline(t time.Time) error { + return setDeadlineImpl(fd, t, 'r'+'w') +} + +func (fd *FD) SetReadDeadline(t time.Time) error { + return setDeadlineImpl(fd, t, 'r') +} + +func (fd *FD) SetWriteDeadline(t time.Time) error { + return setDeadlineImpl(fd, t, 'w') +} + +func setDeadlineImpl(fd *FD, t time.Time, mode int) error { + d := t.Sub(time.Now()) + if mode == 'r' || mode == 'r'+'w' { + fd.rtimedout.setFalse() + } + if mode == 'w' || mode == 'r'+'w' { + fd.wtimedout.setFalse() + } + if t.IsZero() || d < 0 { + // Stop timer + if mode == 'r' || mode == 'r'+'w' { + if fd.rtimer != nil { + fd.rtimer.Stop() + } + fd.rtimer = nil + } + if mode == 'w' || mode == 'r'+'w' { + if fd.wtimer != nil { + fd.wtimer.Stop() + } + fd.wtimer = nil + } + } else { + // Interrupt I/O operation once timer has expired + if mode == 'r' || mode == 'r'+'w' { + fd.rtimer = time.AfterFunc(d, func() { + fd.rtimedout.setTrue() + if fd.raio != nil { + fd.raio.Cancel() + } + }) + } + if mode == 'w' || mode == 'r'+'w' { + fd.wtimer = time.AfterFunc(d, func() { + fd.wtimedout.setTrue() + if fd.waio != nil { + fd.waio.Cancel() + } + }) + } + } + if !t.IsZero() && d < 0 { + // Interrupt current I/O operation + if mode == 'r' || mode == 'r'+'w' { + fd.rtimedout.setTrue() + if fd.raio != nil { + fd.raio.Cancel() + } + } + if mode == 'w' || mode == 'r'+'w' { + fd.wtimedout.setTrue() + if fd.waio != nil { + fd.waio.Cancel() + } + } + } + return nil +} + +// On Plan 9 only, expose the locking for the net code. + +func (fd *FD) ReadLock() error { + return fd.readLock() +} + +func (fd *FD) ReadUnlock() { + fd.readUnlock() +} + +func isHangup(err error) bool { + return err != nil && stringsHasSuffix(err.Error(), "Hangup") +} + +func isInterrupted(err error) bool { + return err != nil && stringsHasSuffix(err.Error(), "interrupted") +} diff --git a/src/net/fd_poll_nacl.go b/src/internal/poll/fd_poll_nacl.go similarity index 66% rename from src/net/fd_poll_nacl.go rename to src/internal/poll/fd_poll_nacl.go index 83987602a585a..69c728d084365 100644 --- a/src/net/fd_poll_nacl.go +++ b/src/internal/poll/fd_poll_nacl.go @@ -2,34 +2,32 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package net +package poll import ( - "runtime" "syscall" "time" ) type pollDesc struct { - fd *netFD + fd *FD closing bool } -func (pd *pollDesc) init(fd *netFD) error { pd.fd = fd; return nil } +func (pd *pollDesc) init(fd *FD) error { pd.fd = fd; return nil } func (pd *pollDesc) close() {} func (pd *pollDesc) evict() { pd.closing = true if pd.fd != nil { - syscall.StopIO(pd.fd.sysfd) - runtime.KeepAlive(pd.fd) + syscall.StopIO(pd.fd.Sysfd) } } func (pd *pollDesc) prepare(mode int) error { if pd.closing { - return errClosing + return ErrClosing } return nil } @@ -40,9 +38,9 @@ func (pd *pollDesc) prepareWrite() error { return pd.prepare('w') } func (pd *pollDesc) wait(mode int) error { if pd.closing { - return errClosing + return ErrClosing } - return errTimeout + return ErrTimeout } func (pd *pollDesc) waitRead() error { return pd.wait('r') } @@ -55,19 +53,19 @@ func (pd *pollDesc) waitCanceledRead() {} func (pd *pollDesc) waitCanceledWrite() {} -func (fd *netFD) setDeadline(t time.Time) error { +func (fd *FD) SetDeadline(t time.Time) error { return setDeadlineImpl(fd, t, 'r'+'w') } -func (fd *netFD) setReadDeadline(t time.Time) error { +func (fd *FD) SetReadDeadline(t time.Time) error { return setDeadlineImpl(fd, t, 'r') } -func (fd *netFD) setWriteDeadline(t time.Time) error { +func (fd *FD) SetWriteDeadline(t time.Time) error { return setDeadlineImpl(fd, t, 'w') } -func setDeadlineImpl(fd *netFD, t time.Time, mode int) error { +func setDeadlineImpl(fd *FD, t time.Time, mode int) error { d := t.UnixNano() if t.IsZero() { d = 0 @@ -77,12 +75,12 @@ func setDeadlineImpl(fd *netFD, t time.Time, mode int) error { } switch mode { case 'r': - syscall.SetReadDeadline(fd.sysfd, d) + syscall.SetReadDeadline(fd.Sysfd, d) case 'w': - syscall.SetWriteDeadline(fd.sysfd, d) + syscall.SetWriteDeadline(fd.Sysfd, d) case 'r' + 'w': - syscall.SetReadDeadline(fd.sysfd, d) - syscall.SetWriteDeadline(fd.sysfd, d) + syscall.SetReadDeadline(fd.Sysfd, d) + syscall.SetWriteDeadline(fd.Sysfd, d) } fd.decref() return nil diff --git a/src/net/fd_poll_runtime.go b/src/internal/poll/fd_poll_runtime.go similarity index 79% rename from src/net/fd_poll_runtime.go rename to src/internal/poll/fd_poll_runtime.go index 62b69fcbf1505..f0d7a22729977 100644 --- a/src/net/fd_poll_runtime.go +++ b/src/internal/poll/fd_poll_runtime.go @@ -4,10 +4,10 @@ // +build darwin dragonfly freebsd linux netbsd openbsd windows solaris -package net +package poll import ( - "runtime" + "errors" "sync" "syscall" "time" @@ -31,11 +31,14 @@ type pollDesc struct { var serverInit sync.Once -func (pd *pollDesc) init(fd *netFD) error { +func (pd *pollDesc) init(fd *FD) error { serverInit.Do(runtime_pollServerInit) - ctx, errno := runtime_pollOpen(uintptr(fd.sysfd)) - runtime.KeepAlive(fd) + ctx, errno := runtime_pollOpen(uintptr(fd.Sysfd)) if errno != 0 { + if ctx != 0 { + runtime_pollUnblock(ctx) + runtime_pollClose(ctx) + } return syscall.Errno(errno) } pd.runtimeCtx = ctx @@ -59,6 +62,9 @@ func (pd *pollDesc) evict() { } func (pd *pollDesc) prepare(mode int) error { + if pd.runtimeCtx == 0 { + return nil + } res := runtime_pollReset(pd.runtimeCtx, mode) return convertErr(res) } @@ -72,6 +78,9 @@ func (pd *pollDesc) prepareWrite() error { } func (pd *pollDesc) wait(mode int) error { + if pd.runtimeCtx == 0 { + return errors.New("waiting for unsupported file type") + } res := runtime_pollWait(pd.runtimeCtx, mode) return convertErr(res) } @@ -85,6 +94,9 @@ func (pd *pollDesc) waitWrite() error { } func (pd *pollDesc) waitCanceled(mode int) { + if pd.runtimeCtx == 0 { + return + } runtime_pollWaitCanceled(pd.runtimeCtx, mode) } @@ -101,27 +113,27 @@ func convertErr(res int) error { case 0: return nil case 1: - return errClosing + return ErrClosing case 2: - return errTimeout + return ErrTimeout } println("unreachable: ", res) panic("unreachable") } -func (fd *netFD) setDeadline(t time.Time) error { +func (fd *FD) SetDeadline(t time.Time) error { return setDeadlineImpl(fd, t, 'r'+'w') } -func (fd *netFD) setReadDeadline(t time.Time) error { +func (fd *FD) SetReadDeadline(t time.Time) error { return setDeadlineImpl(fd, t, 'r') } -func (fd *netFD) setWriteDeadline(t time.Time) error { +func (fd *FD) SetWriteDeadline(t time.Time) error { return setDeadlineImpl(fd, t, 'w') } -func setDeadlineImpl(fd *netFD, t time.Time, mode int) error { +func setDeadlineImpl(fd *FD, t time.Time, mode int) error { diff := int64(time.Until(t)) d := runtimeNano() + diff if d <= 0 && diff > 0 { @@ -135,6 +147,9 @@ func setDeadlineImpl(fd *netFD, t time.Time, mode int) error { if err := fd.incref(); err != nil { return err } + if fd.pd.runtimeCtx == 0 { + return errors.New("file type does not support deadlines") + } runtime_pollSetDeadline(fd.pd.runtimeCtx, d, mode) fd.decref() return nil diff --git a/src/internal/poll/fd_posix.go b/src/internal/poll/fd_posix.go new file mode 100644 index 0000000000000..e0e634cdb2900 --- /dev/null +++ b/src/internal/poll/fd_posix.go @@ -0,0 +1,57 @@ +// Copyright 2009 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. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows + +package poll + +import ( + "io" + "syscall" +) + +// eofError returns io.EOF when fd is available for reading end of +// file. +func (fd *FD) eofError(n int, err error) error { + if n == 0 && err == nil && fd.ZeroReadIsEOF { + return io.EOF + } + return err +} + +// Fchmod wraps syscall.Fchmod. +func (fd *FD) Fchmod(mode uint32) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Fchmod(fd.Sysfd, mode) +} + +// Fchown wraps syscall.Fchown. +func (fd *FD) Fchown(uid, gid int) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Fchown(fd.Sysfd, uid, gid) +} + +// Ftruncate wraps syscall.Ftruncate. +func (fd *FD) Ftruncate(size int64) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Ftruncate(fd.Sysfd, size) +} + +// Fsync wraps syscall.Fsync. +func (fd *FD) Fsync() error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Fsync(fd.Sysfd) +} diff --git a/src/internal/poll/fd_posix_test.go b/src/internal/poll/fd_posix_test.go new file mode 100644 index 0000000000000..edc2dcb97d077 --- /dev/null +++ b/src/internal/poll/fd_posix_test.go @@ -0,0 +1,43 @@ +// 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. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows + +package poll_test + +import ( + . "internal/poll" + "io" + "testing" +) + +var eofErrorTests = []struct { + n int + err error + fd *FD + expected error +}{ + {100, nil, &FD{ZeroReadIsEOF: true}, nil}, + {100, io.EOF, &FD{ZeroReadIsEOF: true}, io.EOF}, + {100, ErrClosing, &FD{ZeroReadIsEOF: true}, ErrClosing}, + {0, nil, &FD{ZeroReadIsEOF: true}, io.EOF}, + {0, io.EOF, &FD{ZeroReadIsEOF: true}, io.EOF}, + {0, ErrClosing, &FD{ZeroReadIsEOF: true}, ErrClosing}, + + {100, nil, &FD{ZeroReadIsEOF: false}, nil}, + {100, io.EOF, &FD{ZeroReadIsEOF: false}, io.EOF}, + {100, ErrClosing, &FD{ZeroReadIsEOF: false}, ErrClosing}, + {0, nil, &FD{ZeroReadIsEOF: false}, nil}, + {0, io.EOF, &FD{ZeroReadIsEOF: false}, io.EOF}, + {0, ErrClosing, &FD{ZeroReadIsEOF: false}, ErrClosing}, +} + +func TestEOFError(t *testing.T) { + for _, tt := range eofErrorTests { + actual := tt.fd.EOFError(tt.n, tt.err) + if actual != tt.expected { + t.Errorf("eofError(%v, %v, %v): expected %v, actual %v", tt.n, tt.err, tt.fd.ZeroReadIsEOF, tt.expected, actual) + } + } +} diff --git a/src/internal/poll/fd_unix.go b/src/internal/poll/fd_unix.go new file mode 100644 index 0000000000000..d97490a257304 --- /dev/null +++ b/src/internal/poll/fd_unix.go @@ -0,0 +1,398 @@ +// Copyright 2017 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. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +package poll + +import ( + "io" + "syscall" +) + +// FD is a file descriptor. The net and os packages use this type as a +// field of a larger type representing a network connection or OS file. +type FD struct { + // Lock sysfd and serialize access to Read and Write methods. + fdmu fdMutex + + // System file descriptor. Immutable until Close. + Sysfd int + + // I/O poller. + pd pollDesc + + // Writev cache. + iovecs *[]syscall.Iovec + + // Whether this is a streaming descriptor, as opposed to a + // packet-based descriptor like a UDP socket. Immutable. + IsStream bool + + // Whether a zero byte read indicates EOF. This is false for a + // message based socket connection. + ZeroReadIsEOF bool +} + +// Init initializes the FD. The Sysfd field should already be set. +// This can be called multiple times on a single FD. +func (fd *FD) Init() error { + return fd.pd.init(fd) +} + +// Destroy closes the file descriptor. This is called when there are +// no remaining references. +func (fd *FD) destroy() error { + // Poller may want to unregister fd in readiness notification mechanism, + // so this must be executed before CloseFunc. + fd.pd.close() + err := CloseFunc(fd.Sysfd) + fd.Sysfd = -1 + return err +} + +// Close closes the FD. The underlying file descriptor is closed by the +// destroy method when there are no remaining references. +func (fd *FD) Close() error { + if !fd.fdmu.increfAndClose() { + return ErrClosing + } + // Unblock any I/O. Once it all unblocks and returns, + // so that it cannot be referring to fd.sysfd anymore, + // the final decref will close fd.sysfd. This should happen + // fairly quickly, since all the I/O is non-blocking, and any + // attempts to block in the pollDesc will return ErrClosing. + fd.pd.evict() + // The call to decref will call destroy if there are no other + // references. + return fd.decref() +} + +// Shutdown wraps the shutdown call. +func (fd *FD) Shutdown(how int) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Shutdown(fd.Sysfd, how) +} + +// Darwin and FreeBSD can't read or write 2GB+ files at a time, +// even on 64-bit systems. +// The same is true of socket implementations on many systems. +// See golang.org/issue/7812 and golang.org/issue/16266. +// Use 1GB instead of, say, 2GB-1, to keep subsequent reads aligned. +const maxRW = 1 << 30 + +// Read implements io.Reader. +func (fd *FD) Read(p []byte) (n int, err error) { + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + if len(p) == 0 { + // If the caller wanted a zero byte read, return immediately + // without trying (but after acquiring the readLock). + // Otherwise syscall.Read returns 0, nil which looks like + // io.EOF. + // TODO(bradfitz): make it wait for readability? (Issue 15735) + return 0, nil + } + if err := fd.pd.prepareRead(); err != nil { + return 0, err + } + if fd.IsStream && len(p) > maxRW { + p = p[:maxRW] + } + for { + n, err = syscall.Read(fd.Sysfd, p) + if err != nil { + n = 0 + if err == syscall.EAGAIN { + if err = fd.pd.waitRead(); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + break + } + return +} + +// Pread wraps the pread system call. +func (fd *FD) Pread(p []byte, off int64) (n int, err error) { + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(); err != nil { + return 0, err + } + if fd.IsStream && len(p) > maxRW { + p = p[:maxRW] + } + for { + n, err = syscall.Pread(fd.Sysfd, p, off) + if err != nil { + n = 0 + if err == syscall.EAGAIN { + if err = fd.pd.waitRead(); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + break + } + return +} + +// RecvFrom wraps the recvfrom network call. +func (fd *FD) RecvFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { + if err := fd.readLock(); err != nil { + return 0, nil, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(); err != nil { + return 0, nil, err + } + for { + n, sa, err = syscall.Recvfrom(fd.Sysfd, p, 0) + if err != nil { + n = 0 + if err == syscall.EAGAIN { + if err = fd.pd.waitRead(); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + break + } + return +} + +// ReadMsg wraps the recvmsg network call. +func (fd *FD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) { + if err := fd.readLock(); err != nil { + return 0, 0, 0, nil, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(); err != nil { + return 0, 0, 0, nil, err + } + for { + n, oobn, flags, sa, err = syscall.Recvmsg(fd.Sysfd, p, oob, 0) + if err != nil { + // TODO(dfc) should n and oobn be set to 0 + if err == syscall.EAGAIN { + if err = fd.pd.waitRead(); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + break + } + return +} + +// Write implements io.Writer. +func (fd *FD) Write(p []byte) (nn int, err error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(); err != nil { + return 0, err + } + for { + var n int + max := len(p) + if fd.IsStream && max-nn > maxRW { + max = nn + maxRW + } + n, err = syscall.Write(fd.Sysfd, p[nn:max]) + if n > 0 { + nn += n + } + if nn == len(p) { + break + } + if err == syscall.EAGAIN { + if err = fd.pd.waitWrite(); err == nil { + continue + } + } + if err != nil { + break + } + if n == 0 { + err = io.ErrUnexpectedEOF + break + } + } + return +} + +// Pwrite wraps the pwrite system call. +func (fd *FD) Pwrite(p []byte, off int64) (nn int, err error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(); err != nil { + return 0, err + } + for { + var n int + max := len(p) + if fd.IsStream && max-nn > maxRW { + max = nn + maxRW + } + n, err = syscall.Pwrite(fd.Sysfd, p[nn:max], off+int64(nn)) + if n > 0 { + nn += n + } + if nn == len(p) { + break + } + if err == syscall.EAGAIN { + if err = fd.pd.waitWrite(); err == nil { + continue + } + } + if err != nil { + break + } + if n == 0 { + err = io.ErrUnexpectedEOF + break + } + } + return +} + +// WriteTo wraps the sendto network call. +func (fd *FD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(); err != nil { + return 0, err + } + for { + err = syscall.Sendto(fd.Sysfd, p, 0, sa) + if err == syscall.EAGAIN { + if err = fd.pd.waitWrite(); err == nil { + continue + } + } + break + } + if err == nil { + n = len(p) + } + return +} + +// WriteMsg wraps the sendmsg network call. +func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { + if err := fd.writeLock(); err != nil { + return 0, 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(); err != nil { + return 0, 0, err + } + for { + n, err = syscall.SendmsgN(fd.Sysfd, p, oob, sa, 0) + if err == syscall.EAGAIN { + if err = fd.pd.waitWrite(); err == nil { + continue + } + } + break + } + if err == nil { + oobn = len(oob) + } + return +} + +// WaitWrite waits until data can be written to fd. +func (fd *FD) WaitWrite() error { + return fd.pd.waitWrite() +} + +// Accept wraps the accept network call. +func (fd *FD) Accept() (newfd int, rsa syscall.Sockaddr, errcall string, err error) { + if err = fd.readLock(); err != nil { + return -1, nil, "", err + } + defer fd.readUnlock() + + var s int + if err = fd.pd.prepareRead(); err != nil { + return -1, nil, "", err + } + for { + s, rsa, errcall, err = accept(fd.Sysfd) + if err == nil { + return s, rsa, "", err + } + switch err { + case syscall.EAGAIN: + if err = fd.pd.waitRead(); err == nil { + continue + } + case syscall.ECONNABORTED: + // This means that a socket on the listen + // queue was closed before we Accept()ed it; + // it's a silly error, so try again. + continue + } + return -1, nil, errcall, err + } +} + +// Seek wraps syscall.Seek. +func (fd *FD) Seek(offset int64, whence int) (ret int64, err error) { + if err := fd.incref(); err != nil { + return 0, err + } + defer fd.decref() + return syscall.Seek(fd.Sysfd, offset, whence) +} + +// ReadDirent wraps syscall.ReadDirent. +// We treat this like an ordinary system call rather than a call +// that tries to fill the buffer. +func (fd *FD) ReadDirent(buf []byte) (n int, err error) { + if err := fd.incref(); err != nil { + return 0, err + } + defer fd.decref() + return syscall.ReadDirent(fd.Sysfd, buf) +} + +// Fchdir wraps syscall.Fchdir. +func (fd *FD) Fchdir() error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Fchdir(fd.Sysfd) +} + +// Fstat wraps syscall.Fstat +func (fd *FD) Fstat(s *syscall.Stat_t) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Fstat(fd.Sysfd, s) +} diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go new file mode 100644 index 0000000000000..33df876e9b816 --- /dev/null +++ b/src/internal/poll/fd_windows.go @@ -0,0 +1,816 @@ +// Copyright 2017 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 poll + +import ( + "errors" + "internal/race" + "io" + "runtime" + "sync" + "syscall" + "unicode/utf16" + "unicode/utf8" + "unsafe" +) + +var ( + initErr error + ioSync uint64 +) + +// CancelIo Windows API cancels all outstanding IO for a particular +// socket on current thread. To overcome that limitation, we run +// special goroutine, locked to OS single thread, that both starts +// and cancels IO. It means, there are 2 unavoidable thread switches +// for every IO. +// Some newer versions of Windows has new CancelIoEx API, that does +// not have that limitation and can be used from any thread. This +// package uses CancelIoEx API, if present, otherwise it fallback +// to CancelIo. + +var ( + canCancelIO bool // determines if CancelIoEx API is present + skipSyncNotif bool + hasLoadSetFileCompletionNotificationModes bool +) + +func init() { + var d syscall.WSAData + e := syscall.WSAStartup(uint32(0x202), &d) + if e != nil { + initErr = e + } + canCancelIO = syscall.LoadCancelIoEx() == nil + hasLoadSetFileCompletionNotificationModes = syscall.LoadSetFileCompletionNotificationModes() == nil + if hasLoadSetFileCompletionNotificationModes { + // It's not safe to use FILE_SKIP_COMPLETION_PORT_ON_SUCCESS if non IFS providers are installed: + // http://support.microsoft.com/kb/2568167 + skipSyncNotif = true + protos := [2]int32{syscall.IPPROTO_TCP, 0} + var buf [32]syscall.WSAProtocolInfo + len := uint32(unsafe.Sizeof(buf)) + n, err := syscall.WSAEnumProtocols(&protos[0], &buf[0], &len) + if err != nil { + skipSyncNotif = false + } else { + for i := int32(0); i < n; i++ { + if buf[i].ServiceFlags1&syscall.XP1_IFS_HANDLES == 0 { + skipSyncNotif = false + break + } + } + } + } +} + +// operation contains superset of data necessary to perform all async IO. +type operation struct { + // Used by IOCP interface, it must be first field + // of the struct, as our code rely on it. + o syscall.Overlapped + + // fields used by runtime.netpoll + runtimeCtx uintptr + mode int32 + errno int32 + qty uint32 + + // fields used only by net package + fd *FD + errc chan error + buf syscall.WSABuf + sa syscall.Sockaddr + rsa *syscall.RawSockaddrAny + rsan int32 + handle syscall.Handle + flags uint32 + bufs []syscall.WSABuf +} + +func (o *operation) InitBuf(buf []byte) { + o.buf.Len = uint32(len(buf)) + o.buf.Buf = nil + if len(buf) != 0 { + o.buf.Buf = &buf[0] + } +} + +func (o *operation) InitBufs(buf *[][]byte) { + if o.bufs == nil { + o.bufs = make([]syscall.WSABuf, 0, len(*buf)) + } else { + o.bufs = o.bufs[:0] + } + for _, b := range *buf { + var p *byte + if len(b) > 0 { + p = &b[0] + } + o.bufs = append(o.bufs, syscall.WSABuf{Len: uint32(len(b)), Buf: p}) + } +} + +// ClearBufs clears all pointers to Buffers parameter captured +// by InitBufs, so it can be released by garbage collector. +func (o *operation) ClearBufs() { + for i := range o.bufs { + o.bufs[i].Buf = nil + } + o.bufs = o.bufs[:0] +} + +// ioSrv executes net IO requests. +type ioSrv struct { + req chan ioSrvReq +} + +type ioSrvReq struct { + o *operation + submit func(o *operation) error // if nil, cancel the operation +} + +// ProcessRemoteIO will execute submit IO requests on behalf +// of other goroutines, all on a single os thread, so it can +// cancel them later. Results of all operations will be sent +// back to their requesters via channel supplied in request. +// It is used only when the CancelIoEx API is unavailable. +func (s *ioSrv) ProcessRemoteIO() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + for r := range s.req { + if r.submit != nil { + r.o.errc <- r.submit(r.o) + } else { + r.o.errc <- syscall.CancelIo(r.o.fd.Sysfd) + } + } +} + +// ExecIO executes a single IO operation o. It submits and cancels +// IO in the current thread for systems where Windows CancelIoEx API +// is available. Alternatively, it passes the request onto +// runtime netpoll and waits for completion or cancels request. +func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) error) (int, error) { + fd := o.fd + // Notify runtime netpoll about starting IO. + err := fd.pd.prepare(int(o.mode)) + if err != nil { + return 0, err + } + // Start IO. + if canCancelIO { + err = submit(o) + } else { + // Send request to a special dedicated thread, + // so it can stop the IO with CancelIO later. + s.req <- ioSrvReq{o, submit} + err = <-o.errc + } + switch err { + case nil: + // IO completed immediately + if o.fd.skipSyncNotif { + // No completion message will follow, so return immediately. + return int(o.qty), nil + } + // Need to get our completion message anyway. + case syscall.ERROR_IO_PENDING: + // IO started, and we have to wait for its completion. + err = nil + default: + return 0, err + } + // Wait for our request to complete. + err = fd.pd.wait(int(o.mode)) + if err == nil { + // All is good. Extract our IO results and return. + if o.errno != 0 { + err = syscall.Errno(o.errno) + return 0, err + } + return int(o.qty), nil + } + // IO is interrupted by "close" or "timeout" + netpollErr := err + switch netpollErr { + case ErrClosing, ErrTimeout: + // will deal with those. + default: + panic("net: unexpected runtime.netpoll error: " + netpollErr.Error()) + } + // Cancel our request. + if canCancelIO { + err := syscall.CancelIoEx(fd.Sysfd, &o.o) + // Assuming ERROR_NOT_FOUND is returned, if IO is completed. + if err != nil && err != syscall.ERROR_NOT_FOUND { + // TODO(brainman): maybe do something else, but panic. + panic(err) + } + } else { + s.req <- ioSrvReq{o, nil} + <-o.errc + } + // Wait for cancelation to complete. + fd.pd.waitCanceled(int(o.mode)) + if o.errno != 0 { + err = syscall.Errno(o.errno) + if err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled + err = netpollErr + } + return 0, err + } + // We issued a cancelation request. But, it seems, IO operation succeeded + // before the cancelation request run. We need to treat the IO operation as + // succeeded (the bytes are actually sent/recv from network). + return int(o.qty), nil +} + +// Start helper goroutines. +var rsrv, wsrv *ioSrv +var onceStartServer sync.Once + +func startServer() { + rsrv = new(ioSrv) + wsrv = new(ioSrv) + if !canCancelIO { + // Only CancelIo API is available. Lets start two special goroutines + // locked to an OS thread, that both starts and cancels IO. One will + // process read requests, while other will do writes. + rsrv.req = make(chan ioSrvReq) + go rsrv.ProcessRemoteIO() + wsrv.req = make(chan ioSrvReq) + go wsrv.ProcessRemoteIO() + } +} + +// FD is a file descriptor. The net and os packages embed this type in +// a larger type representing a network connection or OS file. +type FD struct { + // Lock sysfd and serialize access to Read and Write methods. + fdmu fdMutex + + // System file descriptor. Immutable until Close. + Sysfd syscall.Handle + + // Read operation. + rop operation + // Write operation. + wop operation + + // I/O poller. + pd pollDesc + + // Used to implement pread/pwrite. + l sync.Mutex + + // For console I/O. + isConsole bool + lastbits []byte // first few bytes of the last incomplete rune in last write + readuint16 []uint16 // buffer to hold uint16s obtained with ReadConsole + readbyte []byte // buffer to hold decoding of readuint16 from utf16 to utf8 + readbyteOffset int // readbyte[readOffset:] is yet to be consumed with file.Read + + skipSyncNotif bool + + // Whether this is a streaming descriptor, as opposed to a + // packet-based descriptor like a UDP socket. + IsStream bool + + // Whether a zero byte read indicates EOF. This is false for a + // message based socket connection. + ZeroReadIsEOF bool + + // Whether this is a normal file. + isFile bool + + // Whether this is a directory. + isDir bool +} + +// Init initializes the FD. The Sysfd field should already be set. +// This can be called multiple times on a single FD. +// The net argument is a network name from the net package (e.g., "tcp"), +// or "file" or "console" or "dir". +func (fd *FD) Init(net string) (string, error) { + if initErr != nil { + return "", initErr + } + onceStartServer.Do(startServer) + + switch net { + case "file": + fd.isFile = true + case "console": + fd.isConsole = true + case "dir": + fd.isDir = true + case "tcp", "tcp4", "tcp6": + case "udp", "udp4", "udp6": + case "ip", "ip4", "ip6": + case "unix", "unixgram", "unixpacket": + default: + return "", errors.New("internal error: unknown network type " + net) + } + + if err := fd.pd.init(fd); err != nil { + return "", err + } + if hasLoadSetFileCompletionNotificationModes { + // We do not use events, so we can skip them always. + flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE) + // It's not safe to skip completion notifications for UDP: + // http://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx + if skipSyncNotif && (net == "tcp" || net == "file") { + flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS + } + err := syscall.SetFileCompletionNotificationModes(fd.Sysfd, flags) + if err == nil && flags&syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS != 0 { + fd.skipSyncNotif = true + } + } + // Disable SIO_UDP_CONNRESET behavior. + // http://support.microsoft.com/kb/263823 + switch net { + case "udp", "udp4", "udp6": + ret := uint32(0) + flag := uint32(0) + size := uint32(unsafe.Sizeof(flag)) + err := syscall.WSAIoctl(fd.Sysfd, syscall.SIO_UDP_CONNRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0) + if err != nil { + return "wsaioctl", err + } + } + fd.rop.mode = 'r' + fd.wop.mode = 'w' + fd.rop.fd = fd + fd.wop.fd = fd + fd.rop.runtimeCtx = fd.pd.runtimeCtx + fd.wop.runtimeCtx = fd.pd.runtimeCtx + if !canCancelIO { + fd.rop.errc = make(chan error) + fd.wop.errc = make(chan error) + } + return "", nil +} + +func (fd *FD) destroy() error { + if fd.Sysfd == syscall.InvalidHandle { + return syscall.EINVAL + } + // Poller may want to unregister fd in readiness notification mechanism, + // so this must be executed before fd.CloseFunc. + fd.pd.close() + var err error + if fd.isFile || fd.isConsole { + err = syscall.CloseHandle(fd.Sysfd) + } else if fd.isDir { + err = syscall.FindClose(fd.Sysfd) + } else { + // The net package uses the CloseFunc variable for testing. + err = CloseFunc(fd.Sysfd) + } + fd.Sysfd = syscall.InvalidHandle + return err +} + +func (fd *FD) Close() error { + if !fd.fdmu.increfAndClose() { + return ErrClosing + } + // unblock pending reader and writer + fd.pd.evict() + return fd.decref() +} + +func (fd *FD) Shutdown(how int) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Shutdown(fd.Sysfd, how) +} + +func (fd *FD) Read(buf []byte) (int, error) { + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + + var n int + var err error + if fd.isFile || fd.isDir || fd.isConsole { + fd.l.Lock() + defer fd.l.Unlock() + if fd.isConsole { + n, err = fd.readConsole(buf) + } else { + n, err = syscall.Read(fd.Sysfd, buf) + } + if err != nil { + n = 0 + } + } else { + o := &fd.rop + o.InitBuf(buf) + n, err = rsrv.ExecIO(o, "WSARecv", func(o *operation) error { + return syscall.WSARecv(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil) + }) + if race.Enabled { + race.Acquire(unsafe.Pointer(&ioSync)) + } + } + if len(buf) != 0 { + err = fd.eofError(n, err) + } + return n, err +} + +var ReadConsole = syscall.ReadConsole // changed for testing + +// readConsole reads utf16 characters from console File, +// encodes them into utf8 and stores them in buffer b. +// It returns the number of utf8 bytes read and an error, if any. +func (fd *FD) readConsole(b []byte) (n int, err error) { + if len(b) == 0 { + return 0, nil + } + + if fd.readuint16 == nil { + // Note: syscall.ReadConsole fails for very large buffers. + // The limit is somewhere around (but not exactly) 16384. + // Stay well below. + fd.readuint16 = make([]uint16, 0, 10000) + fd.readbyte = make([]byte, 0, 4*cap(fd.readuint16)) + } + + for fd.readbyteOffset >= len(fd.readbyte) { + n := cap(fd.readuint16) - len(fd.readuint16) + if n > len(b) { + n = len(b) + } + var nw uint32 + err := ReadConsole(fd.Sysfd, &fd.readuint16[:len(fd.readuint16)+1][len(fd.readuint16)], uint32(n), &nw, nil) + if err != nil { + return 0, err + } + uint16s := fd.readuint16[:len(fd.readuint16)+int(nw)] + fd.readuint16 = fd.readuint16[:0] + buf := fd.readbyte[:0] + for i := 0; i < len(uint16s); i++ { + r := rune(uint16s[i]) + if utf16.IsSurrogate(r) { + if i+1 == len(uint16s) { + if nw > 0 { + // Save half surrogate pair for next time. + fd.readuint16 = fd.readuint16[:1] + fd.readuint16[0] = uint16(r) + break + } + r = utf8.RuneError + } else { + r = utf16.DecodeRune(r, rune(uint16s[i+1])) + if r != utf8.RuneError { + i++ + } + } + } + n := utf8.EncodeRune(buf[len(buf):cap(buf)], r) + buf = buf[:len(buf)+n] + } + fd.readbyte = buf + fd.readbyteOffset = 0 + if nw == 0 { + break + } + } + + src := fd.readbyte[fd.readbyteOffset:] + var i int + for i = 0; i < len(src) && i < len(b); i++ { + x := src[i] + if x == 0x1A { // Ctrl-Z + if i == 0 { + fd.readbyteOffset++ + } + break + } + b[i] = x + } + fd.readbyteOffset += i + return i, nil +} + +func (fd *FD) Pread(b []byte, off int64) (n int, err error) { + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + + fd.l.Lock() + defer fd.l.Unlock() + curoffset, e := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent) + if e != nil { + return 0, e + } + defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart) + o := syscall.Overlapped{ + OffsetHigh: uint32(off >> 32), + Offset: uint32(off), + } + var done uint32 + e = syscall.ReadFile(fd.Sysfd, b, &done, &o) + if e != nil { + if e == syscall.ERROR_HANDLE_EOF { + // end of file + return 0, nil + } + return 0, e + } + return int(done), nil +} + +func (fd *FD) RecvFrom(buf []byte) (int, syscall.Sockaddr, error) { + if len(buf) == 0 { + return 0, nil, nil + } + if err := fd.readLock(); err != nil { + return 0, nil, err + } + defer fd.readUnlock() + o := &fd.rop + o.InitBuf(buf) + n, err := rsrv.ExecIO(o, "WSARecvFrom", func(o *operation) error { + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.rsan = int32(unsafe.Sizeof(*o.rsa)) + return syscall.WSARecvFrom(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil) + }) + err = fd.eofError(n, err) + if err != nil { + return n, nil, err + } + sa, _ := o.rsa.Sockaddr() + return n, sa, nil +} + +func (fd *FD) Write(buf []byte) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + + var n int + var err error + if fd.isFile || fd.isDir || fd.isConsole { + fd.l.Lock() + defer fd.l.Unlock() + if fd.isConsole { + n, err = fd.writeConsole(buf) + } else { + n, err = syscall.Write(fd.Sysfd, buf) + } + if err != nil { + n = 0 + } + } else { + if race.Enabled { + race.ReleaseMerge(unsafe.Pointer(&ioSync)) + } + o := &fd.wop + o.InitBuf(buf) + n, err = wsrv.ExecIO(o, "WSASend", func(o *operation) error { + return syscall.WSASend(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, &o.o, nil) + }) + } + return n, err +} + +// writeConsole writes len(b) bytes to the console File. +// It returns the number of bytes written and an error, if any. +func (fd *FD) writeConsole(b []byte) (n int, err error) { + n = len(b) + runes := make([]rune, 0, 256) + if len(fd.lastbits) > 0 { + b = append(fd.lastbits, b...) + fd.lastbits = nil + + } + for len(b) >= utf8.UTFMax || utf8.FullRune(b) { + r, l := utf8.DecodeRune(b) + runes = append(runes, r) + b = b[l:] + } + if len(b) > 0 { + fd.lastbits = make([]byte, len(b)) + copy(fd.lastbits, b) + } + // syscall.WriteConsole seems to fail, if given large buffer. + // So limit the buffer to 16000 characters. This number was + // discovered by experimenting with syscall.WriteConsole. + const maxWrite = 16000 + for len(runes) > 0 { + m := len(runes) + if m > maxWrite { + m = maxWrite + } + chunk := runes[:m] + runes = runes[m:] + uint16s := utf16.Encode(chunk) + for len(uint16s) > 0 { + var written uint32 + err = syscall.WriteConsole(fd.Sysfd, &uint16s[0], uint32(len(uint16s)), &written, nil) + if err != nil { + return 0, nil + } + uint16s = uint16s[written:] + } + } + return n, nil +} + +func (fd *FD) Pwrite(b []byte, off int64) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + + fd.l.Lock() + defer fd.l.Unlock() + curoffset, e := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent) + if e != nil { + return 0, e + } + defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart) + o := syscall.Overlapped{ + OffsetHigh: uint32(off >> 32), + Offset: uint32(off), + } + var done uint32 + e = syscall.WriteFile(fd.Sysfd, b, &done, &o) + if e != nil { + return 0, e + } + return int(done), nil +} + +func (fd *FD) Writev(buf *[][]byte) (int64, error) { + if len(*buf) == 0 { + return 0, nil + } + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if race.Enabled { + race.ReleaseMerge(unsafe.Pointer(&ioSync)) + } + o := &fd.wop + o.InitBufs(buf) + n, err := wsrv.ExecIO(o, "WSASend", func(o *operation) error { + return syscall.WSASend(o.fd.Sysfd, &o.bufs[0], uint32(len(*buf)), &o.qty, 0, &o.o, nil) + }) + o.ClearBufs() + TestHookDidWritev(n) + consume(buf, int64(n)) + return int64(n), err +} + +func (fd *FD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { + if len(buf) == 0 { + return 0, nil + } + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + o := &fd.wop + o.InitBuf(buf) + o.sa = sa + n, err := wsrv.ExecIO(o, "WSASendto", func(o *operation) error { + return syscall.WSASendto(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil) + }) + return n, err +} + +// Call ConnectEx. This doesn't need any locking, since it is only +// called when the descriptor is first created. This is here rather +// than in the net package so that it can use fd.wop. +func (fd *FD) ConnectEx(ra syscall.Sockaddr) error { + o := &fd.wop + o.sa = ra + _, err := wsrv.ExecIO(o, "ConnectEx", func(o *operation) error { + return ConnectExFunc(o.fd.Sysfd, o.sa, nil, 0, nil, &o.o) + }) + return err +} + +func (fd *FD) acceptOne(s syscall.Handle, rawsa []syscall.RawSockaddrAny, o *operation) (string, error) { + // Submit accept request. + o.handle = s + o.rsan = int32(unsafe.Sizeof(rawsa[0])) + _, err := rsrv.ExecIO(o, "AcceptEx", func(o *operation) error { + return AcceptFunc(o.fd.Sysfd, o.handle, (*byte)(unsafe.Pointer(&rawsa[0])), 0, uint32(o.rsan), uint32(o.rsan), &o.qty, &o.o) + }) + if err != nil { + CloseFunc(s) + return "acceptex", err + } + + // Inherit properties of the listening socket. + err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.Sysfd)), int32(unsafe.Sizeof(fd.Sysfd))) + if err != nil { + CloseFunc(s) + return "setsockopt", err + } + + return "", nil +} + +// Accept handles accepting a socket. The sysSocket parameter is used +// to allocate the net socket. +func (fd *FD) Accept(sysSocket func() (syscall.Handle, error)) (syscall.Handle, []syscall.RawSockaddrAny, uint32, string, error) { + if err := fd.readLock(); err != nil { + return syscall.InvalidHandle, nil, 0, "", err + } + defer fd.readUnlock() + + o := &fd.rop + var rawsa [2]syscall.RawSockaddrAny + for { + s, err := sysSocket() + if err != nil { + return syscall.InvalidHandle, nil, 0, "", err + } + + errcall, err := fd.acceptOne(s, rawsa[:], o) + if err == nil { + return s, rawsa[:], uint32(o.rsan), "", nil + } + + // Sometimes we see WSAECONNRESET and ERROR_NETNAME_DELETED is + // returned here. These happen if connection reset is received + // before AcceptEx could complete. These errors relate to new + // connection, not to AcceptEx, so ignore broken connection and + // try AcceptEx again for more connections. + errno, ok := err.(syscall.Errno) + if !ok { + return syscall.InvalidHandle, nil, 0, errcall, err + } + switch errno { + case syscall.ERROR_NETNAME_DELETED, syscall.WSAECONNRESET: + // ignore these and try again + default: + return syscall.InvalidHandle, nil, 0, errcall, err + } + } +} + +func (fd *FD) Seek(offset int64, whence int) (ret int64, err error) { + if err := fd.incref(); err != nil { + return 0, err + } + defer fd.decref() + + fd.l.Lock() + defer fd.l.Unlock() + + return syscall.Seek(fd.Sysfd, offset, whence) +} + +// FindNextFile wraps syscall.FindNextFile. +func (fd *FD) FindNextFile(data *syscall.Win32finddata) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.FindNextFile(fd.Sysfd, data) +} + +// Fchdir wraps syscall.Fchdir. +func (fd *FD) Fchdir() error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Fchdir(fd.Sysfd) +} + +func (fd *FD) GetFileType() (uint32, error) { + if err := fd.incref(); err != nil { + return 0, err + } + defer fd.decref() + return syscall.GetFileType(fd.Sysfd) +} + +func (fd *FD) GetFileInformationByHandle(data *syscall.ByHandleFileInformation) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.GetFileInformationByHandle(fd.Sysfd, data) +} diff --git a/src/net/hook_cloexec.go b/src/internal/poll/hook_cloexec.go similarity index 58% rename from src/net/hook_cloexec.go rename to src/internal/poll/hook_cloexec.go index 870f0d78b128a..062c343abaf36 100644 --- a/src/net/hook_cloexec.go +++ b/src/internal/poll/hook_cloexec.go @@ -4,11 +4,9 @@ // +build freebsd linux -package net +package poll import "syscall" -var ( - // Placeholders for socket system calls. - accept4Func func(int, int) (int, syscall.Sockaddr, error) = syscall.Accept4 -) +// Accept4Func is used to hook the accept4 call. +var Accept4Func func(int, int) (int, syscall.Sockaddr, error) = syscall.Accept4 diff --git a/src/internal/poll/hook_unix.go b/src/internal/poll/hook_unix.go new file mode 100644 index 0000000000000..4a6ff6cd49e1b --- /dev/null +++ b/src/internal/poll/hook_unix.go @@ -0,0 +1,17 @@ +// Copyright 2017 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. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +package poll + +import ( + "syscall" +) + +// CloseFunc is used to hook the close call. +var CloseFunc func(int) error = syscall.Close + +// AcceptFunc is used to hook the accept call. +var AcceptFunc func(int) (int, syscall.Sockaddr, error) = syscall.Accept diff --git a/src/internal/poll/hook_windows.go b/src/internal/poll/hook_windows.go new file mode 100644 index 0000000000000..97665554e8c1a --- /dev/null +++ b/src/internal/poll/hook_windows.go @@ -0,0 +1,18 @@ +// Copyright 2017 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 poll + +import ( + "syscall" +) + +// CloseFunc is used to hook the close call. +var CloseFunc func(syscall.Handle) error = syscall.Closesocket + +// AcceptFunc is used to hook the accept call. +var AcceptFunc func(syscall.Handle, syscall.Handle, *byte, uint32, uint32, uint32, *uint32, *syscall.Overlapped) error = syscall.AcceptEx + +// ConnectExFunc is used to hook the ConnectEx call. +var ConnectExFunc func(syscall.Handle, syscall.Sockaddr, *byte, uint32, *uint32, *syscall.Overlapped) error = syscall.ConnectEx diff --git a/src/internal/poll/sendfile_bsd.go b/src/internal/poll/sendfile_bsd.go new file mode 100644 index 0000000000000..9f8b1a37847eb --- /dev/null +++ b/src/internal/poll/sendfile_bsd.go @@ -0,0 +1,51 @@ +// 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. + +// +build dragonfly freebsd + +package poll + +import "syscall" + +// maxSendfileSize is the largest chunk size we ask the kernel to copy +// at a time. +const maxSendfileSize int = 4 << 20 + +// SendFile wraps the sendfile system call. +func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error) { + if err := dstFD.writeLock(); err != nil { + return 0, err + } + defer dstFD.writeUnlock() + dst := int(dstFD.Sysfd) + for remain > 0 { + n := maxSendfileSize + if int64(n) > remain { + n = int(remain) + } + pos1 := pos + n, err1 := syscall.Sendfile(dst, src, &pos1, n) + if n > 0 { + pos += int64(n) + written += int64(n) + remain -= int64(n) + } + if n == 0 && err1 == nil { + break + } + if err1 == syscall.EAGAIN { + if err1 = dstFD.pd.waitWrite(); err1 == nil { + continue + } + } + if err1 != nil { + // This includes syscall.ENOSYS (no kernel + // support) and syscall.EINVAL (fd types which + // don't implement sendfile) + err = err1 + break + } + } + return written, err +} diff --git a/src/internal/poll/sendfile_linux.go b/src/internal/poll/sendfile_linux.go new file mode 100644 index 0000000000000..0bc752926aeac --- /dev/null +++ b/src/internal/poll/sendfile_linux.go @@ -0,0 +1,48 @@ +// 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 poll + +import "syscall" + +// maxSendfileSize is the largest chunk size we ask the kernel to copy +// at a time. +const maxSendfileSize int = 4 << 20 + +// SendFile wraps the sendfile system call. +func SendFile(dstFD *FD, src int, remain int64) (written int64, err error) { + if err := dstFD.writeLock(); err != nil { + return 0, err + } + defer dstFD.writeUnlock() + + dst := int(dstFD.Sysfd) + for remain > 0 { + n := maxSendfileSize + if int64(n) > remain { + n = int(remain) + } + n, err1 := syscall.Sendfile(dst, src, nil, n) + if n > 0 { + written += int64(n) + remain -= int64(n) + } + if n == 0 && err1 == nil { + break + } + if err1 == syscall.EAGAIN { + if err1 = dstFD.pd.waitWrite(); err1 == nil { + continue + } + } + if err1 != nil { + // This includes syscall.ENOSYS (no kernel + // support) and syscall.EINVAL (fd types which + // don't implement sendfile) + err = err1 + break + } + } + return written, err +} diff --git a/src/internal/poll/sendfile_solaris.go b/src/internal/poll/sendfile_solaris.go new file mode 100644 index 0000000000000..6d03ecc155a21 --- /dev/null +++ b/src/internal/poll/sendfile_solaris.go @@ -0,0 +1,64 @@ +// Copyright 2015 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 poll + +import "syscall" + +// Not strictly needed, but very helpful for debugging, see issue #10221. +//go:cgo_import_dynamic _ _ "libsendfile.so" +//go:cgo_import_dynamic _ _ "libsocket.so" + +// maxSendfileSize is the largest chunk size we ask the kernel to copy +// at a time. +const maxSendfileSize int = 4 << 20 + +// SendFile wraps the sendfile system call. +func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error) { + if err := dstFD.writeLock(); err != nil { + return 0, err + } + defer dstFD.writeUnlock() + + dst := int(dstFD.Sysfd) + for remain > 0 { + n := maxSendfileSize + if int64(n) > remain { + n = int(remain) + } + pos1 := pos + n, err1 := syscall.Sendfile(dst, src, &pos1, n) + if err1 == syscall.EAGAIN || err1 == syscall.EINTR { + // partial write may have occurred + if n = int(pos1 - pos); n == 0 { + // nothing more to write + err1 = nil + } + } + if n > 0 { + pos += int64(n) + written += int64(n) + remain -= int64(n) + } + if n == 0 && err1 == nil { + break + } + if err1 == syscall.EAGAIN { + if err1 = dstFD.pd.waitWrite(); err1 == nil { + continue + } + } + if err1 == syscall.EINTR { + continue + } + if err1 != nil { + // This includes syscall.ENOSYS (no kernel + // support) and syscall.EINVAL (fd types which + // don't implement sendfile) + err = err1 + break + } + } + return written, err +} diff --git a/src/internal/poll/sendfile_windows.go b/src/internal/poll/sendfile_windows.go new file mode 100644 index 0000000000000..762165a2bf8bc --- /dev/null +++ b/src/internal/poll/sendfile_windows.go @@ -0,0 +1,23 @@ +// 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 poll + +import "syscall" + +// SendFile wraps the TransmitFile call. +func SendFile(fd *FD, src syscall.Handle, n int64) (int64, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + + o := &fd.wop + o.qty = uint32(n) + o.handle = src + done, err := wsrv.ExecIO(o, "TransmitFile", func(o *operation) error { + return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) + }) + return int64(done), err +} diff --git a/src/internal/poll/sock_cloexec.go b/src/internal/poll/sock_cloexec.go new file mode 100644 index 0000000000000..28b950c33043b --- /dev/null +++ b/src/internal/poll/sock_cloexec.go @@ -0,0 +1,52 @@ +// Copyright 2013 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. + +// This file implements sysSocket and accept for platforms that +// provide a fast path for setting SetNonblock and CloseOnExec. + +// +build freebsd linux + +package poll + +import ( + "syscall" +) + +// Wrapper around the accept system call that marks the returned file +// descriptor as nonblocking and close-on-exec. +func accept(s int) (int, syscall.Sockaddr, string, error) { + ns, sa, err := Accept4Func(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC) + // On Linux the accept4 system call was introduced in 2.6.28 + // kernel and on FreeBSD it was introduced in 10 kernel. If we + // get an ENOSYS error on both Linux and FreeBSD, or EINVAL + // error on Linux, fall back to using accept. + switch err { + case nil: + return ns, sa, "", nil + default: // errors other than the ones listed + return -1, sa, "accept4", err + case syscall.ENOSYS: // syscall missing + case syscall.EINVAL: // some Linux use this instead of ENOSYS + case syscall.EACCES: // some Linux use this instead of ENOSYS + case syscall.EFAULT: // some Linux use this instead of ENOSYS + } + + // See ../syscall/exec_unix.go for description of ForkLock. + // It is probably okay to hold the lock across syscall.Accept + // because we have put fd.sysfd into non-blocking mode. + // However, a call to the File method will put it back into + // blocking mode. We can't take that risk, so no use of ForkLock here. + ns, sa, err = AcceptFunc(s) + if err == nil { + syscall.CloseOnExec(ns) + } + if err != nil { + return -1, nil, "accept", err + } + if err = syscall.SetNonblock(ns, true); err != nil { + CloseFunc(ns) + return -1, nil, "setnonblock", err + } + return ns, sa, "", nil +} diff --git a/src/internal/poll/sockopt.go b/src/internal/poll/sockopt.go new file mode 100644 index 0000000000000..b841699a9cba2 --- /dev/null +++ b/src/internal/poll/sockopt.go @@ -0,0 +1,38 @@ +// Copyright 2009 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. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows + +package poll + +import ( + "syscall" +) + +// SetsockoptInt wraps the setsockopt network call with an int argument. +func (fd *FD) SetsockoptInt(level, name, arg int) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptInt(fd.Sysfd, level, name, arg) +} + +// SetsockoptInet4Addr wraps the setsockopt network call with an IPv4 address. +func (fd *FD) SetsockoptInet4Addr(level, name int, arg [4]byte) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptInet4Addr(fd.Sysfd, level, name, arg) +} + +// SetsockoptLinger wraps the setsockopt network call with a Linger argument. +func (fd *FD) SetsockoptLinger(level, name int, l *syscall.Linger) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptLinger(fd.Sysfd, level, name, l) +} diff --git a/src/internal/poll/sockopt_linux.go b/src/internal/poll/sockopt_linux.go new file mode 100644 index 0000000000000..ba616db855642 --- /dev/null +++ b/src/internal/poll/sockopt_linux.go @@ -0,0 +1,18 @@ +// 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 poll + +import ( + "syscall" +) + +// SetsockoptIPMreqn wraps the setsockopt network call with a IPMreqn argument. +func (fd *FD) SetsockoptIPMreqn(level, name int, mreq *syscall.IPMreqn) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptIPMreqn(fd.Sysfd, level, name, mreq) +} diff --git a/src/internal/poll/sockopt_unix.go b/src/internal/poll/sockopt_unix.go new file mode 100644 index 0000000000000..b33644db762c3 --- /dev/null +++ b/src/internal/poll/sockopt_unix.go @@ -0,0 +1,18 @@ +// Copyright 2017 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. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package poll + +import "syscall" + +// SetsockoptByte wraps the setsockopt network call with a byte argument. +func (fd *FD) SetsockoptByte(level, name int, arg byte) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptByte(fd.Sysfd, level, name, arg) +} diff --git a/src/internal/poll/sockopt_windows.go b/src/internal/poll/sockopt_windows.go new file mode 100644 index 0000000000000..70501a0965298 --- /dev/null +++ b/src/internal/poll/sockopt_windows.go @@ -0,0 +1,27 @@ +// Copyright 2009 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 poll + +import ( + "syscall" +) + +// Setsockopt wraps the Windows setsockopt network call. +func (fd *FD) Setsockopt(level, optname int32, optval *byte, optlen int32) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.Setsockopt(fd.Sysfd, level, optname, optval, optlen) +} + +// WSAIoctl wraps the Windows WSAIoctl call. +func (fd *FD) WSAIoctl(iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *syscall.Overlapped, completionRoutine uintptr) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.WSAIoctl(fd.Sysfd, iocc, inbuf, cbif, outbuf, cbob, cbbr, overlapped, completionRoutine) +} diff --git a/src/internal/poll/sockoptip.go b/src/internal/poll/sockoptip.go new file mode 100644 index 0000000000000..28900eb7e3045 --- /dev/null +++ b/src/internal/poll/sockoptip.go @@ -0,0 +1,27 @@ +// 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. + +// +build darwin dragonfly freebsd linux netbsd openbsd windows + +package poll + +import "syscall" + +// SetsockoptIPMreq wraps the setsockopt network call with a IPMreq argument. +func (fd *FD) SetsockoptIPMreq(level, name int, mreq *syscall.IPMreq) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptIPMreq(fd.Sysfd, level, name, mreq) +} + +// SetsockoptIPv6Mreq wraps the setsockopt network call with a IPv6Mreq argument. +func (fd *FD) SetsockoptIPv6Mreq(level, name int, mreq *syscall.IPv6Mreq) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return syscall.SetsockoptIPv6Mreq(fd.Sysfd, level, name, mreq) +} diff --git a/src/internal/poll/str.go b/src/internal/poll/str.go new file mode 100644 index 0000000000000..2be35c74db5a6 --- /dev/null +++ b/src/internal/poll/str.go @@ -0,0 +1,39 @@ +// Copyright 2009 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. + +// Simple conversions to avoid depending on strconv. + +package poll + +// Convert integer to decimal string +func itoa(val int) string { + if val < 0 { + return "-" + uitoa(uint(-val)) + } + return uitoa(uint(val)) +} + +// Convert unsigned integer to decimal string +func uitoa(val uint) string { + if val == 0 { // avoid string allocation + return "0" + } + var buf [20]byte // big enough for 64bit value base 10 + i := len(buf) - 1 + for val >= 10 { + q := val / 10 + buf[i] = byte('0' + val - q*10) + i-- + val = q + } + // val < 10 + buf[i] = byte('0' + val) + return string(buf[i:]) +} + +// stringsHasSuffix is strings.HasSuffix. It reports whether s ends in +// suffix. +func stringsHasSuffix(s, suffix string) bool { + return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix +} diff --git a/src/internal/poll/sys_cloexec.go b/src/internal/poll/sys_cloexec.go new file mode 100644 index 0000000000000..d0012459e7d05 --- /dev/null +++ b/src/internal/poll/sys_cloexec.go @@ -0,0 +1,36 @@ +// Copyright 2013 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. + +// This file implements sysSocket and accept for platforms that do not +// provide a fast path for setting SetNonblock and CloseOnExec. + +// +build darwin dragonfly nacl netbsd openbsd solaris + +package poll + +import ( + "syscall" +) + +// Wrapper around the accept system call that marks the returned file +// descriptor as nonblocking and close-on-exec. +func accept(s int) (int, syscall.Sockaddr, string, error) { + // See ../syscall/exec_unix.go for description of ForkLock. + // It is probably okay to hold the lock across syscall.Accept + // because we have put fd.sysfd into non-blocking mode. + // However, a call to the File method will put it back into + // blocking mode. We can't take that risk, so no use of ForkLock here. + ns, sa, err := AcceptFunc(s) + if err == nil { + syscall.CloseOnExec(ns) + } + if err != nil { + return -1, nil, "accept", err + } + if err = syscall.SetNonblock(ns, true); err != nil { + CloseFunc(ns) + return -1, nil, "setnonblock", err + } + return ns, sa, "", nil +} diff --git a/src/internal/poll/writev.go b/src/internal/poll/writev.go new file mode 100644 index 0000000000000..fcee388513d9c --- /dev/null +++ b/src/internal/poll/writev.go @@ -0,0 +1,81 @@ +// Copyright 2016 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. + +// +build darwin dragonfly freebsd linux netbsd openbsd + +package poll + +import ( + "io" + "syscall" + "unsafe" +) + +// Writev wraps the writev system call. +func (fd *FD) Writev(v *[][]byte) (n int64, err error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(); err != nil { + return 0, err + } + + var iovecs []syscall.Iovec + if fd.iovecs != nil { + iovecs = *fd.iovecs + } + // TODO: read from sysconf(_SC_IOV_MAX)? The Linux default is + // 1024 and this seems conservative enough for now. Darwin's + // UIO_MAXIOV also seems to be 1024. + maxVec := 1024 + + for len(*v) > 0 { + iovecs = iovecs[:0] + for _, chunk := range *v { + if len(chunk) == 0 { + continue + } + iovecs = append(iovecs, syscall.Iovec{Base: &chunk[0]}) + if fd.IsStream && len(chunk) > 1<<30 { + iovecs[len(iovecs)-1].SetLen(1 << 30) + break // continue chunk on next writev + } + iovecs[len(iovecs)-1].SetLen(len(chunk)) + if len(iovecs) == maxVec { + break + } + } + if len(iovecs) == 0 { + break + } + fd.iovecs = &iovecs // cache + + wrote, _, e0 := syscall.Syscall(syscall.SYS_WRITEV, + uintptr(fd.Sysfd), + uintptr(unsafe.Pointer(&iovecs[0])), + uintptr(len(iovecs))) + if wrote == ^uintptr(0) { + wrote = 0 + } + TestHookDidWritev(int(wrote)) + n += int64(wrote) + consume(v, int64(wrote)) + if e0 == syscall.EAGAIN { + if err = fd.pd.waitWrite(); err == nil { + continue + } + } else if e0 != 0 { + err = syscall.Errno(e0) + } + if err != nil { + break + } + if n == 0 { + err = io.ErrUnexpectedEOF + break + } + } + return n, err +} diff --git a/src/internal/poll/writev_test.go b/src/internal/poll/writev_test.go new file mode 100644 index 0000000000000..b46657ce96023 --- /dev/null +++ b/src/internal/poll/writev_test.go @@ -0,0 +1,62 @@ +// Copyright 2016 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 poll_test + +import ( + "internal/poll" + "reflect" + "testing" +) + +func TestConsume(t *testing.T) { + tests := []struct { + in [][]byte + consume int64 + want [][]byte + }{ + { + in: [][]byte{[]byte("foo"), []byte("bar")}, + consume: 0, + want: [][]byte{[]byte("foo"), []byte("bar")}, + }, + { + in: [][]byte{[]byte("foo"), []byte("bar")}, + consume: 2, + want: [][]byte{[]byte("o"), []byte("bar")}, + }, + { + in: [][]byte{[]byte("foo"), []byte("bar")}, + consume: 3, + want: [][]byte{[]byte("bar")}, + }, + { + in: [][]byte{[]byte("foo"), []byte("bar")}, + consume: 4, + want: [][]byte{[]byte("ar")}, + }, + { + in: [][]byte{nil, nil, nil, []byte("bar")}, + consume: 1, + want: [][]byte{[]byte("ar")}, + }, + { + in: [][]byte{nil, nil, nil, []byte("foo")}, + consume: 0, + want: [][]byte{[]byte("foo")}, + }, + { + in: [][]byte{nil, nil, nil}, + consume: 0, + want: [][]byte{}, + }, + } + for i, tt := range tests { + in := tt.in + poll.Consume(&in, tt.consume) + if !reflect.DeepEqual(in, tt.want) { + t.Errorf("%d. after consume(%d) = %+v, want %+v", i, tt.consume, in, tt.want) + } + } +} diff --git a/src/net/dial.go b/src/net/dial.go index 50bba5a49e426..0a7da408fe526 100644 --- a/src/net/dial.go +++ b/src/net/dial.go @@ -7,6 +7,7 @@ package net import ( "context" "internal/nettrace" + "internal/poll" "time" ) @@ -110,7 +111,7 @@ func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, er } timeRemaining := deadline.Sub(now) if timeRemaining <= 0 { - return time.Time{}, errTimeout + return time.Time{}, poll.ErrTimeout } // Tentatively allocate equal time to each remaining address. timeout := timeRemaining / time.Duration(addrsRemaining) diff --git a/src/net/dial_test.go b/src/net/dial_test.go index 9919d72ce3b79..9825bc92abc69 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -7,6 +7,7 @@ package net import ( "bufio" "context" + "internal/poll" "internal/testenv" "io" "net/internal/socktest" @@ -94,7 +95,7 @@ func TestDialTimeoutFDLeak(t *testing.T) { default: sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) { time.Sleep(2 * T) - return nil, errTimeout + return nil, poll.ErrTimeout }) defer sw.Set(socktest.FilterConnect, nil) } @@ -585,8 +586,8 @@ func TestDialerPartialDeadline(t *testing.T) { {now, noDeadline, 1, noDeadline, nil}, // Step the clock forward and cross the deadline. {now.Add(-1 * time.Millisecond), now, 1, now, nil}, - {now.Add(0 * time.Millisecond), now, 1, noDeadline, errTimeout}, - {now.Add(1 * time.Millisecond), now, 1, noDeadline, errTimeout}, + {now.Add(0 * time.Millisecond), now, 1, noDeadline, poll.ErrTimeout}, + {now.Add(1 * time.Millisecond), now, 1, noDeadline, poll.ErrTimeout}, } for i, tt := range testCases { deadline, err := partialDeadline(tt.now, tt.deadline, tt.addrs) diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go index 85267bbddc02a..4464804c701e4 100644 --- a/src/net/dnsclient_unix_test.go +++ b/src/net/dnsclient_unix_test.go @@ -9,6 +9,7 @@ package net import ( "context" "fmt" + "internal/poll" "internal/testenv" "io/ioutil" "os" @@ -767,7 +768,7 @@ func TestRetryTimeout(t *testing.T) { if s == "192.0.2.1:53" { deadline0 = deadline time.Sleep(10 * time.Millisecond) - return nil, errTimeout + return nil, poll.ErrTimeout } if deadline == deadline0 { diff --git a/src/net/error_posix.go b/src/net/error_posix.go new file mode 100644 index 0000000000000..dd9754c841aa5 --- /dev/null +++ b/src/net/error_posix.go @@ -0,0 +1,21 @@ +// Copyright 2017 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. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows + +package net + +import ( + "os" + "syscall" +) + +// wrapSyscallError takes an error and a syscall name. If the error is +// a syscall.Errno, it wraps it in a os.SyscallError using the syscall name. +func wrapSyscallError(name string, err error) error { + if _, ok := err.(syscall.Errno); ok { + err = os.NewSyscallError(name, err) + } + return err +} diff --git a/src/net/error_test.go b/src/net/error_test.go index c23da49fad912..61abfae5f0eee 100644 --- a/src/net/error_test.go +++ b/src/net/error_test.go @@ -7,6 +7,7 @@ package net import ( "context" "fmt" + "internal/poll" "io" "io/ioutil" "net/internal/socktest" @@ -87,7 +88,7 @@ second: return nil } switch err := nestedErr.(type) { - case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *timeoutError, UnknownNetworkError: + case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *poll.TimeoutError, UnknownNetworkError: return nil case *os.SyscallError: nestedErr = err.Err @@ -97,7 +98,7 @@ second: goto third } switch nestedErr { - case errCanceled, errClosing, errMissingAddress, errNoSuitableAddress, + case errCanceled, poll.ErrClosing, errMissingAddress, errNoSuitableAddress, context.DeadlineExceeded, context.Canceled: return nil } @@ -432,7 +433,7 @@ second: goto third } switch nestedErr { - case errClosing, errTimeout: + case poll.ErrClosing, poll.ErrTimeout: return nil } return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) @@ -467,14 +468,14 @@ second: return nil } switch err := nestedErr.(type) { - case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *timeoutError, UnknownNetworkError: + case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *poll.TimeoutError, UnknownNetworkError: return nil case *os.SyscallError: nestedErr = err.Err goto third } switch nestedErr { - case errCanceled, errClosing, errMissingAddress, errTimeout, ErrWriteToConnected, io.ErrUnexpectedEOF: + case errCanceled, poll.ErrClosing, errMissingAddress, poll.ErrTimeout, ErrWriteToConnected, io.ErrUnexpectedEOF: return nil } return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) @@ -517,7 +518,7 @@ second: goto third } switch nestedErr { - case errClosing: + case poll.ErrClosing: return nil } return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) @@ -613,7 +614,7 @@ second: goto third } switch nestedErr { - case errClosing, errTimeout: + case poll.ErrClosing, poll.ErrTimeout: return nil } return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) @@ -692,7 +693,7 @@ second: goto third } switch nestedErr { - case errClosing: + case poll.ErrClosing: return nil } return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) diff --git a/src/net/fd_plan9.go b/src/net/fd_plan9.go index 300d8c4543e90..7496e36ca7a0f 100644 --- a/src/net/fd_plan9.go +++ b/src/net/fd_plan9.go @@ -5,23 +5,15 @@ package net import ( + "internal/poll" "io" "os" - "sync/atomic" "syscall" - "time" ) -type atomicBool int32 - -func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } -func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } -func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } - // Network file descriptor. type netFD struct { - // locking/lifetime of sysfd + serialize access to Read and Write methods - fdmu fdMutex + pfd poll.FD // immutable until Close net string @@ -30,14 +22,6 @@ type netFD struct { listen, ctl, data *os.File laddr, raddr Addr isStream bool - - // deadlines - raio *asyncIO - waio *asyncIO - rtimer *time.Timer - wtimer *time.Timer - rtimedout atomicBool // set true when read deadline has been reached - wtimedout atomicBool // set true when write deadline has been reached } var ( @@ -49,7 +33,7 @@ func sysInit() { } func newFD(net, name string, listen, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) { - return &netFD{ + ret := &netFD{ net: net, n: name, dir: netdir + "/" + net + "/" + name, @@ -57,7 +41,9 @@ func newFD(net, name string, listen, ctl, data *os.File, laddr, raddr Addr) (*ne ctl: ctl, data: data, laddr: laddr, raddr: raddr, - }, nil + } + ret.pfd.Destroy = ret.destroy + return ret, nil } func (fd *netFD) init() error { @@ -99,28 +85,10 @@ func (fd *netFD) destroy() { } func (fd *netFD) Read(b []byte) (n int, err error) { - if fd.rtimedout.isSet() { - return 0, errTimeout - } if !fd.ok() || fd.data == nil { return 0, syscall.EINVAL } - if err := fd.readLock(); err != nil { - return 0, err - } - defer fd.readUnlock() - if len(b) == 0 { - return 0, nil - } - fd.raio = newAsyncIO(fd.data.Read, b) - n, err = fd.raio.Wait() - fd.raio = nil - if isHangup(err) { - err = io.EOF - } - if isInterrupted(err) { - err = errTimeout - } + n, err = fd.pfd.Read(fd.data.Read, b) if fd.net == "udp" && err == io.EOF { n = 0 err = nil @@ -129,23 +97,10 @@ func (fd *netFD) Read(b []byte) (n int, err error) { } func (fd *netFD) Write(b []byte) (n int, err error) { - if fd.wtimedout.isSet() { - return 0, errTimeout - } if !fd.ok() || fd.data == nil { return 0, syscall.EINVAL } - if err := fd.writeLock(); err != nil { - return 0, err - } - defer fd.writeUnlock() - fd.waio = newAsyncIO(fd.data.Write, b) - n, err = fd.waio.Wait() - fd.waio = nil - if isInterrupted(err) { - err = errTimeout - } - return + return fd.pfd.Write(fd.data.Write, b) } func (fd *netFD) closeRead() error { @@ -163,8 +118,8 @@ func (fd *netFD) closeWrite() error { } func (fd *netFD) Close() error { - if !fd.fdmu.increfAndClose() { - return errClosing + if err := fd.pfd.Close(); err != nil { + return err } if !fd.ok() { return syscall.EINVAL @@ -216,77 +171,6 @@ func (fd *netFD) file(f *os.File, s string) (*os.File, error) { return os.NewFile(uintptr(dfd), s), nil } -func (fd *netFD) setDeadline(t time.Time) error { - return setDeadlineImpl(fd, t, 'r'+'w') -} - -func (fd *netFD) setReadDeadline(t time.Time) error { - return setDeadlineImpl(fd, t, 'r') -} - -func (fd *netFD) setWriteDeadline(t time.Time) error { - return setDeadlineImpl(fd, t, 'w') -} - -func setDeadlineImpl(fd *netFD, t time.Time, mode int) error { - d := t.Sub(time.Now()) - if mode == 'r' || mode == 'r'+'w' { - fd.rtimedout.setFalse() - } - if mode == 'w' || mode == 'r'+'w' { - fd.wtimedout.setFalse() - } - if t.IsZero() || d < 0 { - // Stop timer - if mode == 'r' || mode == 'r'+'w' { - if fd.rtimer != nil { - fd.rtimer.Stop() - } - fd.rtimer = nil - } - if mode == 'w' || mode == 'r'+'w' { - if fd.wtimer != nil { - fd.wtimer.Stop() - } - fd.wtimer = nil - } - } else { - // Interrupt I/O operation once timer has expired - if mode == 'r' || mode == 'r'+'w' { - fd.rtimer = time.AfterFunc(d, func() { - fd.rtimedout.setTrue() - if fd.raio != nil { - fd.raio.Cancel() - } - }) - } - if mode == 'w' || mode == 'r'+'w' { - fd.wtimer = time.AfterFunc(d, func() { - fd.wtimedout.setTrue() - if fd.waio != nil { - fd.waio.Cancel() - } - }) - } - } - if !t.IsZero() && d < 0 { - // Interrupt current I/O operation - if mode == 'r' || mode == 'r'+'w' { - fd.rtimedout.setTrue() - if fd.raio != nil { - fd.raio.Cancel() - } - } - if mode == 'w' || mode == 'r'+'w' { - fd.wtimedout.setTrue() - if fd.waio != nil { - fd.waio.Cancel() - } - } - } - return nil -} - func setReadBuffer(fd *netFD, bytes int) error { return syscall.EPLAN9 } @@ -294,11 +178,3 @@ func setReadBuffer(fd *netFD, bytes int) error { func setWriteBuffer(fd *netFD, bytes int) error { return syscall.EPLAN9 } - -func isHangup(err error) bool { - return err != nil && stringsHasSuffix(err.Error(), "Hangup") -} - -func isInterrupted(err error) bool { - return err != nil && stringsHasSuffix(err.Error(), "interrupted") -} diff --git a/src/net/fd_posix.go b/src/net/fd_posix.go deleted file mode 100644 index b4b908abacf21..0000000000000 --- a/src/net/fd_posix.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2009 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. - -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows - -package net - -import ( - "io" - "syscall" -) - -// eofError returns io.EOF when fd is available for reading end of -// file. -func (fd *netFD) eofError(n int, err error) error { - if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM && fd.sotype != syscall.SOCK_RAW { - return io.EOF - } - return err -} diff --git a/src/net/fd_posix_test.go b/src/net/fd_posix_test.go deleted file mode 100644 index 85711ef1b70df..0000000000000 --- a/src/net/fd_posix_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// 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. - -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows - -package net - -import ( - "io" - "syscall" - "testing" -) - -var eofErrorTests = []struct { - n int - err error - fd *netFD - expected error -}{ - {100, nil, &netFD{sotype: syscall.SOCK_STREAM}, nil}, - {100, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF}, - {100, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing}, - {0, nil, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF}, - {0, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF}, - {0, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing}, - - {100, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil}, - {100, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF}, - {100, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing}, - {0, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil}, - {0, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF}, - {0, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing}, - - {100, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, nil}, - {100, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF}, - {100, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing}, - {0, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF}, - {0, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF}, - {0, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing}, - - {100, nil, &netFD{sotype: syscall.SOCK_RAW}, nil}, - {100, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF}, - {100, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing}, - {0, nil, &netFD{sotype: syscall.SOCK_RAW}, nil}, - {0, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF}, - {0, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing}, -} - -func TestEOFError(t *testing.T) { - for _, tt := range eofErrorTests { - actual := tt.fd.eofError(tt.n, tt.err) - if actual != tt.expected { - t.Errorf("eofError(%v, %v, %v): expected %v, actual %v", tt.n, tt.err, tt.fd.sotype, tt.expected, actual) - } - } -} diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go index 3c95fc01d401d..9f36069bf367b 100644 --- a/src/net/fd_unix.go +++ b/src/net/fd_unix.go @@ -8,7 +8,7 @@ package net import ( "context" - "io" + "internal/poll" "os" "runtime" "sync/atomic" @@ -17,38 +17,36 @@ import ( // Network file descriptor. type netFD struct { - // locking/lifetime of sysfd + serialize access to Read and Write methods - fdmu fdMutex + pfd poll.FD // immutable until Close - sysfd int family int sotype int - isStream bool isConnected bool net string laddr Addr raddr Addr - - // writev cache. - iovecs *[]syscall.Iovec - - // wait server - pd pollDesc } func sysInit() { } func newFD(sysfd, family, sotype int, net string) (*netFD, error) { - return &netFD{sysfd: sysfd, family: family, sotype: sotype, net: net, isStream: sotype == syscall.SOCK_STREAM}, nil + ret := &netFD{ + pfd: poll.FD{ + Sysfd: sysfd, + IsStream: sotype == syscall.SOCK_STREAM, + ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW, + }, + family: family, + sotype: sotype, + net: net, + } + return ret, nil } func (fd *netFD) init() error { - if err := fd.pd.init(fd); err != nil { - return err - } - return nil + return fd.pfd.Init() } func (fd *netFD) setAddr(laddr, raddr Addr) { @@ -72,7 +70,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro // Do not need to call fd.writeLock here, // because fd is not yet accessible to user, // so no concurrent operations are possible. - switch err := connectFunc(fd.sysfd, ra); err { + switch err := connectFunc(fd.pfd.Sysfd, ra); err { case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR: case nil, syscall.EISCONN: select { @@ -80,9 +78,10 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro return mapErr(ctx.Err()) default: } - if err := fd.init(); err != nil { + if err := fd.pfd.Init(); err != nil { return err } + runtime.KeepAlive(fd) return nil case syscall.EINVAL: // On Solaris we can see EINVAL if the socket has @@ -97,12 +96,12 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro default: return os.NewSyscallError("connect", err) } - if err := fd.init(); err != nil { + if err := fd.pfd.Init(); err != nil { return err } if deadline, _ := ctx.Deadline(); !deadline.IsZero() { - fd.setWriteDeadline(deadline) - defer fd.setWriteDeadline(noDeadline) + fd.pfd.SetWriteDeadline(deadline) + defer fd.pfd.SetWriteDeadline(noDeadline) } // Start the "interrupter" goroutine, if this context might be canceled. @@ -119,7 +118,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro defer func() { close(done) if ctxErr := <-interruptRes; ctxErr != nil && ret == nil { - // The interrupter goroutine called setWriteDeadline, + // The interrupter goroutine called SetWriteDeadline, // but the connect code below had returned from // waitWrite already and did a successful connect (ret // == nil). Because we've now poisoned the connection @@ -135,7 +134,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro // Force the runtime's poller to immediately give up // waiting for writability, unblocking waitWrite // below. - fd.setWriteDeadline(aLongTimeAgo) + fd.pfd.SetWriteDeadline(aLongTimeAgo) testHookCanceledDial() interruptRes <- ctx.Err() case <-done: @@ -153,7 +152,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro // SO_ERROR socket option to see if the connection // succeeded or failed. See issue 7474 for further // details. - if err := fd.pd.waitWrite(); err != nil { + if err := fd.pfd.WaitWrite(); err != nil { select { case <-ctx.Done(): return mapErr(ctx.Err()) @@ -161,7 +160,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro } return err } - nerr, err := getsockoptIntFunc(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) + nerr, err := getsockoptIntFunc(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) if err != nil { return os.NewSyscallError("getsockopt", err) } @@ -174,45 +173,26 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro // See golang.org/issue/14548. // On Darwin, multiple connect system calls on // a non-blocking socket never harm SO_ERROR. - switch err := connectFunc(fd.sysfd, ra); err { + switch err := connectFunc(fd.pfd.Sysfd, ra); err { case nil, syscall.EISCONN: return nil } default: return os.NewSyscallError("getsockopt", err) } + runtime.KeepAlive(fd) } } -func (fd *netFD) destroy() { - // Poller may want to unregister fd in readiness notification mechanism, - // so this must be executed before closeFunc. - fd.pd.close() - closeFunc(fd.sysfd) - fd.sysfd = -1 - runtime.SetFinalizer(fd, nil) -} - func (fd *netFD) Close() error { - if !fd.fdmu.increfAndClose() { - return errClosing - } - // Unblock any I/O. Once it all unblocks and returns, - // so that it cannot be referring to fd.sysfd anymore, - // the final decref will close fd.sysfd. This should happen - // fairly quickly, since all the I/O is non-blocking, and any - // attempts to block in the pollDesc will return errClosing. - fd.pd.evict() - fd.decref() - return nil + runtime.SetFinalizer(fd, nil) + return fd.pfd.Close() } func (fd *netFD) shutdown(how int) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("shutdown", syscall.Shutdown(fd.sysfd, how)) + err := fd.pfd.Shutdown(how) + runtime.KeepAlive(fd) + return wrapSyscallError("shutdown", err) } func (fd *netFD) closeRead() error { @@ -224,233 +204,59 @@ func (fd *netFD) closeWrite() error { } func (fd *netFD) Read(p []byte) (n int, err error) { - if err := fd.readLock(); err != nil { - return 0, err - } - defer fd.readUnlock() - if len(p) == 0 { - // If the caller wanted a zero byte read, return immediately - // without trying. (But after acquiring the readLock.) Otherwise - // syscall.Read returns 0, nil and eofError turns that into - // io.EOF. - // TODO(bradfitz): make it wait for readability? (Issue 15735) - return 0, nil - } - if err := fd.pd.prepareRead(); err != nil { - return 0, err - } - if fd.isStream && len(p) > 1<<30 { - p = p[:1<<30] - } - for { - n, err = syscall.Read(fd.sysfd, p) - if err != nil { - n = 0 - if err == syscall.EAGAIN { - if err = fd.pd.waitRead(); err == nil { - continue - } - } - } - err = fd.eofError(n, err) - break - } - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("read", err) - } - return + n, err = fd.pfd.Read(p) + runtime.KeepAlive(fd) + return n, wrapSyscallError("read", err) } func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { - if err := fd.readLock(); err != nil { - return 0, nil, err - } - defer fd.readUnlock() - if err := fd.pd.prepareRead(); err != nil { - return 0, nil, err - } - for { - n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0) - if err != nil { - n = 0 - if err == syscall.EAGAIN { - if err = fd.pd.waitRead(); err == nil { - continue - } - } - } - err = fd.eofError(n, err) - break - } - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("recvfrom", err) - } - return + n, sa, err = fd.pfd.RecvFrom(p) + runtime.KeepAlive(fd) + return n, sa, wrapSyscallError("recvfrom", err) } func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) { - if err := fd.readLock(); err != nil { - return 0, 0, 0, nil, err - } - defer fd.readUnlock() - if err := fd.pd.prepareRead(); err != nil { - return 0, 0, 0, nil, err - } - for { - n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0) - if err != nil { - // TODO(dfc) should n and oobn be set to 0 - if err == syscall.EAGAIN { - if err = fd.pd.waitRead(); err == nil { - continue - } - } - } - err = fd.eofError(n, err) - break - } - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("recvmsg", err) - } - return + n, oobn, flags, sa, err = fd.pfd.ReadMsg(p, oob) + runtime.KeepAlive(fd) + return n, oobn, flags, sa, wrapSyscallError("recvmsg", err) } func (fd *netFD) Write(p []byte) (nn int, err error) { - if err := fd.writeLock(); err != nil { - return 0, err - } - defer fd.writeUnlock() - if err := fd.pd.prepareWrite(); err != nil { - return 0, err - } - for { - var n int - max := len(p) - if fd.isStream && max-nn > 1<<30 { - max = nn + 1<<30 - } - n, err = syscall.Write(fd.sysfd, p[nn:max]) - if n > 0 { - nn += n - } - if nn == len(p) { - break - } - if err == syscall.EAGAIN { - if err = fd.pd.waitWrite(); err == nil { - continue - } - } - if err != nil { - break - } - if n == 0 { - err = io.ErrUnexpectedEOF - break - } - } - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("write", err) - } - return nn, err + nn, err = fd.pfd.Write(p) + runtime.KeepAlive(fd) + return nn, wrapSyscallError("write", err) } func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) { - if err := fd.writeLock(); err != nil { - return 0, err - } - defer fd.writeUnlock() - if err := fd.pd.prepareWrite(); err != nil { - return 0, err - } - for { - err = syscall.Sendto(fd.sysfd, p, 0, sa) - if err == syscall.EAGAIN { - if err = fd.pd.waitWrite(); err == nil { - continue - } - } - break - } - if err == nil { - n = len(p) - } - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("sendto", err) - } - return + n, err = fd.pfd.WriteTo(p, sa) + runtime.KeepAlive(fd) + return n, wrapSyscallError("sendto", err) } func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { - if err := fd.writeLock(); err != nil { - return 0, 0, err - } - defer fd.writeUnlock() - if err := fd.pd.prepareWrite(); err != nil { - return 0, 0, err - } - for { - n, err = syscall.SendmsgN(fd.sysfd, p, oob, sa, 0) - if err == syscall.EAGAIN { - if err = fd.pd.waitWrite(); err == nil { - continue - } - } - break - } - if err == nil { - oobn = len(oob) - } - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("sendmsg", err) - } - return + n, oobn, err = fd.pfd.WriteMsg(p, oob, sa) + runtime.KeepAlive(fd) + return n, oobn, wrapSyscallError("sendmsg", err) } func (fd *netFD) accept() (netfd *netFD, err error) { - if err := fd.readLock(); err != nil { - return nil, err - } - defer fd.readUnlock() - - var s int - var rsa syscall.Sockaddr - if err = fd.pd.prepareRead(); err != nil { - return nil, err - } - for { - s, rsa, err = accept(fd.sysfd) - if err != nil { - nerr, ok := err.(*os.SyscallError) - if !ok { - return nil, err - } - switch nerr.Err { - case syscall.EAGAIN: - if err = fd.pd.waitRead(); err == nil { - continue - } - case syscall.ECONNABORTED: - // This means that a socket on the - // listen queue was closed before we - // Accept()ed it; it's a silly error, - // so try again. - continue - } - return nil, err + d, rsa, errcall, err := fd.pfd.Accept() + if err != nil { + if errcall != "" { + err = wrapSyscallError(errcall, err) } - break + return nil, err } - if netfd, err = newFD(s, fd.family, fd.sotype, fd.net); err != nil { - closeFunc(s) + if netfd, err = newFD(d, fd.family, fd.sotype, fd.net); err != nil { + poll.CloseFunc(d) return nil, err } if err = netfd.init(); err != nil { fd.Close() return nil, err } - lsa, _ := syscall.Getsockname(netfd.sysfd) + lsa, _ := syscall.Getsockname(netfd.pfd.Sysfd) netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa)) return netfd, nil } @@ -503,7 +309,7 @@ func dupCloseOnExecOld(fd int) (newfd int, err error) { } func (fd *netFD) dup() (f *os.File, err error) { - ns, err := dupCloseOnExec(fd.sysfd) + ns, err := dupCloseOnExec(fd.pfd.Sysfd) if err != nil { return nil, err } diff --git a/src/net/fd_windows.go b/src/net/fd_windows.go index a976f2ac7f9d3..2182b730f9143 100644 --- a/src/net/fd_windows.go +++ b/src/net/fd_windows.go @@ -6,62 +6,14 @@ package net import ( "context" - "internal/race" + "internal/poll" "os" "runtime" - "sync" "syscall" "unsafe" ) -var ( - initErr error - ioSync uint64 -) - -// CancelIo Windows API cancels all outstanding IO for a particular -// socket on current thread. To overcome that limitation, we run -// special goroutine, locked to OS single thread, that both starts -// and cancels IO. It means, there are 2 unavoidable thread switches -// for every IO. -// Some newer versions of Windows has new CancelIoEx API, that does -// not have that limitation and can be used from any thread. This -// package uses CancelIoEx API, if present, otherwise it fallback -// to CancelIo. - -var ( - canCancelIO bool // determines if CancelIoEx API is present - skipSyncNotif bool - hasLoadSetFileCompletionNotificationModes bool -) - func sysInit() { - var d syscall.WSAData - e := syscall.WSAStartup(uint32(0x202), &d) - if e != nil { - initErr = os.NewSyscallError("wsastartup", e) - } - canCancelIO = syscall.LoadCancelIoEx() == nil - hasLoadSetFileCompletionNotificationModes = syscall.LoadSetFileCompletionNotificationModes() == nil - if hasLoadSetFileCompletionNotificationModes { - // It's not safe to use FILE_SKIP_COMPLETION_PORT_ON_SUCCESS if non IFS providers are installed: - // http://support.microsoft.com/kb/2568167 - skipSyncNotif = true - protos := [2]int32{syscall.IPPROTO_TCP, 0} - var buf [32]syscall.WSAProtocolInfo - len := uint32(unsafe.Sizeof(buf)) - n, err := syscall.WSAEnumProtocols(&protos[0], &buf[0], &len) - if err != nil { - skipSyncNotif = false - } else { - for i := int32(0); i < n; i++ { - if buf[i].ServiceFlags1&syscall.XP1_IFS_HANDLES == 0 { - skipSyncNotif = false - break - } - } - } - } } // canUseConnectEx reports whether we can use the ConnectEx Windows API call @@ -75,257 +27,39 @@ func canUseConnectEx(net string) bool { return false } -// operation contains superset of data necessary to perform all async IO. -type operation struct { - // Used by IOCP interface, it must be first field - // of the struct, as our code rely on it. - o syscall.Overlapped - - // fields used by runtime.netpoll - runtimeCtx uintptr - mode int32 - errno int32 - qty uint32 - - // fields used only by net package - fd *netFD - errc chan error - buf syscall.WSABuf - sa syscall.Sockaddr - rsa *syscall.RawSockaddrAny - rsan int32 - handle syscall.Handle - flags uint32 - bufs []syscall.WSABuf -} - -func (o *operation) InitBuf(buf []byte) { - o.buf.Len = uint32(len(buf)) - o.buf.Buf = nil - if len(buf) != 0 { - o.buf.Buf = &buf[0] - } -} - -func (o *operation) InitBufs(buf *Buffers) { - if o.bufs == nil { - o.bufs = make([]syscall.WSABuf, 0, len(*buf)) - } else { - o.bufs = o.bufs[:0] - } - for _, b := range *buf { - var p *byte - if len(b) > 0 { - p = &b[0] - } - o.bufs = append(o.bufs, syscall.WSABuf{Len: uint32(len(b)), Buf: p}) - } -} - -// ClearBufs clears all pointers to Buffers parameter captured -// by InitBufs, so it can be released by garbage collector. -func (o *operation) ClearBufs() { - for i := range o.bufs { - o.bufs[i].Buf = nil - } - o.bufs = o.bufs[:0] -} - -// ioSrv executes net IO requests. -type ioSrv struct { - req chan ioSrvReq -} - -type ioSrvReq struct { - o *operation - submit func(o *operation) error // if nil, cancel the operation -} - -// ProcessRemoteIO will execute submit IO requests on behalf -// of other goroutines, all on a single os thread, so it can -// cancel them later. Results of all operations will be sent -// back to their requesters via channel supplied in request. -// It is used only when the CancelIoEx API is unavailable. -func (s *ioSrv) ProcessRemoteIO() { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - for r := range s.req { - if r.submit != nil { - r.o.errc <- r.submit(r.o) - } else { - r.o.errc <- syscall.CancelIo(r.o.fd.sysfd) - } - } -} - -// ExecIO executes a single IO operation o. It submits and cancels -// IO in the current thread for systems where Windows CancelIoEx API -// is available. Alternatively, it passes the request onto -// runtime netpoll and waits for completion or cancels request. -func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) error) (int, error) { - fd := o.fd - // Notify runtime netpoll about starting IO. - err := fd.pd.prepare(int(o.mode)) - if err != nil { - return 0, err - } - // Start IO. - if canCancelIO { - err = submit(o) - } else { - // Send request to a special dedicated thread, - // so it can stop the IO with CancelIO later. - s.req <- ioSrvReq{o, submit} - err = <-o.errc - } - switch err { - case nil: - // IO completed immediately - if o.fd.skipSyncNotif { - // No completion message will follow, so return immediately. - return int(o.qty), nil - } - // Need to get our completion message anyway. - case syscall.ERROR_IO_PENDING: - // IO started, and we have to wait for its completion. - err = nil - default: - return 0, err - } - // Wait for our request to complete. - err = fd.pd.wait(int(o.mode)) - if err == nil { - // All is good. Extract our IO results and return. - if o.errno != 0 { - err = syscall.Errno(o.errno) - return 0, err - } - return int(o.qty), nil - } - // IO is interrupted by "close" or "timeout" - netpollErr := err - switch netpollErr { - case errClosing, errTimeout: - // will deal with those. - default: - panic("net: unexpected runtime.netpoll error: " + netpollErr.Error()) - } - // Cancel our request. - if canCancelIO { - err := syscall.CancelIoEx(fd.sysfd, &o.o) - // Assuming ERROR_NOT_FOUND is returned, if IO is completed. - if err != nil && err != syscall.ERROR_NOT_FOUND { - // TODO(brainman): maybe do something else, but panic. - panic(err) - } - } else { - s.req <- ioSrvReq{o, nil} - <-o.errc - } - // Wait for cancelation to complete. - fd.pd.waitCanceled(int(o.mode)) - if o.errno != 0 { - err = syscall.Errno(o.errno) - if err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled - err = netpollErr - } - return 0, err - } - // We issued a cancelation request. But, it seems, IO operation succeeded - // before the cancelation request run. We need to treat the IO operation as - // succeeded (the bytes are actually sent/recv from network). - return int(o.qty), nil -} - -// Start helper goroutines. -var rsrv, wsrv *ioSrv -var onceStartServer sync.Once - -func startServer() { - rsrv = new(ioSrv) - wsrv = new(ioSrv) - if !canCancelIO { - // Only CancelIo API is available. Lets start two special goroutines - // locked to an OS thread, that both starts and cancels IO. One will - // process read requests, while other will do writes. - rsrv.req = make(chan ioSrvReq) - go rsrv.ProcessRemoteIO() - wsrv.req = make(chan ioSrvReq) - go wsrv.ProcessRemoteIO() - } -} - // Network file descriptor. type netFD struct { - // locking/lifetime of sysfd + serialize access to Read and Write methods - fdmu fdMutex + pfd poll.FD // immutable until Close - sysfd syscall.Handle - family int - sotype int - isStream bool - isConnected bool - skipSyncNotif bool - net string - laddr Addr - raddr Addr - - rop operation // read operation - wop operation // write operation - - // wait server - pd pollDesc + family int + sotype int + isConnected bool + net string + laddr Addr + raddr Addr } func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error) { - if initErr != nil { - return nil, initErr + ret := &netFD{ + pfd: poll.FD{ + Sysfd: sysfd, + IsStream: sotype == syscall.SOCK_STREAM, + ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW, + }, + family: family, + sotype: sotype, + net: net, } - onceStartServer.Do(startServer) - return &netFD{sysfd: sysfd, family: family, sotype: sotype, net: net, isStream: sotype == syscall.SOCK_STREAM}, nil + return ret, nil } func (fd *netFD) init() error { - if err := fd.pd.init(fd); err != nil { - return err - } - if hasLoadSetFileCompletionNotificationModes { - // We do not use events, so we can skip them always. - flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE) - // It's not safe to skip completion notifications for UDP: - // http://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx - if skipSyncNotif && fd.net == "tcp" { - flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS - } - err := syscall.SetFileCompletionNotificationModes(fd.sysfd, flags) - if err == nil && flags&syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS != 0 { - fd.skipSyncNotif = true - } - } - // Disable SIO_UDP_CONNRESET behavior. - // http://support.microsoft.com/kb/263823 - switch fd.net { - case "udp", "udp4", "udp6": - ret := uint32(0) - flag := uint32(0) - size := uint32(unsafe.Sizeof(flag)) - err := syscall.WSAIoctl(fd.sysfd, syscall.SIO_UDP_CONNRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0) - if err != nil { - return os.NewSyscallError("wsaioctl", err) - } - } - fd.rop.mode = 'r' - fd.wop.mode = 'w' - fd.rop.fd = fd - fd.wop.fd = fd - fd.rop.runtimeCtx = fd.pd.runtimeCtx - fd.wop.runtimeCtx = fd.pd.runtimeCtx - if !canCancelIO { - fd.rop.errc = make(chan error) - fd.wop.errc = make(chan error) + errcall, err := fd.pfd.Init(fd.net) + if errcall != "" { + err = wrapSyscallError(errcall, err) } - return nil + return err } func (fd *netFD) setAddr(laddr, raddr Addr) { @@ -342,11 +76,11 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) error { return err } if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() { - fd.setWriteDeadline(deadline) - defer fd.setWriteDeadline(noDeadline) + fd.pfd.SetWriteDeadline(deadline) + defer fd.pfd.SetWriteDeadline(noDeadline) } if !canUseConnectEx(fd.net) { - err := connectFunc(fd.sysfd, ra) + err := connectFunc(fd.pfd.Sysfd, ra) return os.NewSyscallError("connect", err) } // ConnectEx windows API requires an unconnected, previously bound socket. @@ -359,13 +93,10 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) error { default: panic("unexpected type in connect") } - if err := syscall.Bind(fd.sysfd, la); err != nil { + if err := syscall.Bind(fd.pfd.Sysfd, la); err != nil { return os.NewSyscallError("bind", err) } } - // Call ConnectEx API. - o := &fd.wop - o.sa = ra // Wait for the goroutine converting context.Done into a write timeout // to exist, otherwise our caller might cancel the context and @@ -377,16 +108,14 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) error { case <-ctx.Done(): // Force the runtime's poller to immediately give // up waiting for writability. - fd.setWriteDeadline(aLongTimeAgo) + fd.pfd.SetWriteDeadline(aLongTimeAgo) <-done case <-done: } }() - _, err := wsrv.ExecIO(o, "ConnectEx", func(o *operation) error { - return connectExFunc(o.fd.sysfd, o.sa, nil, 0, nil, &o.o) - }) - if err != nil { + // Call ConnectEx API. + if err := fd.pfd.ConnectEx(ra); err != nil { select { case <-ctx.Done(): return mapErr(ctx.Err()) @@ -398,38 +127,18 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) error { } } // Refresh socket properties. - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))) -} - -func (fd *netFD) destroy() { - if fd.sysfd == syscall.InvalidHandle { - return - } - // Poller may want to unregister fd in readiness notification mechanism, - // so this must be executed before closeFunc. - fd.pd.close() - closeFunc(fd.sysfd) - fd.sysfd = syscall.InvalidHandle - // no need for a finalizer anymore - runtime.SetFinalizer(fd, nil) + return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.pfd.Sysfd)), int32(unsafe.Sizeof(fd.pfd.Sysfd)))) } func (fd *netFD) Close() error { - if !fd.fdmu.increfAndClose() { - return errClosing - } - // unblock pending reader and writer - fd.pd.evict() - fd.decref() - return nil + runtime.SetFinalizer(fd, nil) + return fd.pfd.Close() } func (fd *netFD) shutdown(how int) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return syscall.Shutdown(fd.sysfd, how) + err := fd.pfd.Shutdown(how) + runtime.KeepAlive(fd) + return err } func (fd *netFD) closeRead() error { @@ -441,72 +150,21 @@ func (fd *netFD) closeWrite() error { } func (fd *netFD) Read(buf []byte) (int, error) { - if err := fd.readLock(); err != nil { - return 0, err - } - defer fd.readUnlock() - o := &fd.rop - o.InitBuf(buf) - n, err := rsrv.ExecIO(o, "WSARecv", func(o *operation) error { - return syscall.WSARecv(o.fd.sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil) - }) - if race.Enabled { - race.Acquire(unsafe.Pointer(&ioSync)) - } - if len(buf) != 0 { - err = fd.eofError(n, err) - } - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("wsarecv", err) - } - return n, err + n, err := fd.pfd.Read(buf) + runtime.KeepAlive(fd) + return n, wrapSyscallError("wsarecv", err) } func (fd *netFD) readFrom(buf []byte) (int, syscall.Sockaddr, error) { - if len(buf) == 0 { - return 0, nil, nil - } - if err := fd.readLock(); err != nil { - return 0, nil, err - } - defer fd.readUnlock() - o := &fd.rop - o.InitBuf(buf) - n, err := rsrv.ExecIO(o, "WSARecvFrom", func(o *operation) error { - if o.rsa == nil { - o.rsa = new(syscall.RawSockaddrAny) - } - o.rsan = int32(unsafe.Sizeof(*o.rsa)) - return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil) - }) - err = fd.eofError(n, err) - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("wsarecvfrom", err) - } - if err != nil { - return n, nil, err - } - sa, _ := o.rsa.Sockaddr() - return n, sa, nil + n, sa, err := fd.pfd.RecvFrom(buf) + runtime.KeepAlive(fd) + return n, sa, wrapSyscallError("wsarecvfrom", err) } func (fd *netFD) Write(buf []byte) (int, error) { - if err := fd.writeLock(); err != nil { - return 0, err - } - defer fd.writeUnlock() - if race.Enabled { - race.ReleaseMerge(unsafe.Pointer(&ioSync)) - } - o := &fd.wop - o.InitBuf(buf) - n, err := wsrv.ExecIO(o, "WSASend", func(o *operation) error { - return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &o.qty, 0, &o.o, nil) - }) - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("wsasend", err) - } - return n, err + n, err := fd.pfd.Write(buf) + runtime.KeepAlive(fd) + return n, wrapSyscallError("wsasend", err) } func (c *conn) writeBuffers(v *Buffers) (int64, error) { @@ -521,61 +179,33 @@ func (c *conn) writeBuffers(v *Buffers) (int64, error) { } func (fd *netFD) writeBuffers(buf *Buffers) (int64, error) { - if len(*buf) == 0 { - return 0, nil - } - if err := fd.writeLock(); err != nil { - return 0, err - } - defer fd.writeUnlock() - if race.Enabled { - race.ReleaseMerge(unsafe.Pointer(&ioSync)) - } - o := &fd.wop - o.InitBufs(buf) - n, err := wsrv.ExecIO(o, "WSASend", func(o *operation) error { - return syscall.WSASend(o.fd.sysfd, &o.bufs[0], uint32(len(*buf)), &o.qty, 0, &o.o, nil) - }) - o.ClearBufs() - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("wsasend", err) - } - testHookDidWritev(n) - buf.consume(int64(n)) - return int64(n), err + n, err := fd.pfd.Writev((*[][]byte)(buf)) + runtime.KeepAlive(fd) + return n, wrapSyscallError("wsasend", err) } func (fd *netFD) writeTo(buf []byte, sa syscall.Sockaddr) (int, error) { - if len(buf) == 0 { - return 0, nil - } - if err := fd.writeLock(); err != nil { - return 0, err - } - defer fd.writeUnlock() - o := &fd.wop - o.InitBuf(buf) - o.sa = sa - n, err := wsrv.ExecIO(o, "WSASendto", func(o *operation) error { - return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil) - }) - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("wsasendto", err) - } - return n, err + n, err := fd.pfd.WriteTo(buf, sa) + runtime.KeepAlive(fd) + return n, wrapSyscallError("wsasendto", err) } -func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD, error) { - // Get new socket. - s, err := sysSocket(fd.family, fd.sotype, 0) +func (fd *netFD) accept() (*netFD, error) { + s, rawsa, rsan, errcall, err := fd.pfd.Accept(func() (syscall.Handle, error) { + return sysSocket(fd.family, fd.sotype, 0) + }) + if err != nil { + if errcall != "" { + err = wrapSyscallError(errcall, err) + } return nil, err } // Associate our new socket with IOCP. netfd, err := newFD(s, fd.family, fd.sotype, fd.net) if err != nil { - closeFunc(s) + poll.CloseFunc(s) return nil, err } if err := netfd.init(); err != nil { @@ -583,71 +213,11 @@ func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD return nil, err } - // Submit accept request. - o.handle = s - o.rsan = int32(unsafe.Sizeof(rawsa[0])) - _, err = rsrv.ExecIO(o, "AcceptEx", func(o *operation) error { - return acceptFunc(o.fd.sysfd, o.handle, (*byte)(unsafe.Pointer(&rawsa[0])), 0, uint32(o.rsan), uint32(o.rsan), &o.qty, &o.o) - }) - if err != nil { - netfd.Close() - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("acceptex", err) - } - return nil, err - } - - // Inherit properties of the listening socket. - err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))) - if err != nil { - netfd.Close() - return nil, os.NewSyscallError("setsockopt", err) - } - runtime.KeepAlive(fd) - return netfd, nil -} - -func (fd *netFD) accept() (*netFD, error) { - if err := fd.readLock(); err != nil { - return nil, err - } - defer fd.readUnlock() - - o := &fd.rop - var netfd *netFD - var err error - var rawsa [2]syscall.RawSockaddrAny - for { - netfd, err = fd.acceptOne(rawsa[:], o) - if err == nil { - break - } - // Sometimes we see WSAECONNRESET and ERROR_NETNAME_DELETED is - // returned here. These happen if connection reset is received - // before AcceptEx could complete. These errors relate to new - // connection, not to AcceptEx, so ignore broken connection and - // try AcceptEx again for more connections. - nerr, ok := err.(*os.SyscallError) - if !ok { - return nil, err - } - errno, ok := nerr.Err.(syscall.Errno) - if !ok { - return nil, err - } - switch errno { - case syscall.ERROR_NETNAME_DELETED, syscall.WSAECONNRESET: - // ignore these and try again - default: - return nil, err - } - } - // Get local and peer addr out of AcceptEx buffer. var lrsa, rrsa *syscall.RawSockaddrAny var llen, rlen int32 syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])), - 0, uint32(o.rsan), uint32(o.rsan), &lrsa, &llen, &rrsa, &rlen) + 0, rsan, rsan, &lrsa, &llen, &rrsa, &rlen) lsa, _ := lrsa.Sockaddr() rsa, _ := rrsa.Sockaddr() diff --git a/src/net/file_unix.go b/src/net/file_unix.go index 9e581fcb419ad..d67dff8e0569d 100644 --- a/src/net/file_unix.go +++ b/src/net/file_unix.go @@ -7,6 +7,7 @@ package net import ( + "internal/poll" "os" "syscall" ) @@ -17,7 +18,7 @@ func dupSocket(f *os.File) (int, error) { return -1, err } if err := syscall.SetNonblock(s, true); err != nil { - closeFunc(s) + poll.CloseFunc(s) return -1, os.NewSyscallError("setnonblock", err) } return s, nil @@ -31,7 +32,7 @@ func newFileFD(f *os.File) (*netFD, error) { family := syscall.AF_UNSPEC sotype, err := syscall.GetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_TYPE) if err != nil { - closeFunc(s) + poll.CloseFunc(s) return nil, os.NewSyscallError("getsockopt", err) } lsa, _ := syscall.Getsockname(s) @@ -44,12 +45,12 @@ func newFileFD(f *os.File) (*netFD, error) { case *syscall.SockaddrUnix: family = syscall.AF_UNIX default: - closeFunc(s) + poll.CloseFunc(s) return nil, syscall.EPROTONOSUPPORT } fd, err := newFD(s, family, sotype, "") if err != nil { - closeFunc(s) + poll.CloseFunc(s) return nil, err } laddr := fd.addrFunc()(lsa) diff --git a/src/net/hook_unix.go b/src/net/hook_unix.go index cf52567fcfdad..fee62a972f8c7 100644 --- a/src/net/hook_unix.go +++ b/src/net/hook_unix.go @@ -13,10 +13,8 @@ var ( testHookCanceledDial = func() {} // for golang.org/issue/16523 // Placeholders for socket system calls. - socketFunc func(int, int, int) (int, error) = syscall.Socket - closeFunc func(int) error = syscall.Close - connectFunc func(int, syscall.Sockaddr) error = syscall.Connect - listenFunc func(int, int) error = syscall.Listen - acceptFunc func(int) (int, syscall.Sockaddr, error) = syscall.Accept - getsockoptIntFunc func(int, int, int) (int, error) = syscall.GetsockoptInt + socketFunc func(int, int, int) (int, error) = syscall.Socket + connectFunc func(int, syscall.Sockaddr) error = syscall.Connect + listenFunc func(int, int) error = syscall.Listen + getsockoptIntFunc func(int, int, int) (int, error) = syscall.GetsockoptInt ) diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go index 63ea35ab8c444..4e64dcef517e6 100644 --- a/src/net/hook_windows.go +++ b/src/net/hook_windows.go @@ -13,10 +13,7 @@ var ( testHookDialChannel = func() { time.Sleep(time.Millisecond) } // see golang.org/issue/5349 // Placeholders for socket system calls. - socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket - closeFunc func(syscall.Handle) error = syscall.Closesocket - connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect - connectExFunc func(syscall.Handle, syscall.Sockaddr, *byte, uint32, *uint32, *syscall.Overlapped) error = syscall.ConnectEx - listenFunc func(syscall.Handle, int) error = syscall.Listen - acceptFunc func(syscall.Handle, syscall.Handle, *byte, uint32, uint32, uint32, *uint32, *syscall.Overlapped) error = syscall.AcceptEx + socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket + connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect + listenFunc func(syscall.Handle, int) error = syscall.Listen ) diff --git a/src/net/ipsock_plan9.go b/src/net/ipsock_plan9.go index b7fd344c8ad4a..1cd8fa23ffc45 100644 --- a/src/net/ipsock_plan9.go +++ b/src/net/ipsock_plan9.go @@ -249,10 +249,10 @@ func (fd *netFD) netFD() (*netFD, error) { func (fd *netFD) acceptPlan9() (nfd *netFD, err error) { defer func() { fixErr(err) }() - if err := fd.readLock(); err != nil { + if err := fd.pfd.ReadLock(); err != nil { return nil, err } - defer fd.readUnlock() + defer fd.pfd.ReadUnlock() listen, err := os.Open(fd.dir + "/listen") if err != nil { return nil, err diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go index ff280c3e4e84e..5cb85f8c15c39 100644 --- a/src/net/ipsock_posix.go +++ b/src/net/ipsock_posix.go @@ -8,6 +8,7 @@ package net import ( "context" + "internal/poll" "runtime" "syscall" ) @@ -18,7 +19,7 @@ func probeIPv4Stack() bool { case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT: return false case nil: - closeFunc(s) + poll.CloseFunc(s) } return true } @@ -68,7 +69,7 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { if err != nil { continue } - defer closeFunc(s) + defer poll.CloseFunc(s) syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value) sa, err := probes[i].laddr.sockaddr(syscall.AF_INET6) if err != nil { diff --git a/src/net/main_cloexec_test.go b/src/net/main_cloexec_test.go index 7903819585995..ade71a94906dd 100644 --- a/src/net/main_cloexec_test.go +++ b/src/net/main_cloexec_test.go @@ -6,6 +6,8 @@ package net +import "internal/poll" + func init() { extraTestHookInstallers = append(extraTestHookInstallers, installAccept4TestHook) extraTestHookUninstallers = append(extraTestHookUninstallers, uninstallAccept4TestHook) @@ -13,13 +15,13 @@ func init() { var ( // Placeholders for saving original socket system calls. - origAccept4 = accept4Func + origAccept4 = poll.Accept4Func ) func installAccept4TestHook() { - accept4Func = sw.Accept4 + poll.Accept4Func = sw.Accept4 } func uninstallAccept4TestHook() { - accept4Func = origAccept4 + poll.Accept4Func = origAccept4 } diff --git a/src/net/main_unix_test.go b/src/net/main_unix_test.go index 0cc129f34ddae..9cfbc8efc406a 100644 --- a/src/net/main_unix_test.go +++ b/src/net/main_unix_test.go @@ -6,13 +6,15 @@ package net +import "internal/poll" + var ( // Placeholders for saving original socket system calls. origSocket = socketFunc - origClose = closeFunc + origClose = poll.CloseFunc origConnect = connectFunc origListen = listenFunc - origAccept = acceptFunc + origAccept = poll.AcceptFunc origGetsockoptInt = getsockoptIntFunc extraTestHookInstallers []func() @@ -21,10 +23,10 @@ var ( func installTestHooks() { socketFunc = sw.Socket - closeFunc = sw.Close + poll.CloseFunc = sw.Close connectFunc = sw.Connect listenFunc = sw.Listen - acceptFunc = sw.Accept + poll.AcceptFunc = sw.Accept getsockoptIntFunc = sw.GetsockoptInt for _, fn := range extraTestHookInstallers { @@ -34,10 +36,10 @@ func installTestHooks() { func uninstallTestHooks() { socketFunc = origSocket - closeFunc = origClose + poll.CloseFunc = origClose connectFunc = origConnect listenFunc = origListen - acceptFunc = origAccept + poll.AcceptFunc = origAccept getsockoptIntFunc = origGetsockoptInt for _, fn := range extraTestHookUninstallers { @@ -48,6 +50,6 @@ func uninstallTestHooks() { // forceCloseSockets must be called only from TestMain. func forceCloseSockets() { for s := range sw.Sockets() { - closeFunc(s) + poll.CloseFunc(s) } } diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go index 6ea318c2a5f3a..f38a3a0d66817 100644 --- a/src/net/main_windows_test.go +++ b/src/net/main_windows_test.go @@ -4,37 +4,39 @@ package net +import "internal/poll" + var ( // Placeholders for saving original socket system calls. origSocket = socketFunc - origClosesocket = closeFunc + origClosesocket = poll.CloseFunc origConnect = connectFunc - origConnectEx = connectExFunc + origConnectEx = poll.ConnectExFunc origListen = listenFunc - origAccept = acceptFunc + origAccept = poll.AcceptFunc ) func installTestHooks() { socketFunc = sw.Socket - closeFunc = sw.Closesocket + poll.CloseFunc = sw.Closesocket connectFunc = sw.Connect - connectExFunc = sw.ConnectEx + poll.ConnectExFunc = sw.ConnectEx listenFunc = sw.Listen - acceptFunc = sw.AcceptEx + poll.AcceptFunc = sw.AcceptEx } func uninstallTestHooks() { socketFunc = origSocket - closeFunc = origClosesocket + poll.CloseFunc = origClosesocket connectFunc = origConnect - connectExFunc = origConnectEx + poll.ConnectExFunc = origConnectEx listenFunc = origListen - acceptFunc = origAccept + poll.AcceptFunc = origAccept } // forceCloseSockets must be called only from TestMain. func forceCloseSockets() { for s := range sw.Sockets() { - closeFunc(s) + poll.CloseFunc(s) } } diff --git a/src/net/net.go b/src/net/net.go index 81206ea1cb6c8..9c27f1baf91cc 100644 --- a/src/net/net.go +++ b/src/net/net.go @@ -81,6 +81,7 @@ package net import ( "context" "errors" + "internal/poll" "io" "os" "syscall" @@ -234,7 +235,7 @@ func (c *conn) SetDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } - if err := c.fd.setDeadline(t); err != nil { + if err := c.fd.pfd.SetDeadline(t); err != nil { return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err} } return nil @@ -245,7 +246,7 @@ func (c *conn) SetReadDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } - if err := c.fd.setReadDeadline(t); err != nil { + if err := c.fd.pfd.SetReadDeadline(t); err != nil { return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err} } return nil @@ -256,7 +257,7 @@ func (c *conn) SetWriteDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } - if err := c.fd.setWriteDeadline(t); err != nil { + if err := c.fd.pfd.SetWriteDeadline(t); err != nil { return &OpError{Op: "set", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err} } return nil @@ -391,10 +392,8 @@ var ( errMissingAddress = errors.New("missing address") // For both read and write operations. - errTimeout error = &timeoutError{} - errCanceled = errors.New("operation was canceled") - errClosing = errors.New("use of closed network connection") - ErrWriteToConnected = errors.New("use of WriteTo with pre-connected connection") + errCanceled = errors.New("operation was canceled") + ErrWriteToConnected = errors.New("use of WriteTo with pre-connected connection") ) // mapErr maps from the context errors to the historical internal net @@ -407,7 +406,7 @@ func mapErr(err error) error { case context.Canceled: return errCanceled case context.DeadlineExceeded: - return errTimeout + return poll.ErrTimeout default: return err } @@ -502,12 +501,6 @@ func (e *OpError) Temporary() bool { return ok && t.Temporary() } -type timeoutError struct{} - -func (e *timeoutError) Error() string { return "i/o timeout" } -func (e *timeoutError) Timeout() bool { return true } -func (e *timeoutError) Temporary() bool { return true } - // A ParseError is the error type of literal network address parsers. type ParseError struct { // Type is the type of string that was expected, such as @@ -632,8 +625,6 @@ type buffersWriter interface { writeBuffers(*Buffers) (int64, error) } -var testHookDidWritev = func(wrote int) {} - // Buffers contains zero or more runs of bytes to write. // // On certain machines, for certain types of connections, this is diff --git a/src/net/sendfile_bsd.go b/src/net/sendfile_bsd.go index 67e80c9c6a775..7a2b48c6cfa46 100644 --- a/src/net/sendfile_bsd.go +++ b/src/net/sendfile_bsd.go @@ -7,15 +7,11 @@ package net import ( + "internal/poll" "io" "os" - "syscall" ) -// maxSendfileSize is the largest chunk size we ask the kernel to copy -// at a time. -const maxSendfileSize int = 4 << 20 - // sendFile copies the contents of r to c using the sendfile // system call to minimize copies. // @@ -62,49 +58,10 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { return 0, err, false } - if err := c.writeLock(); err != nil { - return 0, err, true - } - defer c.writeUnlock() + written, err = poll.SendFile(&c.pfd, int(f.Fd()), pos, remain) - dst := c.sysfd - src := int(f.Fd()) - for remain > 0 { - n := maxSendfileSize - if int64(n) > remain { - n = int(remain) - } - pos1 := pos - n, err1 := syscall.Sendfile(dst, src, &pos1, n) - if n > 0 { - pos += int64(n) - written += int64(n) - remain -= int64(n) - } - if n == 0 && err1 == nil { - break - } - if err1 == syscall.EAGAIN { - if err1 = c.pd.waitWrite(); err1 == nil { - continue - } - } - if err1 == syscall.EINTR { - continue - } - if err1 != nil { - // This includes syscall.ENOSYS (no kernel - // support) and syscall.EINVAL (fd types which - // don't implement sendfile) - err = err1 - break - } - } if lr != nil { - lr.N = remain - } - if err != nil { - err = os.NewSyscallError("sendfile", err) + lr.N = remain - written } - return written, err, written > 0 + return written, wrapSyscallError("sendfile", err), written > 0 } diff --git a/src/net/sendfile_linux.go b/src/net/sendfile_linux.go index 7e741f979411d..c537ea68b2b41 100644 --- a/src/net/sendfile_linux.go +++ b/src/net/sendfile_linux.go @@ -5,15 +5,11 @@ package net import ( + "internal/poll" "io" "os" - "syscall" ) -// maxSendfileSize is the largest chunk size we ask the kernel to copy -// at a time. -const maxSendfileSize int = 4 << 20 - // sendFile copies the contents of r to c using the sendfile // system call to minimize copies. // @@ -36,44 +32,10 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { return 0, nil, false } - if err := c.writeLock(); err != nil { - return 0, err, true - } - defer c.writeUnlock() + written, err = poll.SendFile(&c.pfd, int(f.Fd()), remain) - dst := c.sysfd - src := int(f.Fd()) - for remain > 0 { - n := maxSendfileSize - if int64(n) > remain { - n = int(remain) - } - n, err1 := syscall.Sendfile(dst, src, nil, n) - if n > 0 { - written += int64(n) - remain -= int64(n) - } - if n == 0 && err1 == nil { - break - } - if err1 == syscall.EAGAIN { - if err1 = c.pd.waitWrite(); err1 == nil { - continue - } - } - if err1 != nil { - // This includes syscall.ENOSYS (no kernel - // support) and syscall.EINVAL (fd types which - // don't implement sendfile) - err = err1 - break - } - } if lr != nil { - lr.N = remain - } - if err != nil { - err = os.NewSyscallError("sendfile", err) + lr.N = remain - written } - return written, err, written > 0 + return written, wrapSyscallError("sendfile", err), written > 0 } diff --git a/src/net/sendfile_solaris.go b/src/net/sendfile_solaris.go index add70c3147e65..63ca9d47b8ae2 100644 --- a/src/net/sendfile_solaris.go +++ b/src/net/sendfile_solaris.go @@ -5,19 +5,11 @@ package net import ( + "internal/poll" "io" "os" - "syscall" ) -// Not strictly needed, but very helpful for debugging, see issue #10221. -//go:cgo_import_dynamic _ _ "libsendfile.so" -//go:cgo_import_dynamic _ _ "libsocket.so" - -// maxSendfileSize is the largest chunk size we ask the kernel to copy -// at a time. -const maxSendfileSize int = 4 << 20 - // sendFile copies the contents of r to c using the sendfile // system call to minimize copies. // @@ -62,56 +54,10 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { return 0, err, false } - if err := c.writeLock(); err != nil { - return 0, err, true - } - defer c.writeUnlock() + written, err = poll.SendFile(&c.pfd, int(f.Fd()), pos, remain) - dst := c.sysfd - src := int(f.Fd()) - for remain > 0 { - n := maxSendfileSize - if int64(n) > remain { - n = int(remain) - } - pos1 := pos - n, err1 := syscall.Sendfile(dst, src, &pos1, n) - if err1 == syscall.EAGAIN || err1 == syscall.EINTR { - // partial write may have occurred - if n = int(pos1 - pos); n == 0 { - // nothing more to write - err1 = nil - } - } - if n > 0 { - pos += int64(n) - written += int64(n) - remain -= int64(n) - } - if n == 0 && err1 == nil { - break - } - if err1 == syscall.EAGAIN { - if err1 = c.pd.waitWrite(); err1 == nil { - continue - } - } - if err1 == syscall.EINTR { - continue - } - if err1 != nil { - // This includes syscall.ENOSYS (no kernel - // support) and syscall.EINVAL (fd types which - // don't implement sendfile) - err = err1 - break - } - } if lr != nil { - lr.N = remain - } - if err != nil { - err = os.NewSyscallError("sendfile", err) + lr.N = remain - written } - return written, err, written > 0 + return written, wrapSyscallError("sendfile", err), written > 0 } diff --git a/src/net/sendfile_windows.go b/src/net/sendfile_windows.go index bc0b7fb5b2508..bccd8b149f738 100644 --- a/src/net/sendfile_windows.go +++ b/src/net/sendfile_windows.go @@ -5,6 +5,7 @@ package net import ( + "internal/poll" "io" "os" "syscall" @@ -34,19 +35,10 @@ func sendFile(fd *netFD, r io.Reader) (written int64, err error, handled bool) { return 0, nil, false } - if err := fd.writeLock(); err != nil { - return 0, err, true - } - defer fd.writeUnlock() + done, err := poll.SendFile(&fd.pfd, syscall.Handle(f.Fd()), n) - o := &fd.wop - o.qty = uint32(n) - o.handle = syscall.Handle(f.Fd()) - done, err := wsrv.ExecIO(o, "TransmitFile", func(o *operation) error { - return syscall.TransmitFile(o.fd.sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) - }) if err != nil { - return 0, os.NewSyscallError("transmitfile", err), false + return 0, wrapSyscallError("transmitfile", err), false } if lr != nil { lr.N -= int64(done) diff --git a/src/net/sock_cloexec.go b/src/net/sock_cloexec.go index 616a101eacb50..3f5be2d62c60a 100644 --- a/src/net/sock_cloexec.go +++ b/src/net/sock_cloexec.go @@ -10,6 +10,7 @@ package net import ( + "internal/poll" "os" "syscall" ) @@ -42,46 +43,8 @@ func sysSocket(family, sotype, proto int) (int, error) { return -1, os.NewSyscallError("socket", err) } if err = syscall.SetNonblock(s, true); err != nil { - closeFunc(s) + poll.CloseFunc(s) return -1, os.NewSyscallError("setnonblock", err) } return s, nil } - -// Wrapper around the accept system call that marks the returned file -// descriptor as nonblocking and close-on-exec. -func accept(s int) (int, syscall.Sockaddr, error) { - ns, sa, err := accept4Func(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC) - // On Linux the accept4 system call was introduced in 2.6.28 - // kernel and on FreeBSD it was introduced in 10 kernel. If we - // get an ENOSYS error on both Linux and FreeBSD, or EINVAL - // error on Linux, fall back to using accept. - switch err { - case nil: - return ns, sa, nil - default: // errors other than the ones listed - return -1, sa, os.NewSyscallError("accept4", err) - case syscall.ENOSYS: // syscall missing - case syscall.EINVAL: // some Linux use this instead of ENOSYS - case syscall.EACCES: // some Linux use this instead of ENOSYS - case syscall.EFAULT: // some Linux use this instead of ENOSYS - } - - // See ../syscall/exec_unix.go for description of ForkLock. - // It is probably okay to hold the lock across syscall.Accept - // because we have put fd.sysfd into non-blocking mode. - // However, a call to the File method will put it back into - // blocking mode. We can't take that risk, so no use of ForkLock here. - ns, sa, err = acceptFunc(s) - if err == nil { - syscall.CloseOnExec(ns) - } - if err != nil { - return -1, nil, os.NewSyscallError("accept", err) - } - if err = syscall.SetNonblock(ns, true); err != nil { - closeFunc(ns) - return -1, nil, os.NewSyscallError("setnonblock", err) - } - return ns, sa, nil -} diff --git a/src/net/sock_posix.go b/src/net/sock_posix.go index 16351e1f14ed8..8985f8f23f124 100644 --- a/src/net/sock_posix.go +++ b/src/net/sock_posix.go @@ -8,6 +8,7 @@ package net import ( "context" + "internal/poll" "os" "syscall" ) @@ -43,11 +44,11 @@ func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only return nil, err } if err = setDefaultSockopts(s, family, sotype, ipv6only); err != nil { - closeFunc(s) + poll.CloseFunc(s) return nil, err } if fd, err = newFD(s, family, sotype, net); err != nil { - closeFunc(s) + poll.CloseFunc(s) return nil, err } @@ -127,7 +128,7 @@ func (fd *netFD) dial(ctx context.Context, laddr, raddr sockaddr) error { if lsa, err = laddr.sockaddr(fd.family); err != nil { return err } else if lsa != nil { - if err := syscall.Bind(fd.sysfd, lsa); err != nil { + if err := syscall.Bind(fd.pfd.Sysfd, lsa); err != nil { return os.NewSyscallError("bind", err) } } @@ -146,8 +147,8 @@ func (fd *netFD) dial(ctx context.Context, laddr, raddr sockaddr) error { return err } } - lsa, _ = syscall.Getsockname(fd.sysfd) - if rsa, _ = syscall.Getpeername(fd.sysfd); rsa != nil { + lsa, _ = syscall.Getsockname(fd.pfd.Sysfd) + if rsa, _ = syscall.Getpeername(fd.pfd.Sysfd); rsa != nil { fd.setAddr(fd.addrFunc()(lsa), fd.addrFunc()(rsa)) } else { fd.setAddr(fd.addrFunc()(lsa), raddr) @@ -156,23 +157,23 @@ func (fd *netFD) dial(ctx context.Context, laddr, raddr sockaddr) error { } func (fd *netFD) listenStream(laddr sockaddr, backlog int) error { - if err := setDefaultListenerSockopts(fd.sysfd); err != nil { + if err := setDefaultListenerSockopts(fd.pfd.Sysfd); err != nil { return err } if lsa, err := laddr.sockaddr(fd.family); err != nil { return err } else if lsa != nil { - if err := syscall.Bind(fd.sysfd, lsa); err != nil { + if err := syscall.Bind(fd.pfd.Sysfd, lsa); err != nil { return os.NewSyscallError("bind", err) } } - if err := listenFunc(fd.sysfd, backlog); err != nil { + if err := listenFunc(fd.pfd.Sysfd, backlog); err != nil { return os.NewSyscallError("listen", err) } if err := fd.init(); err != nil { return err } - lsa, _ := syscall.Getsockname(fd.sysfd) + lsa, _ := syscall.Getsockname(fd.pfd.Sysfd) fd.setAddr(fd.addrFunc()(lsa), nil) return nil } @@ -188,7 +189,7 @@ func (fd *netFD) listenDatagram(laddr sockaddr) error { // multiple UDP listeners that listen on the same UDP // port to join the same group address. if addr.IP != nil && addr.IP.IsMulticast() { - if err := setDefaultMulticastSockopts(fd.sysfd); err != nil { + if err := setDefaultMulticastSockopts(fd.pfd.Sysfd); err != nil { return err } addr := *addr @@ -204,14 +205,14 @@ func (fd *netFD) listenDatagram(laddr sockaddr) error { if lsa, err := laddr.sockaddr(fd.family); err != nil { return err } else if lsa != nil { - if err := syscall.Bind(fd.sysfd, lsa); err != nil { + if err := syscall.Bind(fd.pfd.Sysfd, lsa); err != nil { return os.NewSyscallError("bind", err) } } if err := fd.init(); err != nil { return err } - lsa, _ := syscall.Getsockname(fd.sysfd) + lsa, _ := syscall.Getsockname(fd.pfd.Sysfd) fd.setAddr(fd.addrFunc()(lsa), nil) return nil } diff --git a/src/net/sockopt_posix.go b/src/net/sockopt_posix.go index cd3d56228941e..e8af84f418ebe 100644 --- a/src/net/sockopt_posix.go +++ b/src/net/sockopt_posix.go @@ -7,7 +7,7 @@ package net import ( - "os" + "runtime" "syscall" ) @@ -101,27 +101,21 @@ done: } func setReadBuffer(fd *netFD, bytes int) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes)) + err := fd.pfd.SetsockoptInt(syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } func setWriteBuffer(fd *netFD, bytes int) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)) + err := fd.pfd.SetsockoptInt(syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } func setKeepAlive(fd *netFD, keepalive bool) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive))) + err := fd.pfd.SetsockoptInt(syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } func setLinger(fd *netFD, sec int) error { @@ -133,9 +127,7 @@ func setLinger(fd *netFD, sec int) error { l.Onoff = 0 l.Linger = 0 } - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptLinger(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_LINGER, &l)) + err := fd.pfd.SetsockoptLinger(syscall.SOL_SOCKET, syscall.SO_LINGER, &l) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } diff --git a/src/net/sockoptip_bsd.go b/src/net/sockoptip_bsd.go index b15c6396ba17f..b11f3a4edbe24 100644 --- a/src/net/sockoptip_bsd.go +++ b/src/net/sockoptip_bsd.go @@ -7,28 +7,24 @@ package net import ( - "os" + "runtime" "syscall" ) func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { ip, err := interfaceToIPv4Addr(ifi) if err != nil { - return os.NewSyscallError("setsockopt", err) + return wrapSyscallError("setsockopt", err) } var a [4]byte copy(a[:], ip.To4()) - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, a)) + err = fd.pfd.SetsockoptInet4Addr(syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, a) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v)))) + err := fd.pfd.SetsockoptByte(syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v))) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } diff --git a/src/net/sockoptip_linux.go b/src/net/sockoptip_linux.go index c1dcc911c7363..bd7d8344258e2 100644 --- a/src/net/sockoptip_linux.go +++ b/src/net/sockoptip_linux.go @@ -5,7 +5,7 @@ package net import ( - "os" + "runtime" "syscall" ) @@ -15,17 +15,13 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { v = int32(ifi.Index) } mreq := &syscall.IPMreqn{Ifindex: v} - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq)) + err := fd.pfd.SetsockoptIPMreqn(syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))) + err := fd.pfd.SetsockoptInt(syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } diff --git a/src/net/sockoptip_posix.go b/src/net/sockoptip_posix.go index d50886003eb87..4e10f2a6a5e9c 100644 --- a/src/net/sockoptip_posix.go +++ b/src/net/sockoptip_posix.go @@ -7,7 +7,7 @@ package net import ( - "os" + "runtime" "syscall" ) @@ -16,11 +16,9 @@ func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error { if err := setIPv4MreqToInterface(mreq, ifi); err != nil { return err } - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) + err := fd.pfd.SetsockoptIPMreq(syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error { @@ -28,19 +26,15 @@ func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error { if ifi != nil { v = ifi.Index } - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v)) + err := fd.pfd.SetsockoptInt(syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } func setIPv6MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v))) + err := fd.pfd.SetsockoptInt(syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v)) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error { @@ -49,9 +43,7 @@ func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error { if ifi != nil { mreq.Interface = uint32(ifi.Index) } - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)) + err := fd.pfd.SetsockoptIPv6Mreq(syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } diff --git a/src/net/sockoptip_windows.go b/src/net/sockoptip_windows.go index 916debebc6f70..62676039a3b0a 100644 --- a/src/net/sockoptip_windows.go +++ b/src/net/sockoptip_windows.go @@ -6,6 +6,7 @@ package net import ( "os" + "runtime" "syscall" "unsafe" ) @@ -17,17 +18,13 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { } var a [4]byte copy(a[:], ip.To4()) - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, (*byte)(unsafe.Pointer(&a[0])), 4)) + err = fd.pfd.Setsockopt(syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, (*byte)(unsafe.Pointer(&a[0])), 4) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))) + err := fd.pfd.SetsockoptInt(syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } diff --git a/src/net/sys_cloexec.go b/src/net/sys_cloexec.go index ba266e6534cbc..b7a842501ece6 100644 --- a/src/net/sys_cloexec.go +++ b/src/net/sys_cloexec.go @@ -10,6 +10,7 @@ package net import ( + "internal/poll" "os" "syscall" ) @@ -28,30 +29,8 @@ func sysSocket(family, sotype, proto int) (int, error) { return -1, os.NewSyscallError("socket", err) } if err = syscall.SetNonblock(s, true); err != nil { - closeFunc(s) + poll.CloseFunc(s) return -1, os.NewSyscallError("setnonblock", err) } return s, nil } - -// Wrapper around the accept system call that marks the returned file -// descriptor as nonblocking and close-on-exec. -func accept(s int) (int, syscall.Sockaddr, error) { - // See ../syscall/exec_unix.go for description of ForkLock. - // It is probably okay to hold the lock across syscall.Accept - // because we have put fd.sysfd into non-blocking mode. - // However, a call to the File method will put it back into - // blocking mode. We can't take that risk, so no use of ForkLock here. - ns, sa, err := acceptFunc(s) - if err == nil { - syscall.CloseOnExec(ns) - } - if err != nil { - return -1, nil, os.NewSyscallError("accept", err) - } - if err = syscall.SetNonblock(ns, true); err != nil { - closeFunc(ns) - return -1, nil, os.NewSyscallError("setnonblock", err) - } - return ns, sa, nil -} diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go index 69731ebc9143c..a544a5b3c32c2 100644 --- a/src/net/tcpsock.go +++ b/src/net/tcpsock.go @@ -255,7 +255,7 @@ func (l *TCPListener) SetDeadline(t time.Time) error { if !l.ok() { return syscall.EINVAL } - if err := l.fd.setDeadline(t); err != nil { + if err := l.fd.pfd.SetDeadline(t); err != nil { return &OpError{Op: "set", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err} } return nil diff --git a/src/net/tcpsockopt_darwin.go b/src/net/tcpsockopt_darwin.go index 0d1310eaf9ea7..7415c763c501b 100644 --- a/src/net/tcpsockopt_darwin.go +++ b/src/net/tcpsockopt_darwin.go @@ -5,7 +5,7 @@ package net import ( - "os" + "runtime" "syscall" "time" ) @@ -13,17 +13,15 @@ import ( const sysTCP_KEEPINTVL = 0x101 func setKeepAlivePeriod(fd *netFD, d time.Duration) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() // The kernel expects seconds so round to next highest second. d += (time.Second - time.Nanosecond) secs := int(d.Seconds()) - switch err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, sysTCP_KEEPINTVL, secs); err { + switch err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, sysTCP_KEEPINTVL, secs); err { case nil, syscall.ENOPROTOOPT: // OS X 10.7 and earlier don't support this option default: - return os.NewSyscallError("setsockopt", err) + return wrapSyscallError("setsockopt", err) } - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs)) + err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } diff --git a/src/net/tcpsockopt_posix.go b/src/net/tcpsockopt_posix.go index 805b56b5c765e..9cef434b6fa26 100644 --- a/src/net/tcpsockopt_posix.go +++ b/src/net/tcpsockopt_posix.go @@ -7,14 +7,12 @@ package net import ( - "os" + "runtime" "syscall" ) func setNoDelay(fd *netFD, noDelay bool) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay))) + err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay)) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } diff --git a/src/net/tcpsockopt_solaris.go b/src/net/tcpsockopt_solaris.go index 76285e5d2e566..019fe349eb21d 100644 --- a/src/net/tcpsockopt_solaris.go +++ b/src/net/tcpsockopt_solaris.go @@ -5,16 +5,12 @@ package net import ( - "os" + "runtime" "syscall" "time" ) func setKeepAlivePeriod(fd *netFD, d time.Duration) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() // The kernel expects milliseconds so round to next highest // millisecond. d += (time.Millisecond - time.Nanosecond) @@ -31,5 +27,7 @@ func setKeepAlivePeriod(fd *netFD, d time.Duration) error { // allocate a constant with a different meaning for the value of // TCP_KEEPINTVL on illumos. - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE_THRESHOLD, msecs)) + err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE_THRESHOLD, msecs) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } diff --git a/src/net/tcpsockopt_unix.go b/src/net/tcpsockopt_unix.go index 8d44fb209546e..c1df6605be4f6 100644 --- a/src/net/tcpsockopt_unix.go +++ b/src/net/tcpsockopt_unix.go @@ -7,21 +7,19 @@ package net import ( - "os" + "runtime" "syscall" "time" ) func setKeepAlivePeriod(fd *netFD, d time.Duration) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() // The kernel expects seconds so round to next highest second. d += (time.Second - time.Nanosecond) secs := int(d.Seconds()) - if err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs); err != nil { - return os.NewSyscallError("setsockopt", err) + if err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs); err != nil { + return wrapSyscallError("setsockopt", err) } - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs)) + err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) } diff --git a/src/net/tcpsockopt_windows.go b/src/net/tcpsockopt_windows.go index 45a4dca5257d3..73dead11d00be 100644 --- a/src/net/tcpsockopt_windows.go +++ b/src/net/tcpsockopt_windows.go @@ -6,16 +6,13 @@ package net import ( "os" + "runtime" "syscall" "time" "unsafe" ) func setKeepAlivePeriod(fd *netFD, d time.Duration) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() // The kernel expects milliseconds so round to next highest // millisecond. d += (time.Millisecond - time.Nanosecond) @@ -27,6 +24,7 @@ func setKeepAlivePeriod(fd *netFD, d time.Duration) error { } ret := uint32(0) size := uint32(unsafe.Sizeof(ka)) - err := syscall.WSAIoctl(fd.sysfd, syscall.SIO_KEEPALIVE_VALS, (*byte)(unsafe.Pointer(&ka)), size, nil, 0, &ret, nil, 0) + err := fd.pfd.WSAIoctl(syscall.SIO_KEEPALIVE_VALS, (*byte)(unsafe.Pointer(&ka)), size, nil, 0, &ret, nil, 0) + runtime.KeepAlive(fd) return os.NewSyscallError("wsaioctl", err) } diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go index 55bbf4402d980..9de7801ad107a 100644 --- a/src/net/timeout_test.go +++ b/src/net/timeout_test.go @@ -6,6 +6,7 @@ package net import ( "fmt" + "internal/poll" "internal/testenv" "io" "io/ioutil" @@ -145,9 +146,9 @@ var acceptTimeoutTests = []struct { }{ // Tests that accept deadlines in the past work, even if // there's incoming connections available. - {-5 * time.Second, [2]error{errTimeout, errTimeout}}, + {-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}}, - {50 * time.Millisecond, [2]error{nil, errTimeout}}, + {50 * time.Millisecond, [2]error{nil, poll.ErrTimeout}}, } func TestAcceptTimeout(t *testing.T) { @@ -299,9 +300,9 @@ var readTimeoutTests = []struct { }{ // Tests that read deadlines work, even if there's data ready // to be read. - {-5 * time.Second, [2]error{errTimeout, errTimeout}}, + {-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}}, - {50 * time.Millisecond, [2]error{nil, errTimeout}}, + {50 * time.Millisecond, [2]error{nil, poll.ErrTimeout}}, } func TestReadTimeout(t *testing.T) { @@ -423,9 +424,9 @@ var readFromTimeoutTests = []struct { }{ // Tests that read deadlines work, even if there's data ready // to be read. - {-5 * time.Second, [2]error{errTimeout, errTimeout}}, + {-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}}, - {50 * time.Millisecond, [2]error{nil, errTimeout}}, + {50 * time.Millisecond, [2]error{nil, poll.ErrTimeout}}, } func TestReadFromTimeout(t *testing.T) { @@ -496,9 +497,9 @@ var writeTimeoutTests = []struct { }{ // Tests that write deadlines work, even if there's buffer // space available to write. - {-5 * time.Second, [2]error{errTimeout, errTimeout}}, + {-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}}, - {10 * time.Millisecond, [2]error{nil, errTimeout}}, + {10 * time.Millisecond, [2]error{nil, poll.ErrTimeout}}, } func TestWriteTimeout(t *testing.T) { @@ -610,9 +611,9 @@ var writeToTimeoutTests = []struct { }{ // Tests that write deadlines work, even if there's buffer // space available to write. - {-5 * time.Second, [2]error{errTimeout, errTimeout}}, + {-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}}, - {10 * time.Millisecond, [2]error{nil, errTimeout}}, + {10 * time.Millisecond, [2]error{nil, poll.ErrTimeout}}, } func TestWriteToTimeout(t *testing.T) { diff --git a/src/net/unixsock.go b/src/net/unixsock.go index b25d492f5915b..d29514e47b320 100644 --- a/src/net/unixsock.go +++ b/src/net/unixsock.go @@ -264,7 +264,7 @@ func (l *UnixListener) SetDeadline(t time.Time) error { if !l.ok() { return syscall.EINVAL } - if err := l.fd.setDeadline(t); err != nil { + if err := l.fd.pfd.SetDeadline(t); err != nil { return &OpError{Op: "set", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err} } return nil diff --git a/src/net/writev_test.go b/src/net/writev_test.go index 7160d28c3a0fb..4c05be473d9ec 100644 --- a/src/net/writev_test.go +++ b/src/net/writev_test.go @@ -7,6 +7,7 @@ package net import ( "bytes" "fmt" + "internal/poll" "io" "io/ioutil" "reflect" @@ -99,13 +100,13 @@ func TestBuffers_WriteTo(t *testing.T) { } func testBuffer_writeTo(t *testing.T, chunks int, useCopy bool) { - oldHook := testHookDidWritev - defer func() { testHookDidWritev = oldHook }() + oldHook := poll.TestHookDidWritev + defer func() { poll.TestHookDidWritev = oldHook }() var writeLog struct { sync.Mutex log []int } - testHookDidWritev = func(size int) { + poll.TestHookDidWritev = func(size int) { writeLog.Lock() writeLog.log = append(writeLog.log, size) writeLog.Unlock() diff --git a/src/net/writev_unix.go b/src/net/writev_unix.go index 174e6bc51e3a6..bf0fbf8a13611 100644 --- a/src/net/writev_unix.go +++ b/src/net/writev_unix.go @@ -7,10 +7,8 @@ package net import ( - "io" - "os" + "runtime" "syscall" - "unsafe" ) func (c *conn) writeBuffers(v *Buffers) (int64, error) { @@ -25,71 +23,7 @@ func (c *conn) writeBuffers(v *Buffers) (int64, error) { } func (fd *netFD) writeBuffers(v *Buffers) (n int64, err error) { - if err := fd.writeLock(); err != nil { - return 0, err - } - defer fd.writeUnlock() - if err := fd.pd.prepareWrite(); err != nil { - return 0, err - } - - var iovecs []syscall.Iovec - if fd.iovecs != nil { - iovecs = *fd.iovecs - } - // TODO: read from sysconf(_SC_IOV_MAX)? The Linux default is - // 1024 and this seems conservative enough for now. Darwin's - // UIO_MAXIOV also seems to be 1024. - maxVec := 1024 - - for len(*v) > 0 { - iovecs = iovecs[:0] - for _, chunk := range *v { - if len(chunk) == 0 { - continue - } - iovecs = append(iovecs, syscall.Iovec{Base: &chunk[0]}) - if fd.isStream && len(chunk) > 1<<30 { - iovecs[len(iovecs)-1].SetLen(1 << 30) - break // continue chunk on next writev - } - iovecs[len(iovecs)-1].SetLen(len(chunk)) - if len(iovecs) == maxVec { - break - } - } - if len(iovecs) == 0 { - break - } - fd.iovecs = &iovecs // cache - - wrote, _, e0 := syscall.Syscall(syscall.SYS_WRITEV, - uintptr(fd.sysfd), - uintptr(unsafe.Pointer(&iovecs[0])), - uintptr(len(iovecs))) - if wrote == ^uintptr(0) { - wrote = 0 - } - testHookDidWritev(int(wrote)) - n += int64(wrote) - v.consume(int64(wrote)) - if e0 == syscall.EAGAIN { - if err = fd.pd.waitWrite(); err == nil { - continue - } - } else if e0 != 0 { - err = syscall.Errno(e0) - } - if err != nil { - break - } - if n == 0 { - err = io.ErrUnexpectedEOF - break - } - } - if _, ok := err.(syscall.Errno); ok { - err = os.NewSyscallError("writev", err) - } - return n, err + n, err = fd.pfd.Writev((*[][]byte)(v)) + runtime.KeepAlive(fd) + return n, wrapSyscallError("writev", err) } diff --git a/src/runtime/net_plan9.go b/src/runtime/net_plan9.go index 10fd089aea327..b1ac7c7887650 100644 --- a/src/runtime/net_plan9.go +++ b/src/runtime/net_plan9.go @@ -8,12 +8,12 @@ import ( _ "unsafe" ) -//go:linkname runtime_ignoreHangup net.runtime_ignoreHangup +//go:linkname runtime_ignoreHangup internal/poll.runtime_ignoreHangup func runtime_ignoreHangup() { getg().m.ignoreHangup = true } -//go:linkname runtime_unignoreHangup net.runtime_unignoreHangup +//go:linkname runtime_unignoreHangup internal/poll.runtime_unignoreHangup func runtime_unignoreHangup(sig string) { getg().m.ignoreHangup = false } diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go index 10a3c88a0972d..ac8d071045828 100644 --- a/src/runtime/netpoll.go +++ b/src/runtime/netpoll.go @@ -81,8 +81,8 @@ var ( pollcache pollCache ) -//go:linkname net_runtime_pollServerInit net.runtime_pollServerInit -func net_runtime_pollServerInit() { +//go:linkname poll_runtime_pollServerInit internal/poll.runtime_pollServerInit +func poll_runtime_pollServerInit() { netpollinit() atomic.Store(&netpollInited, 1) } @@ -91,8 +91,8 @@ func netpollinited() bool { return atomic.Load(&netpollInited) != 0 } -//go:linkname net_runtime_pollOpen net.runtime_pollOpen -func net_runtime_pollOpen(fd uintptr) (*pollDesc, int) { +//go:linkname poll_runtime_pollOpen internal/poll.runtime_pollOpen +func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) { pd := pollcache.alloc() lock(&pd.lock) if pd.wg != 0 && pd.wg != pdReady { @@ -115,8 +115,8 @@ func net_runtime_pollOpen(fd uintptr) (*pollDesc, int) { return pd, int(errno) } -//go:linkname net_runtime_pollClose net.runtime_pollClose -func net_runtime_pollClose(pd *pollDesc) { +//go:linkname poll_runtime_pollClose internal/poll.runtime_pollClose +func poll_runtime_pollClose(pd *pollDesc) { if !pd.closing { throw("netpollClose: close w/o unblock") } @@ -137,8 +137,8 @@ func (c *pollCache) free(pd *pollDesc) { unlock(&c.lock) } -//go:linkname net_runtime_pollReset net.runtime_pollReset -func net_runtime_pollReset(pd *pollDesc, mode int) int { +//go:linkname poll_runtime_pollReset internal/poll.runtime_pollReset +func poll_runtime_pollReset(pd *pollDesc, mode int) int { err := netpollcheckerr(pd, int32(mode)) if err != 0 { return err @@ -151,8 +151,8 @@ func net_runtime_pollReset(pd *pollDesc, mode int) int { return 0 } -//go:linkname net_runtime_pollWait net.runtime_pollWait -func net_runtime_pollWait(pd *pollDesc, mode int) int { +//go:linkname poll_runtime_pollWait internal/poll.runtime_pollWait +func poll_runtime_pollWait(pd *pollDesc, mode int) int { err := netpollcheckerr(pd, int32(mode)) if err != 0 { return err @@ -173,16 +173,16 @@ func net_runtime_pollWait(pd *pollDesc, mode int) int { return 0 } -//go:linkname net_runtime_pollWaitCanceled net.runtime_pollWaitCanceled -func net_runtime_pollWaitCanceled(pd *pollDesc, mode int) { +//go:linkname poll_runtime_pollWaitCanceled internal/poll.runtime_pollWaitCanceled +func poll_runtime_pollWaitCanceled(pd *pollDesc, mode int) { // This function is used only on windows after a failed attempt to cancel // a pending async IO operation. Wait for ioready, ignore closing or timeouts. for !netpollblock(pd, int32(mode), true) { } } -//go:linkname net_runtime_pollSetDeadline net.runtime_pollSetDeadline -func net_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { +//go:linkname poll_runtime_pollSetDeadline internal/poll.runtime_pollSetDeadline +func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { lock(&pd.lock) if pd.closing { unlock(&pd.lock) @@ -251,8 +251,8 @@ func net_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { } } -//go:linkname net_runtime_pollUnblock net.runtime_pollUnblock -func net_runtime_pollUnblock(pd *pollDesc) { +//go:linkname poll_runtime_pollUnblock internal/poll.runtime_pollUnblock +func poll_runtime_pollUnblock(pd *pollDesc) { lock(&pd.lock) if pd.closing { throw("netpollUnblock: already closing") diff --git a/src/runtime/netpoll_windows.go b/src/runtime/netpoll_windows.go index 7ad115850d58f..32c120c4c30e1 100644 --- a/src/runtime/netpoll_windows.go +++ b/src/runtime/netpoll_windows.go @@ -12,7 +12,8 @@ const _DWORD_MAX = 0xffffffff const _INVALID_HANDLE_VALUE = ^uintptr(0) -// net_op must be the same as beginning of net.operation. Keep these in sync. +// net_op must be the same as beginning of internal/poll.operation. +// Keep these in sync. type net_op struct { // used by windows o overlapped diff --git a/src/runtime/sema.go b/src/runtime/sema.go index 40463117039fa..5b9a264ebd932 100644 --- a/src/runtime/sema.go +++ b/src/runtime/sema.go @@ -61,8 +61,8 @@ func sync_runtime_Semacquire(addr *uint32) { semacquire(addr, semaBlockProfile) } -//go:linkname net_runtime_Semacquire net.runtime_Semacquire -func net_runtime_Semacquire(addr *uint32) { +//go:linkname poll_runtime_Semacquire internal/poll.runtime_Semacquire +func poll_runtime_Semacquire(addr *uint32) { semacquire(addr, semaBlockProfile) } @@ -76,8 +76,8 @@ func sync_runtime_SemacquireMutex(addr *uint32) { semacquire(addr, semaBlockProfile|semaMutexProfile) } -//go:linkname net_runtime_Semrelease net.runtime_Semrelease -func net_runtime_Semrelease(addr *uint32) { +//go:linkname poll_runtime_Semrelease internal/poll.runtime_Semrelease +func poll_runtime_Semrelease(addr *uint32) { semrelease(addr) } diff --git a/src/runtime/time.go b/src/runtime/time.go index a095ec04fddea..c296338e9ba89 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -292,8 +292,8 @@ func siftdownTimer(i int) { // Entry points for net, time to call nanotime. -//go:linkname net_runtimeNano net.runtimeNano -func net_runtimeNano() int64 { +//go:linkname poll_runtimeNano internal/poll.runtimeNano +func poll_runtimeNano() int64 { return nanotime() } diff --git a/src/runtime/trace/trace_stack_test.go b/src/runtime/trace/trace_stack_test.go index f8abf66500eac..d6a3858b91337 100644 --- a/src/runtime/trace/trace_stack_test.go +++ b/src/runtime/trace/trace_stack_test.go @@ -231,6 +231,7 @@ func TestTraceSymbolize(t *testing.T) { if runtime.GOOS != "windows" && runtime.GOOS != "plan9" { want = append(want, []eventDesc{ {trace.EvGoBlockNet, []frame{ + {"internal/poll.(*FD).Accept", 0}, {"net.(*netFD).accept", 0}, {"net.(*TCPListener).accept", 0}, {"net.(*TCPListener).Accept", 0},