Maintained by: NLnet Labs

[Unbound-users] Using libunbound from other languages

Phil Pennock
Tue Jul 23 00:12:31 CEST 2013


On 2013-07-22 at 16:43 +0000, Miek Gieben wrote:
> [ Quoting <unbound-users+phil at spodhu> in "Re: [Unbound-users] Using libunboun..." ]
> > On 2013-07-22 at 10:32 +0000, Miek Gieben wrote:
> > > In this case I'm talking about using libunbound in Go and the locking issues
> > > that can occur with wrt to the crypto library that is used.
> > > 
> > > In Go you do not have control over the amount of OS threads the runtime uses.
> > > Go uses goroutines and N of those routines are mapped to an OS thread at any moment
> > > in time. 
> > 
> > For this sort of scenario, you want Go's runtime.LockOSThread().
> 
> Hmmmm, that would certainly help (and maybe hurt performance), but then I still
> don't have the proper thread locking in place....

You don't need it.  You create a go-routine which uses channels for
asking for some DNS and getting the response back.  Something like this
(untested):

  type ResolveResult struct {
    // whatever
    Error   error
  }
  type ResolveResultAndError struct {
    Result  *ResolveResult
    Error   error
  }
  type ResolveRequest struct {
    Label     string
    DNSType   string
    Response  chan ResolveResult
  }

  var GlobalResolver chan *ResolveRequest

  func QueryDNS(label, typ string) (ResolveResult, error) {
    inbox := make(chan ResolveResultAndError, 1)
    GlobalResolver <- &ResolveRequest{
      Label:    label,
      DNSType:  type,
      Response: inbox,
    }
    response := <-inbox
    if response.Error != nil {
      return nil, response.Error
    }
    return response.Result, nil
  }

  func InitDNS() {
    GlobalResolver := make(chan *ResolveRequest, 10)
    oneResolverThread := func() {
      runtime.LockOSThread()
      defer runtime.UnlockOSThread()
      // do Unbound setup
      for {
        switch {
        case req := <-GlobalResolver:
          // initiate request into Unbound
        case resp := <-somethingGettingUnboundResponses
          // send an result/error combo back down req.Response
        }
      }
    }
    go oneResolverThread()  // repeat N times (see below)
  }

You can then have a pool of Unbound clients, for as many concurrent
resolver OS threads as you want, all reading from the same
GlobalResolver channel, giving you automatic load-balancing across the
resolver threads.  The response goes back to the inbox supplied.  If the
Unbound API is being used synchronously, then you don't even have the
inner switch in the routine, just get the client request, handle it,
respond.

Synchronous with N OS threads (no Go switch) and C.ub_resolve() should
get things going, since the callbacks for async will basically need to
be handled carefully, since if memory serves Go doesn't expose channel
write to the C layer.  Or am I wrong?

-Phil