bootstrap.go 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. package dht
  2. import (
  3. "context"
  4. "errors"
  5. "time"
  6. "github.com/anacrolix/dht/v2/krpc"
  7. "github.com/anacrolix/dht/v2/traversal"
  8. )
  9. type TraversalStats = traversal.Stats
  10. // See BootstrapContext.
  11. func (s *Server) Bootstrap() (TraversalStats, error) {
  12. return s.BootstrapContext(context.Background())
  13. }
  14. // Populates the node table.
  15. func (s *Server) BootstrapContext(ctx context.Context) (_ TraversalStats, err error) {
  16. s.mu.Lock()
  17. if s.bootstrappingNow {
  18. s.mu.Unlock()
  19. err = errors.New("already bootstrapping")
  20. return
  21. }
  22. s.bootstrappingNow = true
  23. s.mu.Unlock()
  24. defer func() {
  25. s.mu.Lock()
  26. defer s.mu.Unlock()
  27. s.bootstrappingNow = false
  28. }()
  29. // Track number of responses, for STM use. (It's available via atomic in TraversalStats but that
  30. // won't let wake up STM transactions that are observing the value.)
  31. t := traversal.Start(traversal.OperationInput{
  32. Target: krpc.ID(s.id.AsByteArray()),
  33. K: 16,
  34. DoQuery: func(ctx context.Context, addr krpc.NodeAddr) traversal.QueryResult {
  35. return s.FindNode(NewAddr(addr.UDP()), s.id, QueryRateLimiting{}).TraversalQueryResult(addr)
  36. },
  37. NodeFilter: s.TraversalNodeFilter,
  38. })
  39. nodes, err := s.TraversalStartingNodes()
  40. if err != nil {
  41. return
  42. }
  43. t.AddNodes(nodes)
  44. s.mu.Lock()
  45. s.lastBootstrap = time.Now()
  46. s.mu.Unlock()
  47. select {
  48. case <-ctx.Done():
  49. err = ctx.Err()
  50. case <-t.Stalled():
  51. }
  52. t.Stop()
  53. if err != nil {
  54. // Could test for Stopped and return stats here but the interface doesn't tell the caller if
  55. // we were successful in taking the stats. We could also take a snapshot instead.
  56. return
  57. }
  58. <-t.Stopped()
  59. return *t.Stats(), nil
  60. }