// doBlockingWithCtx executes a blocking function in a separate goroutine when the provided
// context is cancellable. It is intended for use with calls that don't support context
// cancellation (cgo, syscalls). blocking func may still be running after this function finishes.
-func doBlockingWithCtx[T any](ctx context.Context, blocking func() (T, error)) (T, error) {
+// For the duration of the execution of the blocking function, the thread is 'acquired' using [acquireThread],
+// blocking might not be executed when the context gets cancelled early.
+func doBlockingWithCtx[T any](ctx context.Context, lookupName string, blocking func() (T, error)) (T, error) {
+ if err := acquireThread(ctx); err != nil {
+ var zero T
+ return zero, &DNSError{
+ Name: lookupName,
+ Err: mapErr(err).Error(),
+ IsTimeout: err == context.DeadlineExceeded,
+ }
+ }
+
if ctx.Done() == nil {
+ defer releaseThread()
return blocking()
}
res := make(chan result, 1)
go func() {
+ defer releaseThread()
var r result
r.res, r.err = blocking()
res <- r
return r.res, r.err
case <-ctx.Done():
var zero T
- return zero, mapErr(ctx.Err())
+ return zero, &DNSError{
+ Name: lookupName,
+ Err: mapErr(ctx.Err()).Error(),
+ IsTimeout: ctx.Err() == context.DeadlineExceeded,
+ }
}
}
*_C_ai_family(&hints) = _C_AF_INET6
}
- return doBlockingWithCtx(ctx, func() (int, error) {
+ return doBlockingWithCtx(ctx, network+"/"+service, func() (int, error) {
return cgoLookupServicePort(&hints, network, service)
})
}
}
func cgoLookupHostIP(network, name string) (addrs []IPAddr, err error) {
- acquireThread()
- defer releaseThread()
-
var hints _C_struct_addrinfo
*_C_ai_flags(&hints) = cgoAddrInfoFlags
*_C_ai_socktype(&hints) = _C_SOCK_STREAM
}
func cgoLookupIP(ctx context.Context, network, name string) (addrs []IPAddr, err error) {
- return doBlockingWithCtx(ctx, func() ([]IPAddr, error) {
+ return doBlockingWithCtx(ctx, name, func() ([]IPAddr, error) {
return cgoLookupHostIP(network, name)
})
}
return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}
}
- return doBlockingWithCtx(ctx, func() ([]string, error) {
+ return doBlockingWithCtx(ctx, addr, func() ([]string, error) {
return cgoLookupAddrPTR(addr, sa, salen)
})
}
func cgoLookupAddrPTR(addr string, sa *_C_struct_sockaddr, salen _C_socklen_t) (names []string, err error) {
- acquireThread()
- defer releaseThread()
-
var gerrno int
var b []byte
for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 {
// resSearch will make a call to the 'res_nsearch' routine in the C library
// and parse the output as a slice of DNS resources.
func resSearch(ctx context.Context, hostname string, rtype, class int) ([]dnsmessage.Resource, error) {
- return doBlockingWithCtx(ctx, func() ([]dnsmessage.Resource, error) {
+ return doBlockingWithCtx(ctx, hostname, func() ([]dnsmessage.Resource, error) {
return cgoResSearch(hostname, rtype, class)
})
}
func cgoResSearch(hostname string, rtype, class int) ([]dnsmessage.Resource, error) {
- acquireThread()
- defer releaseThread()
-
resStateSize := unsafe.Sizeof(_C_struct___res_state{})
var state *_C_struct___res_state
if resStateSize > 0 {
}
ch := make(chan result) // unbuffered
go func() {
- acquireThread()
+ if err := acquireThread(ctx); err != nil {
+ ch <- result{err: mapErr(err)}
+ return
+ }
defer releaseThread()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
}
getaddr := func() ([]IPAddr, error) {
- acquireThread()
+ if err := acquireThread(ctx); err != nil {
+ return nil, &DNSError{
+ Name: name,
+ Err: mapErr(err).Error(),
+ IsTimeout: ctx.Err() == context.DeadlineExceeded,
+ }
+ }
defer releaseThread()
hints := syscall.AddrinfoW{
Family: family,
return lookupPortMap(network, service)
}
- // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
- acquireThread()
+ // TODO(bradfitz): finish ctx plumbing
+ if err := acquireThread(ctx); err != nil {
+ return 0, &DNSError{
+ Name: network + "/" + service,
+ Err: mapErr(err).Error(),
+ IsTimeout: ctx.Err() == context.DeadlineExceeded,
+ }
+ }
defer releaseThread()
var hints syscall.AddrinfoW
return r.goLookupCNAME(ctx, name, order, conf)
}
- // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
- acquireThread()
+ // TODO(bradfitz): finish ctx plumbing
+ if err := acquireThread(ctx); err != nil {
+ return "", &DNSError{
+ Name: name,
+ Err: mapErr(err).Error(),
+ IsTimeout: ctx.Err() == context.DeadlineExceeded,
+ }
+ }
defer releaseThread()
var rec *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &rec, nil)
if systemConf().mustUseGoResolver(r) {
return r.goLookupSRV(ctx, service, proto, name)
}
- // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
- acquireThread()
+ // TODO(bradfitz): finish ctx plumbing
+ if err := acquireThread(ctx); err != nil {
+ return "", nil, &DNSError{
+ Name: name,
+ Err: mapErr(err).Error(),
+ IsTimeout: ctx.Err() == context.DeadlineExceeded,
+ }
+ }
defer releaseThread()
var target string
if service == "" && proto == "" {
if systemConf().mustUseGoResolver(r) {
return r.goLookupMX(ctx, name)
}
- // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
- acquireThread()
+ // TODO(bradfitz): finish ctx plumbing.
+ if err := acquireThread(ctx); err != nil {
+ return nil, &DNSError{
+ Name: name,
+ Err: mapErr(err).Error(),
+ IsTimeout: ctx.Err() == context.DeadlineExceeded,
+ }
+ }
defer releaseThread()
var rec *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &rec, nil)
if systemConf().mustUseGoResolver(r) {
return r.goLookupNS(ctx, name)
}
- // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
- acquireThread()
+ // TODO(bradfitz): finish ctx plumbing.
+ if err := acquireThread(ctx); err != nil {
+ return nil, &DNSError{
+ Name: name,
+ Err: mapErr(err).Error(),
+ IsTimeout: ctx.Err() == context.DeadlineExceeded,
+ }
+ }
defer releaseThread()
var rec *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &rec, nil)
if systemConf().mustUseGoResolver(r) {
return r.goLookupTXT(ctx, name)
}
- // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
- acquireThread()
+ // TODO(bradfitz): finish ctx plumbing.
+ if err := acquireThread(ctx); err != nil {
+ return nil, &DNSError{
+ Name: name,
+ Err: mapErr(err).Error(),
+ IsTimeout: ctx.Err() == context.DeadlineExceeded,
+ }
+ }
defer releaseThread()
var rec *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &rec, nil)
return r.goLookupPTR(ctx, addr, order, conf)
}
- // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
- acquireThread()
+ // TODO(bradfitz): finish ctx plumbing.
+ if err := acquireThread(ctx); err != nil {
+ return nil, &DNSError{
+ Name: addr,
+ Err: mapErr(err).Error(),
+ IsTimeout: ctx.Err() == context.DeadlineExceeded,
+ }
+ }
defer releaseThread()
arpa, err := reverseaddr(addr)
if err != nil {