forked from hyperledger/fabric-sdk-go
/
futurevalue.go
102 lines (86 loc) · 2.24 KB
/
futurevalue.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
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package futurevalue
import (
"fmt"
"sync"
"sync/atomic"
"unsafe"
)
// Initializer initializes the value
type Initializer func() (interface{}, error)
// valueHolder holds the actual value
type valueHolder struct {
value interface{}
err error
}
// Value implements a Future Value in which a reference is initialized once
// (and only once) using the Initialize function. Only one Go routine can call
// Initialize whereas multiple Go routines may invoke Get, and will wait
// until the reference has been initialized.
// Regardless of whether Initialize returns success or error,
// the value cannot be initialized again.
type Value struct {
sync.RWMutex
ref unsafe.Pointer
initializer Initializer
}
// New returns a new future value
func New(initializer Initializer) *Value {
f := &Value{
initializer: initializer,
}
f.Lock()
return f
}
// Initialize initializes the future value.
// This function must be called only once. Subsequent
// calls may result in deadlock.
func (f *Value) Initialize() (interface{}, error) {
value, err := f.initializer()
f.set(value, err)
f.Unlock()
return value, err
}
// Get returns the value and/or error that occurred during initialization.
func (f *Value) Get() (interface{}, error) {
// Try outside of a lock
if ok, value, err := f.get(); ok {
return value, err
}
f.RLock()
defer f.RUnlock()
_, value, err := f.get()
return value, err
}
// MustGet returns the value. If an error resulted
// during initialization then this function will panic.
func (f *Value) MustGet() interface{} {
value, err := f.Get()
if err != nil {
panic(fmt.Sprintf("get returned error: %s", err))
}
return value
}
// IsSet returns true if the value has been set, otherwise false is returned
func (f *Value) IsSet() bool {
isSet, _, _ := f.get()
return isSet
}
func (f *Value) get() (bool, interface{}, error) {
p := atomic.LoadPointer(&f.ref)
if p == nil {
return false, nil, nil
}
holder := (*valueHolder)(p)
return true, holder.value, holder.err
}
func (f *Value) set(value interface{}, err error) {
holder := &valueHolder{
value: value,
err: err,
}
atomic.StorePointer(&f.ref, unsafe.Pointer(holder))
}