|
|
- package p2p
-
- import (
- "sync"
- )
-
- /*
- PeerSet is a special structure for keeping a table of peers.
- Iteration over the peers is super fast and thread-safe.
- */
- type PeerSet struct {
- mtx sync.Mutex
- lookup map[string]*peerSetItem
- list []*Peer
- }
-
- type peerSetItem struct {
- peer *Peer
- index int
- }
-
- func NewPeerSet() *PeerSet {
- return &PeerSet{
- lookup: make(map[string]*peerSetItem),
- list: make([]*Peer, 0, 256),
- }
- }
-
- func (ps *PeerSet) Add(peer *Peer) bool {
- ps.mtx.Lock()
- defer ps.mtx.Unlock()
- addr := peer.RemoteAddress().String()
- if ps.lookup[addr] != nil {
- return false
- }
- index := len(ps.list)
- // Appending is safe even with other goroutines
- // iterating over the ps.list slice.
- ps.list = append(ps.list, peer)
- ps.lookup[addr] = &peerSetItem{peer, index}
- return true
- }
-
- func (ps *PeerSet) Remove(peer *Peer) {
- ps.mtx.Lock()
- defer ps.mtx.Unlock()
- addr := peer.RemoteAddress().String()
- item := ps.lookup[addr]
- if item == nil {
- return
- }
- index := item.index
- // Copy the list but without the last element.
- // (we must copy because we're mutating the list)
- newList := make([]*Peer, len(ps.list)-1)
- copy(newList, ps.list)
- // If it's the last peer, that's an easy special case.
- if index == len(ps.list)-1 {
- ps.list = newList
- return
- }
- // Move the last item from ps.list to "index" in list.
- lastPeer := ps.list[len(ps.list)-1]
- lastPeerAddr := lastPeer.RemoteAddress().String()
- lastPeerItem := ps.lookup[lastPeerAddr]
- newList[index] = lastPeer
- lastPeerItem.index = index
- ps.list = newList
- delete(ps.lookup, addr)
- }
-
- // threadsafe list of peers.
- func (ps *PeerSet) List() []*Peer {
- ps.mtx.Lock()
- defer ps.mtx.Unlock()
- return ps.list
- }
|