mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-17 02:47:51 +01:00
Break up SendRequest into individual functions SendRequestStore and SendRequestFindNode, which improves readability. Alpha parameter must be now passed. MarkNodeAsSeen is exported. Breaking up dht.iterate and embedding into 3 functions Get, FindNode, and FindValue.
137 lines
4.2 KiB
Go
137 lines
4.2 KiB
Go
/*
|
|
File Name: Information Request.go
|
|
Copyright: 2021 Peernet s.r.o.
|
|
Author: Peter Kleissner
|
|
|
|
Information requests are asynchronous queries sent to nodes.
|
|
*/
|
|
|
|
package dht
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// InformationRequest is an asynchronous request sent to nodes. It tracks any asynchronous replies and handles timeouts.
|
|
type InformationRequest struct {
|
|
Action int // ActionX
|
|
Key []byte // Key that is being queried
|
|
ResultChan chan *NodeMessage // Result channel
|
|
ActiveNodes uint64 // Number of nodes actively handling the request.
|
|
Nodes []*Node // Nodes that are receiving the request.
|
|
IsTerminated bool // If true, it was signaled for termination
|
|
TerminateSignal chan struct{} // gets closed on termination signal, can be used in select via "case _ = <- network.terminateSignal:"
|
|
sync.Mutex // for sychronized closing
|
|
}
|
|
|
|
// Actions for performing the information request
|
|
const (
|
|
ActionFindNode = iota // Find a node
|
|
ActionFindValue // Find a value
|
|
)
|
|
|
|
// NewInformationRequest creates a new information request and adds it to the list.
|
|
// It marks the count of nodes as active, meaning the caller should later decrease it via ActiveNodesSub.
|
|
func (dht *DHT) NewInformationRequest(Action int, Key []byte, Nodes []*Node) (ir *InformationRequest) {
|
|
ir = &InformationRequest{
|
|
ResultChan: make(chan *NodeMessage),
|
|
TerminateSignal: make(chan struct{}),
|
|
Action: Action,
|
|
Key: Key,
|
|
Nodes: Nodes,
|
|
ActiveNodes: uint64(len(Nodes)),
|
|
}
|
|
|
|
for _, node := range Nodes {
|
|
dht.irAdd(node.ID, ir)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// CollectResults collects all information request responses within the given timeout.
|
|
func (ir *InformationRequest) CollectResults(timeout time.Duration) (results []*NodeMessage) {
|
|
for {
|
|
select {
|
|
case result := <-ir.ResultChan:
|
|
results = append(results, result)
|
|
|
|
case <-time.After(timeout):
|
|
ir.Terminate()
|
|
return
|
|
|
|
case <-ir.TerminateSignal:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Terminate sends the termination signal to all workers. It is safe to call Terminate multiple times.
|
|
func (ir *InformationRequest) Terminate() {
|
|
ir.Lock()
|
|
defer ir.Unlock()
|
|
|
|
if ir.IsTerminated {
|
|
return
|
|
}
|
|
|
|
// set the termination signal
|
|
ir.IsTerminated = true
|
|
close(ir.TerminateSignal) // safety guaranteed via lock
|
|
|
|
close(ir.ResultChan)
|
|
}
|
|
|
|
// ActiveNodesAdd increases the number of active nodes handling this request
|
|
func (ir *InformationRequest) ActiveNodesAdd(count uint64) {
|
|
atomic.AddUint64(&ir.ActiveNodes, count)
|
|
}
|
|
|
|
// ActiveNodesSub decreases the number of active nodes handling this request
|
|
func (ir *InformationRequest) ActiveNodesSub(count uint64) {
|
|
if atomic.AddUint64(&ir.ActiveNodes, ^uint64(count-1)) <= 0 {
|
|
// If the counter reaches 0, it means no nodes are handling this request anymore -> terminate it.
|
|
ir.Terminate()
|
|
}
|
|
}
|
|
|
|
// ---- keep track of information requests ----
|
|
|
|
// irRemove add the information request to the list
|
|
// If a request to the same node with the same key exists, it is overwritten. This will be improved. TODO: A nonce could easily fix that?
|
|
func (dht *DHT) irAdd(nodeID []byte, request *InformationRequest) {
|
|
dht.listIRmutex.Lock()
|
|
defer dht.listIRmutex.Unlock()
|
|
|
|
// The list only supports one request per target node and key. This should be improved.
|
|
lookupKey := string(nodeID) + string(request.Key)
|
|
dht.ListIR[lookupKey] = request
|
|
|
|
// auto remove from list upon termination
|
|
go func(lookupKey string, terminateChan <-chan struct{}) {
|
|
<-terminateChan
|
|
dht.irRemove(lookupKey)
|
|
}(lookupKey, request.TerminateSignal)
|
|
}
|
|
|
|
// irRemove removes the information request from the list
|
|
func (dht *DHT) irRemove(lookupKey string) {
|
|
dht.listIRmutex.Lock()
|
|
defer dht.listIRmutex.Unlock()
|
|
|
|
delete(dht.ListIR, lookupKey)
|
|
}
|
|
|
|
// IRLookup looks up the information request based on the peers public key and hash
|
|
func (dht *DHT) IRLookup(nodeID []byte, hash []byte) (request *InformationRequest) {
|
|
dht.listIRmutex.RLock()
|
|
defer dht.listIRmutex.RUnlock()
|
|
|
|
lookupKey := string(nodeID) + string(request.Key)
|
|
request = dht.ListIR[lookupKey]
|
|
|
|
return request
|
|
}
|