2018-03-24 00:18:00 +01:00
|
|
|
package dht
|
|
|
|
|
|
|
|
import (
|
2018-03-29 03:05:27 +02:00
|
|
|
"context"
|
2018-03-24 00:18:00 +01:00
|
|
|
"net"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
// query represents the query data included queried node and query-formed data.
|
|
|
|
type transaction struct {
|
2018-03-29 03:05:27 +02:00
|
|
|
node Node
|
2018-03-24 00:18:00 +01:00
|
|
|
req *Request
|
|
|
|
res chan *Response
|
|
|
|
}
|
|
|
|
|
|
|
|
// transactionManager represents the manager of transactions.
|
|
|
|
type transactionManager struct {
|
|
|
|
lock *sync.RWMutex
|
|
|
|
transactions map[string]*transaction
|
|
|
|
dht *DHT
|
|
|
|
}
|
|
|
|
|
|
|
|
// newTransactionManager returns new transactionManager pointer.
|
|
|
|
func newTransactionManager(dht *DHT) *transactionManager {
|
|
|
|
return &transactionManager{
|
|
|
|
lock: &sync.RWMutex{},
|
|
|
|
transactions: make(map[string]*transaction),
|
|
|
|
dht: dht,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// insert adds a transaction to transactionManager.
|
|
|
|
func (tm *transactionManager) insert(trans *transaction) {
|
|
|
|
tm.lock.Lock()
|
|
|
|
defer tm.lock.Unlock()
|
|
|
|
tm.transactions[trans.req.ID] = trans
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete removes a transaction from transactionManager.
|
|
|
|
func (tm *transactionManager) delete(transID string) {
|
|
|
|
tm.lock.Lock()
|
|
|
|
defer tm.lock.Unlock()
|
|
|
|
delete(tm.transactions, transID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// find transaction for id. optionally ensure that addr matches node from transaction
|
|
|
|
func (tm *transactionManager) Find(id string, addr *net.UDPAddr) *transaction {
|
|
|
|
tm.lock.RLock()
|
|
|
|
defer tm.lock.RUnlock()
|
|
|
|
|
|
|
|
t, ok := tm.transactions[id]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
} else if addr != nil && t.node.Addr().String() != addr.String() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2018-03-29 03:05:27 +02:00
|
|
|
func (tm *transactionManager) SendAsync(ctx context.Context, node Node, req *Request) <-chan *Response {
|
2018-03-24 00:18:00 +01:00
|
|
|
if node.id.Equals(tm.dht.node.id) {
|
|
|
|
log.Error("sending query to self")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-03-29 03:05:27 +02:00
|
|
|
ch := make(chan *Response, 1)
|
2018-03-24 00:18:00 +01:00
|
|
|
|
2018-03-29 03:05:27 +02:00
|
|
|
go func() {
|
|
|
|
defer close(ch)
|
2018-03-24 00:18:00 +01:00
|
|
|
|
2018-03-29 03:05:27 +02:00
|
|
|
req.ID = newMessageID()
|
|
|
|
req.NodeID = tm.dht.node.id.RawString()
|
|
|
|
trans := &transaction{
|
|
|
|
node: node,
|
|
|
|
req: req,
|
|
|
|
res: make(chan *Response),
|
2018-03-24 00:18:00 +01:00
|
|
|
}
|
|
|
|
|
2018-03-29 03:05:27 +02:00
|
|
|
tm.insert(trans)
|
|
|
|
defer tm.delete(trans.req.ID)
|
|
|
|
|
|
|
|
for i := 0; i < udpRetry; i++ {
|
|
|
|
if err := send(tm.dht, trans.node.Addr(), *trans.req); err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
continue // try again? return?
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case res := <-trans.res:
|
|
|
|
ch <- res
|
|
|
|
return
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
case <-time.After(udpTimeout):
|
|
|
|
}
|
2018-03-24 00:18:00 +01:00
|
|
|
}
|
|
|
|
|
2018-03-29 03:05:27 +02:00
|
|
|
// if request timed out each time
|
|
|
|
tm.dht.rt.RemoveByID(trans.node.id)
|
|
|
|
}()
|
|
|
|
|
|
|
|
return ch
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tm *transactionManager) Send(node Node, req *Request) *Response {
|
|
|
|
return <-tm.SendAsync(context.Background(), node, req)
|
2018-03-24 00:18:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Count returns the number of transactions in the manager
|
|
|
|
func (tm *transactionManager) Count() int {
|
|
|
|
tm.lock.Lock()
|
|
|
|
defer tm.lock.Unlock()
|
|
|
|
return len(tm.transactions)
|
|
|
|
}
|