|
|
|
@ -126,8 +126,15 @@ type topicRegisterReq struct { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type topicSearchReq struct { |
|
|
|
|
topic Topic |
|
|
|
|
found chan<- string |
|
|
|
|
topic Topic |
|
|
|
|
found chan<- *Node |
|
|
|
|
lookup chan<- bool |
|
|
|
|
delay time.Duration |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type topicSearchResult struct { |
|
|
|
|
target lookupInfo |
|
|
|
|
nodes []*Node |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type timeoutEvent struct { |
|
|
|
@ -263,16 +270,23 @@ func (net *Network) lookup(target common.Hash, stopOnMatch bool) []*Node { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
// Wait for the next reply.
|
|
|
|
|
for _, n := range <-reply { |
|
|
|
|
if n != nil && !seen[n.ID] { |
|
|
|
|
seen[n.ID] = true |
|
|
|
|
result.push(n, bucketSize) |
|
|
|
|
if stopOnMatch && n.sha == target { |
|
|
|
|
return result.entries |
|
|
|
|
select { |
|
|
|
|
case nodes := <-reply: |
|
|
|
|
for _, n := range nodes { |
|
|
|
|
if n != nil && !seen[n.ID] { |
|
|
|
|
seen[n.ID] = true |
|
|
|
|
result.push(n, bucketSize) |
|
|
|
|
if stopOnMatch && n.sha == target { |
|
|
|
|
return result.entries |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
pendingQueries-- |
|
|
|
|
case <-time.After(respTimeout): |
|
|
|
|
// forget all pending requests, start new ones
|
|
|
|
|
pendingQueries = 0 |
|
|
|
|
reply = make(chan []*Node, alpha) |
|
|
|
|
} |
|
|
|
|
pendingQueries-- |
|
|
|
|
} |
|
|
|
|
return result.entries |
|
|
|
|
} |
|
|
|
@ -293,18 +307,20 @@ func (net *Network) RegisterTopic(topic Topic, stop <-chan struct{}) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (net *Network) SearchTopic(topic Topic, stop <-chan struct{}, found chan<- string) { |
|
|
|
|
select { |
|
|
|
|
case net.topicSearchReq <- topicSearchReq{topic, found}: |
|
|
|
|
case <-net.closed: |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
select { |
|
|
|
|
case <-net.closed: |
|
|
|
|
case <-stop: |
|
|
|
|
func (net *Network) SearchTopic(topic Topic, setPeriod <-chan time.Duration, found chan<- *Node, lookup chan<- bool) { |
|
|
|
|
for { |
|
|
|
|
select { |
|
|
|
|
case net.topicSearchReq <- topicSearchReq{topic, nil}: |
|
|
|
|
case <-net.closed: |
|
|
|
|
return |
|
|
|
|
case delay, ok := <-setPeriod: |
|
|
|
|
select { |
|
|
|
|
case net.topicSearchReq <- topicSearchReq{topic: topic, found: found, lookup: lookup, delay: delay}: |
|
|
|
|
case <-net.closed: |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
if !ok { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -347,6 +363,13 @@ func (net *Network) reqTableOp(f func()) (called bool) { |
|
|
|
|
|
|
|
|
|
// TODO: external address handling.
|
|
|
|
|
|
|
|
|
|
type topicSearchInfo struct { |
|
|
|
|
lookupChn chan<- bool |
|
|
|
|
period time.Duration |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const maxSearchCount = 5 |
|
|
|
|
|
|
|
|
|
func (net *Network) loop() { |
|
|
|
|
var ( |
|
|
|
|
refreshTimer = time.NewTicker(autoRefreshInterval) |
|
|
|
@ -385,10 +408,12 @@ func (net *Network) loop() { |
|
|
|
|
topicRegisterLookupTarget lookupInfo |
|
|
|
|
topicRegisterLookupDone chan []*Node |
|
|
|
|
topicRegisterLookupTick = time.NewTimer(0) |
|
|
|
|
topicSearchLookupTarget lookupInfo |
|
|
|
|
searchReqWhenRefreshDone []topicSearchReq |
|
|
|
|
searchInfo = make(map[Topic]topicSearchInfo) |
|
|
|
|
activeSearchCount int |
|
|
|
|
) |
|
|
|
|
topicSearchLookupDone := make(chan []*Node, 1) |
|
|
|
|
topicSearchLookupDone := make(chan topicSearchResult, 100) |
|
|
|
|
topicSearch := make(chan Topic, 100) |
|
|
|
|
<-topicRegisterLookupTick.C |
|
|
|
|
|
|
|
|
|
statsDump := time.NewTicker(10 * time.Second) |
|
|
|
@ -504,21 +529,52 @@ loop: |
|
|
|
|
case req := <-net.topicSearchReq: |
|
|
|
|
if refreshDone == nil { |
|
|
|
|
debugLog("<-net.topicSearchReq") |
|
|
|
|
if req.found == nil { |
|
|
|
|
net.ticketStore.removeSearchTopic(req.topic) |
|
|
|
|
info, ok := searchInfo[req.topic] |
|
|
|
|
if ok { |
|
|
|
|
if req.delay == time.Duration(0) { |
|
|
|
|
delete(searchInfo, req.topic) |
|
|
|
|
net.ticketStore.removeSearchTopic(req.topic) |
|
|
|
|
} else { |
|
|
|
|
info.period = req.delay |
|
|
|
|
searchInfo[req.topic] = info |
|
|
|
|
} |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
net.ticketStore.addSearchTopic(req.topic, req.found) |
|
|
|
|
if (topicSearchLookupTarget.target == common.Hash{}) { |
|
|
|
|
topicSearchLookupDone <- nil |
|
|
|
|
if req.delay != time.Duration(0) { |
|
|
|
|
var info topicSearchInfo |
|
|
|
|
info.period = req.delay |
|
|
|
|
info.lookupChn = req.lookup |
|
|
|
|
searchInfo[req.topic] = info |
|
|
|
|
net.ticketStore.addSearchTopic(req.topic, req.found) |
|
|
|
|
topicSearch <- req.topic |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
searchReqWhenRefreshDone = append(searchReqWhenRefreshDone, req) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case nodes := <-topicSearchLookupDone: |
|
|
|
|
debugLog("<-topicSearchLookupDone") |
|
|
|
|
net.ticketStore.searchLookupDone(topicSearchLookupTarget, nodes, func(n *Node) []byte { |
|
|
|
|
case topic := <-topicSearch: |
|
|
|
|
if activeSearchCount < maxSearchCount { |
|
|
|
|
activeSearchCount++ |
|
|
|
|
target := net.ticketStore.nextSearchLookup(topic) |
|
|
|
|
go func() { |
|
|
|
|
nodes := net.lookup(target.target, false) |
|
|
|
|
topicSearchLookupDone <- topicSearchResult{target: target, nodes: nodes} |
|
|
|
|
}() |
|
|
|
|
} |
|
|
|
|
period := searchInfo[topic].period |
|
|
|
|
if period != time.Duration(0) { |
|
|
|
|
go func() { |
|
|
|
|
time.Sleep(period) |
|
|
|
|
topicSearch <- topic |
|
|
|
|
}() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case res := <-topicSearchLookupDone: |
|
|
|
|
activeSearchCount-- |
|
|
|
|
if lookupChn := searchInfo[res.target.topic].lookupChn; lookupChn != nil { |
|
|
|
|
lookupChn <- net.ticketStore.radius[res.target.topic].converged |
|
|
|
|
} |
|
|
|
|
net.ticketStore.searchLookupDone(res.target, res.nodes, func(n *Node) []byte { |
|
|
|
|
net.ping(n, n.addr()) |
|
|
|
|
return n.pingEcho |
|
|
|
|
}, func(n *Node, topic Topic) []byte { |
|
|
|
@ -531,11 +587,6 @@ loop: |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
topicSearchLookupTarget = net.ticketStore.nextSearchLookup() |
|
|
|
|
target := topicSearchLookupTarget.target |
|
|
|
|
if (target != common.Hash{}) { |
|
|
|
|
go func() { topicSearchLookupDone <- net.lookup(target, false) }() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case <-statsDump.C: |
|
|
|
|
debugLog("<-statsDump.C") |
|
|
|
|