-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlocal.go
More file actions
124 lines (99 loc) · 2.91 KB
/
local.go
File metadata and controls
124 lines (99 loc) · 2.91 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
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package serialkey
import (
"context"
"sync"
"sync/atomic"
)
// NewLocal returns the serialkeys keychain based on the memory of the host
// where the module is running.
func NewLocal(opts ...LocalOption) *Local {
var cfg LocalConfiguration
for _, opt := range opts {
opt(&cfg)
}
return &Local{
start: cfg.start,
table: make(map[string]*int64),
}
}
// Local is the serialkeys keychain based on the local memory.
type Local struct {
sync.RWMutex
start int64
table map[string]*int64
}
// Next for the passed key name returns an value guaranteed to be greater
// than the value returned for the same key name passed at the time
// of previous call of the next method or the forward method.
// The next method is thread safe.
func (chain *Local) Next(_ context.Context, key string) (int64, error) {
chain.RLock()
if value, ok := chain.table[key]; ok {
i := atomic.AddInt64((*int64)(value), 1)
chain.RUnlock()
return i, nil
}
chain.RUnlock()
chain.Lock()
defer chain.Unlock()
if value, ok := chain.table[key]; ok {
return atomic.AddInt64((*int64)(value), 1), nil
}
i := chain.start
chain.table[key] = &i
return i, nil
}
// Last for the passed key name returns the value returned for
// the same key name passed at the time of previous call
// of the next method or the forward method.
// The last method is thread safe.
func (chain *Local) Last(_ context.Context, key string) (int64, error) {
chain.RLock()
defer chain.RUnlock()
if value, ok := chain.table[key]; ok {
return atomic.LoadInt64((*int64)(value)), nil
}
return chain.start - 1, nil
}
// Forward for the passed key name returns an value guaranteed
// to be greater or equal to the target value and guaranteed to be greater
// than the value returned for the same key name passed at the time
// of previous call of the forward method or the next method.
// Forward method is thread safe.
func (chain *Local) Forward(_ context.Context, key string, target int64) (int64, error) {
chain.RLock()
if value, ok := chain.table[key]; ok {
if i := atomic.LoadInt64((*int64)(value)); i >= target {
chain.RUnlock()
return i, nil
}
}
chain.RUnlock()
chain.Lock()
defer chain.Unlock()
if value, ok := chain.table[key]; ok {
if i := atomic.LoadInt64((*int64)(value)); i >= target {
return i, nil
}
}
chain.table[key] = &target
return target, nil
}
// Close do nothing.
// The close method is thread safe.
func (*Local) Close() error {
return nil
}
// LocalOption changes configuration.
type LocalOption func(*LocalConfiguration)
// LocalConfiguration holds values changeable by options.
type LocalConfiguration struct {
start int64
}
// LocalWithStart sets the start number.
func LocalWithStart(start int64) LocalOption {
return func(cfg *LocalConfiguration) { cfg.start = start }
}