// reserveHashes reserves a set of hashes for the given peer, skipping previously // failed ones. // // Note, this method expects the queue lock to be already held for writing. The // reason the lock is not obtained in here is because the parameters already need // to access the queue, so they already need a lock anyway. func (q *queue) reserveHashes(p *peer, count int, taskQueue *prque.Prque, taskGen func(int), pendPool map[string]*fetchRequest, maxPending int) *fetchRequest { // Short circuit if the peer's already downloading something (sanity check to // not corrupt state) if _, ok := pendPool[p.id]; ok { return nil } // Calculate an upper limit on the hashes we might fetch (i.e. throttling) allowance := maxPending if allowance > 0 { for _, request := range pendPool { allowance -= len(request.Hashes) } } // If there's a task generator, ask it to fill our task queue if taskGen != nil && taskQueue.Size() < allowance { taskGen(allowance - taskQueue.Size()) } if taskQueue.Empty() { return nil } // Retrieve a batch of hashes, skipping previously failed ones send := make(map[common.Hash]int) skip := make(map[common.Hash]int) for proc := 0; (allowance == 0 || proc < allowance) && len(send) < count && !taskQueue.Empty(); proc++ { hash, priority := taskQueue.Pop() if p.Lacks(hash.(common.Hash)) { skip[hash.(common.Hash)] = int(priority) } else { send[hash.(common.Hash)] = int(priority) } } // Merge all the skipped hashes back for hash, index := range skip { taskQueue.Push(hash, float32(index)) } // Assemble and return the block download request if len(send) == 0 { return nil } request := &fetchRequest{ Peer: p, Hashes: send, Time: time.Now(), } pendPool[p.id] = request return request }
// reserveHeaders reserves a set of data download operations for a given peer, // skipping any previously failed ones. This method is a generic version used // by the individual special reservation functions. // // Note, this method expects the queue lock to be already held for writing. The // reason the lock is not obtained in here is because the parameters already need // to access the queue, so they already need a lock anyway. func (q *queue) reserveHeaders(p *peer, count int, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque, pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}, isNoop func(*types.Header) bool) (*fetchRequest, bool, error) { // Short circuit if the pool has been depleted, or if the peer's already // downloading something (sanity check not to corrupt state) if taskQueue.Empty() { return nil, false, nil } if _, ok := pendPool[p.id]; ok { return nil, false, nil } // Calculate an upper limit on the items we might fetch (i.e. throttling) space := len(q.resultCache) - len(donePool) for _, request := range pendPool { space -= len(request.Headers) } // Retrieve a batch of tasks, skipping previously failed ones send := make([]*types.Header, 0, count) skip := make([]*types.Header, 0) progress := false for proc := 0; proc < space && len(send) < count && !taskQueue.Empty(); proc++ { header := taskQueue.PopItem().(*types.Header) // If we're the first to request this task, initialise the result container index := int(header.Number.Int64() - int64(q.resultOffset)) if index >= len(q.resultCache) || index < 0 { common.Report("index allocation went beyond available resultCache space") return nil, false, errInvalidChain } if q.resultCache[index] == nil { components := 1 if q.mode == FastSync && header.Number.Uint64() <= q.fastSyncPivot { components = 2 } q.resultCache[index] = &fetchResult{ Pending: components, Header: header, } } // If this fetch task is a noop, skip this fetch operation if isNoop(header) { donePool[header.Hash()] = struct{}{} delete(taskPool, header.Hash()) space, proc = space-1, proc-1 q.resultCache[index].Pending-- progress = true continue } // Otherwise unless the peer is known not to have the data, add to the retrieve list if p.Lacks(header.Hash()) { skip = append(skip, header) } else { send = append(send, header) } } // Merge all the skipped headers back for _, header := range skip { taskQueue.Push(header, -float32(header.Number.Uint64())) } if progress { // Wake WaitResults, resultCache was modified q.active.Signal() } // Assemble and return the block download request if len(send) == 0 { return nil, progress, nil } request := &fetchRequest{ Peer: p, Headers: send, Time: time.Now(), } pendPool[p.id] = request return request, progress, nil }