-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathatom.go
521 lines (433 loc) · 15.2 KB
/
atom.go
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
// Package atom provides simple wrappers around types enforcing atomic usage
//
// The wrapper types do not introduce any size overhead and have the same size
// as the wrapped type.
package atom
import (
"errors"
"math"
"sync/atomic"
"time"
)
// noCopy may be embedded into structs which must not be copied
// after the first use.
//
// See https://github.com/golang/go/issues/8005#issuecomment-190753527
// for details.
type noCopy struct{}
// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {}
// Bool is a wrapper around uint32 for usage as a boolean value with
// atomic access.
type Bool struct {
_ noCopy
value uint32
}
// CompareAndSwap atomically sets the new value only if the current value
// matches the given old value and returns whether the new value was set.
func (b *Bool) CompareAndSwap(old, new bool) (swapped bool) {
var uold, unew uint32
if old {
uold = 1
}
if new {
unew = 1
}
return atomic.CompareAndSwapUint32(&b.value, uold, unew)
}
// Set sets the new value regardless of the previous value.
func (b *Bool) Set(value bool) {
if value {
atomic.StoreUint32(&b.value, 1)
} else {
atomic.StoreUint32(&b.value, 0)
}
}
// Swap atomically sets the new value and returns the previous value.
func (b *Bool) Swap(new bool) (old bool) {
if new {
return atomic.SwapUint32(&b.value, 1) > 0
}
return atomic.SwapUint32(&b.value, 0) > 0
}
// Value returns the current value.
func (b *Bool) Value() (value bool) {
return atomic.LoadUint32(&b.value) > 0
}
// Duration is a wrapper for atomically accessed time.Duration values.
type Duration struct {
_ noCopy
value int64
}
// Add atomically adds delta to the current value and returns the new value.
// No arithmetic overflow checks are applied.
func (d *Duration) Add(delta time.Duration) (new time.Duration) {
return time.Duration(atomic.AddInt64(&d.value, int64(delta)))
}
// CompareAndSwap atomically sets the new value only if the current value
// matches the given old value and returns whether the new value was set.
func (d *Duration) CompareAndSwap(old, new time.Duration) (swapped bool) {
return atomic.CompareAndSwapInt64(&d.value, int64(old), int64(new))
}
// Set sets the new value regardless of the previous value.
func (d *Duration) Set(value time.Duration) {
atomic.StoreInt64(&d.value, int64(value))
}
// Sub atomically subtracts delta to the current value and returns the new value.
// No arithmetic underflow checks are applied.
func (d *Duration) Sub(delta time.Duration) (new time.Duration) {
return time.Duration(atomic.AddInt64(&d.value, -int64(delta)))
}
// Swap atomically sets the new value and returns the previous value.
func (d *Duration) Swap(new time.Duration) (old time.Duration) {
return time.Duration(atomic.SwapInt64(&d.value, int64(new)))
}
// Value returns the current value.
func (d *Duration) Value() (value time.Duration) {
return time.Duration(atomic.LoadInt64(&d.value))
}
// errNil is a special error signaling a nil value.
var errNil = errors.New("nil")
// Error is a wrapper for atomically accessed error values
type Error struct {
_ noCopy
value atomic.Value
}
// Set sets the new value regardless of the previous value.
// The value may be nil.
func (e *Error) Set(value error) {
// Setting atomic.Value to nil is not allowed.
// Use the special error errNil instead to signal a nil value.
if value == nil {
value = errNil
}
e.value.Store(value)
}
// Value returns the current error value.
func (e *Error) Value() (value error) {
v := e.value.Load()
if v == nil || v == errNil {
return nil
}
return v.(error)
}
// Float32 is a wrapper for atomically accessed float32 values.
type Float32 struct {
_ noCopy
value uint32
}
// Add adds delta to the current value and returns the new value.
// Note: Internally this performs a CompareAndSwap operation within a loop.
func (f *Float32) Add(delta float32) (new float32) {
for {
old := f.Value()
new := old + delta
if f.CompareAndSwap(old, new) {
return new
}
}
}
// CompareAndSwap atomically sets the new value only if the current value
// matches the given old value and returns whether the new value was set.
func (f *Float32) CompareAndSwap(old, new float32) (swapped bool) {
return atomic.CompareAndSwapUint32(&f.value, math.Float32bits(old), math.Float32bits(new))
}
// Set sets the new value regardless of the previous value.
func (f *Float32) Set(value float32) {
atomic.StoreUint32(&f.value, math.Float32bits(value))
}
// Sub atomically subtracts delta to the current value and returns the new value.
func (f *Float32) Sub(delta float32) (new float32) {
return f.Add(-delta)
}
// Swap atomically sets the new value and returns the previous value.
func (f *Float32) Swap(new float32) (old float32) {
return math.Float32frombits(atomic.SwapUint32(&f.value, math.Float32bits(new)))
}
// Value returns the current value.
func (f *Float32) Value() (value float32) {
return math.Float32frombits(atomic.LoadUint32(&f.value))
}
// Float64 is a wrapper for atomically accessed float64 values.
type Float64 struct {
_ noCopy
value uint64
}
// Add adds delta to the current value and returns the new value.
// Note: Internally this performs a CompareAndSwap operation within a loop.
func (f *Float64) Add(delta float64) (new float64) {
for {
old := f.Value()
new := old + delta
if f.CompareAndSwap(old, new) {
return new
}
}
}
// CompareAndSwap atomically sets the new value only if the current value
// matches the given old value and returns whether the new value was set.
func (f *Float64) CompareAndSwap(old, new float64) (swapped bool) {
return atomic.CompareAndSwapUint64(&f.value, math.Float64bits(old), math.Float64bits(new))
}
// Set sets the new value regardless of the previous value.
func (f *Float64) Set(value float64) {
atomic.StoreUint64(&f.value, math.Float64bits(value))
}
// Sub atomically subtracts delta to the current value and returns the new value.
func (f *Float64) Sub(delta float64) (new float64) {
return f.Add(-delta)
}
// Swap atomically sets the new value and returns the previous value.
func (f *Float64) Swap(new float64) (old float64) {
return math.Float64frombits(atomic.SwapUint64(&f.value, math.Float64bits(new)))
}
// Value returns the current value.
func (f *Float64) Value() (value float64) {
return math.Float64frombits(atomic.LoadUint64(&f.value))
}
// Int is a wrapper for atomically accessed int values.
type Int struct {
_ noCopy
value uintptr
}
// Add atomically adds delta to the current value and returns the new value.
func (i *Int) Add(delta int) (new int) {
return int(atomic.AddUintptr(&i.value, uintptr(delta)))
}
// CompareAndSwap atomically sets the new value only if the current value
// matches the given old value and returns whether the new value was set.
func (i *Int) CompareAndSwap(old, new int) (swapped bool) {
return atomic.CompareAndSwapUintptr(&i.value, uintptr(old), uintptr(new))
}
// Set sets the new value regardless of the previous value.
func (i *Int) Set(value int) {
atomic.StoreUintptr(&i.value, uintptr(value))
}
// Sub atomically subtracts delta to the current value and returns the new value.
func (i *Int) Sub(delta int) (new int) {
return i.Add(-delta)
}
// Swap atomically sets the new value and returns the previous value.
func (i *Int) Swap(new int) (old int) {
return int(atomic.SwapUintptr(&i.value, uintptr(new)))
}
// Value returns the current value.
func (i *Int) Value() (value int) {
return int(atomic.LoadUintptr(&i.value))
}
// Int32 is a wrapper for atomically accessed int32 values.
type Int32 struct {
_ noCopy
value int32
}
// Add atomically adds delta to the current value and returns the new value.
func (i *Int32) Add(delta int32) (new int32) {
return atomic.AddInt32(&i.value, delta)
}
// CompareAndSwap atomically sets the new value only if the current value
// matches the given old value and returns whether the new value was set.
func (i *Int32) CompareAndSwap(old, new int32) (swapped bool) {
return atomic.CompareAndSwapInt32(&i.value, old, new)
}
// Set sets the new value regardless of the previous value.
func (i *Int32) Set(value int32) {
atomic.StoreInt32(&i.value, value)
}
// Sub atomically subtracts delta to the current value and returns the new value.
func (i *Int32) Sub(delta int32) (new int32) {
return i.Add(-delta)
}
// Swap atomically sets the new value and returns the previous value.
func (i *Int32) Swap(new int32) (old int32) {
return atomic.SwapInt32(&i.value, new)
}
// Value returns the current value.
func (i *Int32) Value() (value int32) {
return atomic.LoadInt32(&i.value)
}
// Int64 is a wrapper for atomically accessed int64 values.
type Int64 struct {
_ noCopy
value int64
}
// Add atomically adds delta to the current value and returns the new value.
func (i *Int64) Add(delta int64) (new int64) {
return atomic.AddInt64(&i.value, delta)
}
// CompareAndSwap atomically sets the new value only if the current value
// matches the given old value and returns whether the new value was set.
func (i *Int64) CompareAndSwap(old, new int64) (swapped bool) {
return atomic.CompareAndSwapInt64(&i.value, old, new)
}
// Set sets the new value regardless of the previous value.
func (i *Int64) Set(value int64) {
atomic.StoreInt64(&i.value, value)
}
// Sub atomically subtracts delta to the current value and returns the new value.
func (i *Int64) Sub(delta int64) (new int64) {
return i.Add(-delta)
}
// Swap atomically sets the new value and returns the previous value.
func (i *Int64) Swap(new int64) (old int64) {
return atomic.SwapInt64(&i.value, new)
}
// Value returns the current value.
func (i *Int64) Value() (value int64) {
return atomic.LoadInt64(&i.value)
}
// String is a wrapper for atomically accessed string values.
// Note: The string value is wrapped in an interface. Thus, this wrapper has
// a memory overhead.
type String struct {
_ noCopy
value atomic.Value
}
// Set sets the new value regardless of the previous value.
// Note: Set requires an allocation as the value is wrapped in an interface.
func (s *String) Set(value string) {
s.value.Store(value)
}
// Value returns the current error value.
func (s *String) Value() (value string) {
v := s.value.Load()
if v == nil {
return ""
}
return v.(string)
}
// Uint is a wrapper for atomically accessed uint values.
type Uint struct {
_ noCopy
value uintptr
}
// Add atomically adds delta to the current value and returns the new value.
func (u *Uint) Add(delta uint) (new uint) {
return uint(atomic.AddUintptr(&u.value, uintptr(delta)))
}
// CompareAndSwap atomically sets the new value only if the current value
// matches the given old value and returns whether the new value was set.
func (u *Uint) CompareAndSwap(old, new uint) (swapped bool) {
return atomic.CompareAndSwapUintptr(&u.value, uintptr(old), uintptr(new))
}
// Set sets the new value regardless of the previous value.
func (u *Uint) Set(value uint) {
atomic.StoreUintptr(&u.value, uintptr(value))
}
// Sub atomically subtracts delta to the current value and returns the new value.
func (u *Uint) Sub(delta uint) (new uint) {
return u.Add(^(delta - 1))
}
// Swap atomically sets the new value and returns the previous value.
func (u *Uint) Swap(new uint) (old uint) {
return uint(atomic.SwapUintptr(&u.value, uintptr(new)))
}
// Value returns the current value.
func (u *Uint) Value() (value uint) {
return uint(atomic.LoadUintptr(&u.value))
}
// Uint32 is a wrapper for atomically accessed uint32 values.
type Uint32 struct {
_ noCopy
value uint32
}
// Add atomically adds delta to the current value and returns the new value.
func (u *Uint32) Add(delta uint32) (new uint32) {
return atomic.AddUint32(&u.value, delta)
}
// CompareAndSwap atomically sets the new value only if the current value
// matches the given old value and returns whether the new value was set.
func (u *Uint32) CompareAndSwap(old, new uint32) (swapped bool) {
return atomic.CompareAndSwapUint32(&u.value, old, new)
}
// Set sets the new value regardless of the previous value.
func (u *Uint32) Set(value uint32) {
atomic.StoreUint32(&u.value, value)
}
// Sub atomically subtracts delta to the current value and returns the new value.
func (u *Uint32) Sub(delta uint32) (new uint32) {
return u.Add(^(delta - 1))
}
// Swap atomically sets the new value and returns the previous value.
func (u *Uint32) Swap(new uint32) (old uint32) {
return atomic.SwapUint32(&u.value, new)
}
// Value returns the current value.
func (u *Uint32) Value() (value uint32) {
return atomic.LoadUint32(&u.value)
}
// Uint64 is a wrapper for atomically accessed uint64 values.
type Uint64 struct {
_ noCopy
value uint64
}
// Add atomically adds delta to the current value and returns the new value.
func (u *Uint64) Add(delta uint64) (new uint64) {
return atomic.AddUint64(&u.value, delta)
}
// CompareAndSwap atomically sets the new value only if the current value
// matches the given old value and returns whether the new value was set.
func (u *Uint64) CompareAndSwap(old, new uint64) (swapped bool) {
return atomic.CompareAndSwapUint64(&u.value, old, new)
}
// Set sets the new value regardless of the previous value.
func (u *Uint64) Set(value uint64) {
atomic.StoreUint64(&u.value, value)
}
// Sub atomically subtracts delta to the current value and returns the new value.
func (u *Uint64) Sub(delta uint64) (new uint64) {
return u.Add(^(delta - 1))
}
// Swap atomically sets the new value and returns the previous value.
func (u *Uint64) Swap(new uint64) (old uint64) {
return atomic.SwapUint64(&u.value, new)
}
// Value returns the current value.
func (u *Uint64) Value() (value uint64) {
return atomic.LoadUint64(&u.value)
}
// Uintptr is a wrapper for atomically accessed uintptr values.
type Uintptr struct {
_ noCopy
value uintptr
}
// Add atomically adds delta to the current value and returns the new value.
func (u *Uintptr) Add(delta uintptr) (new uintptr) {
return atomic.AddUintptr(&u.value, delta)
}
// CompareAndSwap atomically sets the new value only if the current value
// matches the given old value and returns whether the new value was set.
func (u *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) {
return atomic.CompareAndSwapUintptr(&u.value, old, new)
}
// Set sets the new value regardless of the previous value.
func (u *Uintptr) Set(value uintptr) {
atomic.StoreUintptr(&u.value, value)
}
// Sub atomically subtracts delta to the current value and returns the new value.
func (u *Uintptr) Sub(delta uintptr) (new uintptr) {
return u.Add(^(delta - 1))
}
// Swap atomically sets the new value and returns the previous value.
func (u *Uintptr) Swap(new uintptr) (old uintptr) {
return atomic.SwapUintptr(&u.value, new)
}
// Value returns the current value.
func (u *Uintptr) Value() (value uintptr) {
return atomic.LoadUintptr(&u.value)
}
// Value is a wrapper for atomically accessed consistently typed values.
type Value struct {
_ noCopy
value atomic.Value
}
// Set sets the new value regardless of the previous value.
// All calls to Set for a given Value must use values of the same concrete type.
// Set of an inconsistent type panics, as does Set(nil).
func (v *Value) Set(value interface{}) {
v.value.Store(value)
}
// Value returns the current value.
// It returns nil if there has been no call to Set for this Value.
func (v *Value) Value() (value interface{}) {
return v.value.Load()
}