You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

101 lines
2.1 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. package p2p
  2. import (
  3. "sync"
  4. )
  5. /*
  6. IPeerSet has a (immutable) subset of the methods of PeerSet.
  7. */
  8. type IPeerSet interface {
  9. Has(addr *NetAddress) bool
  10. List() []*Peer
  11. Size() int
  12. }
  13. //-----------------------------------------------------------------------------
  14. /*
  15. PeerSet is a special structure for keeping a table of peers.
  16. Iteration over the peers is super fast and thread-safe.
  17. */
  18. type PeerSet struct {
  19. mtx sync.Mutex
  20. lookup map[string]*peerSetItem
  21. list []*Peer
  22. }
  23. type peerSetItem struct {
  24. peer *Peer
  25. index int
  26. }
  27. func NewPeerSet() *PeerSet {
  28. return &PeerSet{
  29. lookup: make(map[string]*peerSetItem),
  30. list: make([]*Peer, 0, 256),
  31. }
  32. }
  33. func (ps *PeerSet) Add(peer *Peer) bool {
  34. ps.mtx.Lock()
  35. defer ps.mtx.Unlock()
  36. addr := peer.RemoteAddress().String()
  37. if ps.lookup[addr] != nil {
  38. return false
  39. }
  40. index := len(ps.list)
  41. // Appending is safe even with other goroutines
  42. // iterating over the ps.list slice.
  43. ps.list = append(ps.list, peer)
  44. ps.lookup[addr] = &peerSetItem{peer, index}
  45. return true
  46. }
  47. func (ps *PeerSet) Has(addr *NetAddress) bool {
  48. ps.mtx.Lock()
  49. defer ps.mtx.Unlock()
  50. _, ok := ps.lookup[addr.String()]
  51. return ok
  52. }
  53. func (ps *PeerSet) Remove(peer *Peer) {
  54. ps.mtx.Lock()
  55. defer ps.mtx.Unlock()
  56. addr := peer.RemoteAddress().String()
  57. item := ps.lookup[addr]
  58. if item == nil {
  59. return
  60. }
  61. index := item.index
  62. // Copy the list but without the last element.
  63. // (we must copy because we're mutating the list)
  64. newList := make([]*Peer, len(ps.list)-1)
  65. copy(newList, ps.list)
  66. // If it's the last peer, that's an easy special case.
  67. if index == len(ps.list)-1 {
  68. ps.list = newList
  69. return
  70. }
  71. // Move the last item from ps.list to "index" in list.
  72. lastPeer := ps.list[len(ps.list)-1]
  73. lastPeerAddr := lastPeer.RemoteAddress().String()
  74. lastPeerItem := ps.lookup[lastPeerAddr]
  75. newList[index] = lastPeer
  76. lastPeerItem.index = index
  77. ps.list = newList
  78. delete(ps.lookup, addr)
  79. }
  80. func (ps *PeerSet) Size() int {
  81. ps.mtx.Lock()
  82. defer ps.mtx.Unlock()
  83. return len(ps.list)
  84. }
  85. // threadsafe list of peers.
  86. func (ps *PeerSet) List() []*Peer {
  87. ps.mtx.Lock()
  88. defer ps.mtx.Unlock()
  89. return ps.list
  90. }