mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-17 02:47:51 +01:00
90 lines
2.7 KiB
Go
90 lines
2.7 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)),
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
// Done is called when a remote node is done.
|
|
func (ir *InformationRequest) Done() {
|
|
if atomic.AddUint64(&ir.ActiveNodes, ^uint64(0)) <= 0 {
|
|
// If the counter reaches 0, it means no nodes are handling this request anymore -> terminate it.
|
|
ir.Terminate()
|
|
}
|
|
}
|