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.

96 lines
2.1 KiB

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