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.

102 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
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. // Returns false if peer with address is already in set.
  34. func (ps *PeerSet) Add(peer *Peer) bool {
  35. ps.mtx.Lock()
  36. defer ps.mtx.Unlock()
  37. addr := peer.RemoteAddress().String()
  38. if ps.lookup[addr] != nil {
  39. return false
  40. }
  41. index := len(ps.list)
  42. // Appending is safe even with other goroutines
  43. // iterating over the ps.list slice.
  44. ps.list = append(ps.list, peer)
  45. ps.lookup[addr] = &peerSetItem{peer, index}
  46. return true
  47. }
  48. func (ps *PeerSet) Has(addr *NetAddress) bool {
  49. ps.mtx.Lock()
  50. defer ps.mtx.Unlock()
  51. _, ok := ps.lookup[addr.String()]
  52. return ok
  53. }
  54. func (ps *PeerSet) Remove(peer *Peer) {
  55. ps.mtx.Lock()
  56. defer ps.mtx.Unlock()
  57. addr := peer.RemoteAddress().String()
  58. item := ps.lookup[addr]
  59. if item == nil {
  60. return
  61. }
  62. index := item.index
  63. // Copy the list but without the last element.
  64. // (we must copy because we're mutating the list)
  65. newList := make([]*Peer, len(ps.list)-1)
  66. copy(newList, ps.list)
  67. // If it's the last peer, that's an easy special case.
  68. if index == len(ps.list)-1 {
  69. ps.list = newList
  70. return
  71. }
  72. // Move the last item from ps.list to "index" in list.
  73. lastPeer := ps.list[len(ps.list)-1]
  74. lastPeerAddr := lastPeer.RemoteAddress().String()
  75. lastPeerItem := ps.lookup[lastPeerAddr]
  76. newList[index] = lastPeer
  77. lastPeerItem.index = index
  78. ps.list = newList
  79. delete(ps.lookup, addr)
  80. }
  81. func (ps *PeerSet) Size() int {
  82. ps.mtx.Lock()
  83. defer ps.mtx.Unlock()
  84. return len(ps.list)
  85. }
  86. // threadsafe list of peers.
  87. func (ps *PeerSet) List() []*Peer {
  88. ps.mtx.Lock()
  89. defer ps.mtx.Unlock()
  90. return ps.list
  91. }