7
7
"debug/elf"
8
8
"errors"
9
9
"fmt"
10
+ "log/slog"
11
+ "sync"
12
+ "sync/atomic"
10
13
11
14
"github.com/Masterminds/semver/v3"
12
15
@@ -15,11 +18,26 @@ import (
15
18
16
19
// Info are the details about a target process.
17
20
type Info struct {
18
- ID ID
19
- Functions []* binary.Func
20
- GoVersion * semver.Version
21
- Modules map [string ]* semver.Version
22
- Allocation * Allocation
21
+ ID ID
22
+ Functions []* binary.Func
23
+ GoVersion * semver.Version
24
+ Modules map [string ]* semver.Version
25
+
26
+ allocOnce onceResult [* Allocation ]
27
+ }
28
+
29
+ // Alloc allocates memory for the process described by Info i.
30
+ //
31
+ // The underlying memory allocation is only successfully performed once for the
32
+ // instance i. Meaning, it is safe to call this multiple times. The first
33
+ // successful result will be returned to all subsequent calls. If an error is
34
+ // returned, subsequent calls will re-attempt to perform the allocation.
35
+ //
36
+ // It is safe to call this method concurrently.
37
+ func (i * Info ) Alloc (logger * slog.Logger ) (* Allocation , error ) {
38
+ return i .allocOnce .Do (func () (* Allocation , error ) {
39
+ return allocate (logger , i .ID )
40
+ })
23
41
}
24
42
25
43
// GetFunctionOffset returns the offset for of the function with name.
@@ -104,3 +122,36 @@ func (a *Analyzer) findFunctions(elfF *elf.File, relevantFuncs map[string]interf
104
122
105
123
return result , nil
106
124
}
125
+
126
+ // onceResult is an object that will perform exactly one action if that action
127
+ // does not error. For errors, no state is stored and subsequent attempts will
128
+ // be tried.
129
+ type onceResult [T any ] struct {
130
+ done atomic.Bool
131
+ mutex sync.Mutex
132
+ val T
133
+ }
134
+
135
+ // Do runs f only once, and only stores the result if f returns a nil error.
136
+ // Subsequent calls to Do will return the stored value or they will re-attempt
137
+ // to run f and store the result if an error had been returned.
138
+ func (o * onceResult [T ]) Do (f func () (T , error )) (T , error ) {
139
+ if o .done .Load () {
140
+ o .mutex .Lock ()
141
+ defer o .mutex .Unlock ()
142
+ return o .val , nil
143
+ }
144
+
145
+ o .mutex .Lock ()
146
+ defer o .mutex .Unlock ()
147
+ if o .done .Load () {
148
+ return o .val , nil
149
+ }
150
+
151
+ var err error
152
+ o .val , err = f ()
153
+ if err == nil {
154
+ o .done .Store (true )
155
+ }
156
+ return o .val , err
157
+ }
0 commit comments