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.

100 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. /*
  6. IPeerSet has a (immutable) subset of the methods of PeerSet.
  7. */
  8. type IPeerSet interface {
  9. Has(key string) 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 key (address) is already in set.
  34. func (ps *PeerSet) Add(peer *Peer) bool {
  35. ps.mtx.Lock()
  36. defer ps.mtx.Unlock()
  37. if ps.lookup[peer.Key] != 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[peer.Key] = &peerSetItem{peer, index}
  45. return true
  46. }
  47. func (ps *PeerSet) Has(peerKey string) bool {
  48. ps.mtx.Lock()
  49. defer ps.mtx.Unlock()
  50. _, ok := ps.lookup[peerKey]
  51. return ok
  52. }
  53. func (ps *PeerSet) Remove(peer *Peer) {
  54. ps.mtx.Lock()
  55. defer ps.mtx.Unlock()
  56. item := ps.lookup[peer.Key]
  57. if item == nil {
  58. return
  59. }
  60. index := item.index
  61. // Copy the list but without the last element.
  62. // (we must copy because we're mutating the list)
  63. newList := make([]*Peer, len(ps.list)-1)
  64. copy(newList, ps.list)
  65. // If it's the last peer, that's an easy special case.
  66. if index == len(ps.list)-1 {
  67. ps.list = newList
  68. return
  69. }
  70. // Move the last item from ps.list to "index" in list.
  71. lastPeer := ps.list[len(ps.list)-1]
  72. lastPeerAddr := lastPeer.mconn.RemoteAddress.String()
  73. lastPeerItem := ps.lookup[lastPeerAddr]
  74. newList[index] = lastPeer
  75. lastPeerItem.index = index
  76. ps.list = newList
  77. delete(ps.lookup, peer.Key)
  78. }
  79. func (ps *PeerSet) Size() int {
  80. ps.mtx.Lock()
  81. defer ps.mtx.Unlock()
  82. return len(ps.list)
  83. }
  84. // threadsafe list of peers.
  85. func (ps *PeerSet) List() []*Peer {
  86. ps.mtx.Lock()
  87. defer ps.mtx.Unlock()
  88. return ps.list
  89. }