-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathnoncecache.go
More file actions
139 lines (126 loc) · 3.06 KB
/
noncecache.go
File metadata and controls
139 lines (126 loc) · 3.06 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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright 2017-2019 Tensigma Ltd. All rights reserved.
// Use of this source code is governed by Microsoft Reference Source
// License (MS-RSL) that can be found in the LICENSE file.
package ethfw
import (
"sync"
"github.com/ethereum/go-ethereum/common"
)
type NonceCache interface {
Serialize(account common.Address, fn func() error) error
Sync(account common.Address, syncFn func() (uint64, error))
Set(account common.Address, nonce uint64)
Get(account common.Address) uint64
Incr(account common.Address) uint64
Decr(account common.Address) uint64
}
func NewNonceCache() NonceCache {
return &nonceCache{
mux: new(sync.RWMutex),
nonces: make(map[common.Address]uint64),
locks: make(map[common.Address]*sync.RWMutex),
guard: NewUniquify(),
}
}
type nonceCache struct {
mux *sync.RWMutex
nonces map[common.Address]uint64
locks map[common.Address]*sync.RWMutex
guard Uniquify
}
// Serialize serializes access to the nonce cache for all goroutines, all nonce increments should be done
// in this context. If a transaction increments nonce, but has not been submitted,
// it will have exclusive right to decrease nonce back for other transactions.
func (n nonceCache) Serialize(account common.Address, fn func() error) error {
return n.guard.Call(account.Hex(), fn)
}
func (n nonceCache) Get(account common.Address) uint64 {
n.mux.RLock()
lock, ok := n.locks[account]
if !ok {
n.mux.RUnlock()
return 0
}
lock.RLock()
n.mux.RUnlock()
nonce := n.nonces[account]
lock.RUnlock()
return nonce
}
func (n nonceCache) Set(account common.Address, nonce uint64) {
n.mux.Lock()
lock, ok := n.locks[account]
if !ok {
n.locks[account] = new(sync.RWMutex)
n.nonces[account] = nonce
n.mux.Unlock()
return
}
lock.Lock()
n.mux.Unlock()
n.nonces[account] = nonce
lock.Unlock()
}
func (n nonceCache) Incr(account common.Address) uint64 {
n.mux.Lock()
lock, ok := n.locks[account]
if !ok {
n.nonces[account] = 1
n.locks[account] = new(sync.RWMutex)
n.mux.Unlock()
return 0
}
lock.Lock()
n.mux.Unlock()
nonce := n.nonces[account]
n.nonces[account]++
lock.Unlock()
return nonce
}
func (n nonceCache) Decr(account common.Address) uint64 {
n.mux.Lock()
lock, ok := n.locks[account]
if !ok {
n.nonces[account] = 0
n.locks[account] = new(sync.RWMutex)
n.mux.Unlock()
return 0
}
lock.Lock()
n.mux.Unlock()
nonce := n.nonces[account]
n.nonces[account]--
lock.Unlock()
return nonce
}
func (n nonceCache) Sync(account common.Address, syncFn func() (uint64, error)) {
n.mux.Lock()
prevNonce, prevOk := n.nonces[account]
lock, ok := n.locks[account]
if !ok {
n.nonces[account] = 0
n.locks[account] = new(sync.RWMutex)
lock = n.locks[account]
}
lock.Lock()
n.mux.Unlock()
{
n.mux.RLock()
nextNonce, nextOk := n.nonces[account]
n.mux.RUnlock()
if !prevOk && nextOk {
// we're not fist here to sync - skip
lock.Unlock()
return
} else if nextNonce != prevNonce {
lock.Unlock()
return
}
if nonce, err := syncFn(); err == nil {
n.mux.Lock()
n.nonces[account] = nonce
n.mux.Unlock()
}
}
lock.Unlock()
}