forked from cruzbit/cruzbit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathblock_queue.go
More file actions
101 lines (89 loc) · 2.64 KB
/
Copy pathblock_queue.go
File metadata and controls
101 lines (89 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// Copyright 2019 cruzbit developers
// Use of this source code is governed by a MIT-style license that can be found in the LICENSE file.
package cruzbit
import (
"container/list"
"sync"
"time"
)
// BlockQueue is a queue of blocks to download.
type BlockQueue struct {
blockMap map[BlockID]*list.Element
blockQueue *list.List
lock sync.RWMutex
}
// If a block has been in the queue for more than 2 minutes it can be re-added with a new peer responsible for its download.
const maxQueueWait = 2 * time.Minute
type blockQueueEntry struct {
id BlockID
who string
when time.Time
}
// NewBlockQueue returns a new instance of a BlockQueue.
func NewBlockQueue() *BlockQueue {
return &BlockQueue{
blockMap: make(map[BlockID]*list.Element),
blockQueue: list.New(),
}
}
// Add adds the block ID to the back of the queue and records the address of the peer who pushed it if it didn't exist in the queue.
// If it did exist and maxQueueWait has elapsed, the block is left in its position but the peer responsible for download is updated.
func (b *BlockQueue) Add(id BlockID, who string) bool {
b.lock.Lock()
defer b.lock.Unlock()
if e, ok := b.blockMap[id]; ok {
entry := e.Value.(*blockQueueEntry)
if time.Since(entry.when) < maxQueueWait {
// it's still pending download
return false
}
// it's expired. signal that it can be tried again and leave it in place
entry.when = time.Now()
// new peer owns its place in the queue
entry.who = who
return true
}
// add to the back of the queue
entry := &blockQueueEntry{id: id, who: who, when: time.Now()}
e := b.blockQueue.PushBack(entry)
b.blockMap[id] = e
return true
}
// Remove removes the block ID from the queue only if the requester is who is currently responsible for its download.
func (b *BlockQueue) Remove(id BlockID, who string) bool {
b.lock.Lock()
defer b.lock.Unlock()
if e, ok := b.blockMap[id]; ok {
entry := e.Value.(*blockQueueEntry)
if entry.who == who {
b.blockQueue.Remove(e)
delete(b.blockMap, entry.id)
return true
}
}
return false
}
// Exists returns true if the block ID exists in the queue.
func (b *BlockQueue) Exists(id BlockID) bool {
b.lock.RLock()
defer b.lock.RUnlock()
_, ok := b.blockMap[id]
return ok
}
// Peek returns the ID of the block at the front of the queue.
func (b *BlockQueue) Peek() (BlockID, bool) {
b.lock.RLock()
defer b.lock.RUnlock()
if b.blockQueue.Len() == 0 {
return BlockID{}, false
}
e := b.blockQueue.Front()
entry := e.Value.(*blockQueueEntry)
return entry.id, true
}
// Len returns the length of the queue.
func (b *BlockQueue) Len() int {
b.lock.RLock()
defer b.lock.RUnlock()
return b.blockQueue.Len()
}