-
Notifications
You must be signed in to change notification settings - Fork 1
/
if-go.go
345 lines (327 loc) · 14.9 KB
/
if-go.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
/*
© 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
ISC License
*/
package parl
import (
"context"
"fmt"
"time"
"github.com/haraldrudell/parl/pruntime"
)
// Thread interface
// Go provides methods for a running goroutione thread to be provided as a function
// argument in the go statement function call launching the thread.
// - Go.CancelGo affects this Go thread only.
// - Go.Cancel cancels:
// - — this Go thread
// - — this Go’s parent thread-group and
// - — this Go’s parent thread-group’s subordinate thread-groups
// - The Go Context is canceled when
// - — the parent GoGroup thread-group’s context is Canceled or
// - —a thread in the parent GoGroup thread-group initiates Cancel
// - Cancel by threads in sub ordinate thread-groups do not Cancel this Go thread
type Go interface {
// Register performs no function but allows the Go object to collect
// information on the new thread.
// - label is an optional name that can be assigned to a Go goroutine thread
Register(label ...string) (g0 Go)
// AddError emits a non-fatal errors
AddError(err error)
// Go returns a Go object to be provided as a go-statement function-argument
// in a function call invocation launching a new gorotuine thread.
// - the new thread belongs to the same GoGroup thread-group as the Go
// object whose Go method was invoked.
Go() (g0 Go)
// SubGo returns a GoGroup thread-group whose fatal and non-fatel errors go to
// the Go object’s parent thread-group.
// - a SubGo is used to ensure sub-threads exiting prior to their parent thread
// or to facilitate separate cancelation of the threads in the subordinate thread-group.
// - fatal errors from SubGo threads are handled in the same way as those of the
// Go object, typically terminating the application.
// - the SubGo thread-group terminates when both its own threads have exited and
// - the threads of its subordinate thread-groups.
SubGo(onFirstFatal ...GoFatalCallback) (subGo SubGo)
// SubGroup returns a thread-group with its own error channel.
// - a SubGroup is used for threads whose fatal errors should be handled
// in the Go thread.
// - The threads of the Subgroup can be canceled separately.
// - SubGroup’s error channel collects fatal thread terminations
// - the SubGroup’s error channel needs to be read in real-time or after
// SubGroup termination
// - non-fatal errors in SubGroup threads are sent to the Go object’s parent
// similar to the AddError method
// - the SubGroup thread-group terminates when both its own threads have exited and
// - the threads of its subordinate thread-groups.
SubGroup(onFirstFatal ...GoFatalCallback) (subGroup SubGroup)
// Done indicates that this goroutine is exiting
// - err == nil means successful exit
// - non-nil err indicates fatal error
// - deferrable
Done(errp *error)
// Wait awaits exit of this Go thread.
Wait()
WaitCh() (ch AwaitableCh)
// Cancel signals for the threads in this Go thread’s parent GoGroup thread-group
// and any subordinate thread-groups to exit.
Cancel()
// Context will Cancel when the parent thread-group Cancels
// or Cancel is invoked on this Go object.
// - Subordinate thread-groups do not Cancel the context of the Go thread.
Context() (ctx context.Context)
// ThreadInfo returns thread data that is partially or fully populated
// - ThreadID may be invalid: threadID.IsValid.
// - goFunction may be zero-value: goFunction.IsSet
// - those values present after public methods of parl.Go has been invoked by
// the new goroutine
ThreadInfo() (threadData ThreadData)
// values always present
Creator() (threadID ThreadID, createLocation *pruntime.CodeLocation)
// - ThreadID may be invalid: threadID.IsValid.
// - goFunction may be zero-value: goFunction.IsSet
// - those values present after public methods of parl.Go has been invoked by
// the new goroutine
GoRoutine() (threadID ThreadID, goFunction *pruntime.CodeLocation)
// GoID efficiently returns the goroutine ID that may be invalid
// - valid after public methods of parl.Go has been invoked by
// the new goroutine
GoID() (threadID ThreadID)
// EntityID returns a value unique for this Go
// - ordered: usable as map key or for sorting
// - always valid, has .String method
EntityID() (goEntityID GoEntityID)
fmt.Stringer
}
// GoFatalCallback receives the thread-group on its first fatal thread-exit
// - GoFatalCallback is an optional onFirstFatal argument to
// - — NewGoGroup
// - — SubGo
// - — SubGroup
type GoFatalCallback func(goGen GoGen)
// GoGen allows for new Go threads, new SubGo and SubGroup thread-groups and
// cancel of threads in the thread-group and its subordinate thread-groups.
// - GoGen is value from NewGoGroup GoGroup SubGo SubGroup Go,
// ie. any Go-interface object
type GoGen interface {
// Go returns a Go object to be provided as a go statement function argument.
Go() (g0 Go)
// SubGo returns a thread-group whose fatal errors go to GoGen’s parent.
// - both non-fatal and fatal errors in SubGo threads are sent to GoGen’s parent
// like Go.AddError and Go.Done.
// - therefore, when a SubGo thread fails, the application will typically exit.
// - by awaiting SubGo, Go can delay its exit until SubGo has terminated
// - the SubGo thread-group terminates when the its thread exits
SubGo(onFirstFatal ...GoFatalCallback) (subGo SubGo)
// SubGroup creates a sub-ordinate thread-group
SubGroup(onFirstFatal ...GoFatalCallback) (subGroup SubGroup)
// Cancel terminates the threads in the Go consumer thread-group.
Cancel()
// Context will Cancel when the parent thread-group Cancels.
// Subordinate thread-groups do not Cancel this context.
Context() (ctx context.Context)
}
// Thread Group interfaces and Factory
// GoGroup manages a thread-group.
// - A thread from this thread-group will terminate all threads in this
// and subordinate thread-groups if this thread-group was provided
// the FirstFailTerminates option, which is default.
// - A fatal thread-termination in a sub thread-group only affects this
// thread-group if the sub thread-group was provided a nil fatal function,
// the FirstFailTerminates option, which is default, and no explicit
// FailChannel option.
// - Fatal thread terminations will propagate to parent thread-groups if
// this thread group did not have a fatal function provided and was not
// explicitly provided the FailChannel option.
// - A Cancel in this thread-group or in a parent context cancels threads in
// this and all subordinate thread-groups.
// - A Cancel in a subordinate thread-group does not affect this thread-group.
// - Wait in this thread-group wait for threads in this and all subordinate
// thread-groups.
type GoGroup interface {
// Go returns a Go object to be provided as a go statement function argument.
Go() (g0 Go)
// SubGo returns athread-group whose fatal errors go to Go’s parent.
// - both non-fatal and fatal errors in SubGo threads are sent to Go’s parent
// like Go.AddError and Go.Done.
// - therefore, when a SubGo thread fails, the application will typically exit.
// - by awaiting SubGo, Go can delay its exit until SubGo has terminated
// - the SubGo thread-group terminates when the its thread exits
SubGo(onFirstFatal ...GoFatalCallback) (subGo SubGo)
// SubGroup creates a sub-ordinate GoGroup.
// - SubGroup fatal and non-fatal errors are sent to the parent GoGroup.
// - SubGroup-context initiated Cancel only affect threads in the SubGroup thread-group
// - parent-initiated Cancel terminates SubGroup threads
// - SubGroup only awaits SubGroup threads
// - parent await also awaits SubGroup threads
SubGroup(onFirstFatal ...GoFatalCallback) (subGroup SubGroup)
// Ch returns a channel sending the all fatal termination errors when
// the FailChannel option is present, or only the first when both
// FailChannel and StoreSubsequentFail options are present.
Ch() (ch <-chan GoError)
// Wait waits for all threads of this thread-group to terminate.
Wait()
// EnableTermination false prevents the SubGo or GoGroup from terminating
// even if the number of threads is zero
EnableTermination(allowTermination bool)
// IsEnableTermination returns the state of EnableTermination,
// initially true
IsEnableTermination() (mayTerminate bool)
// Cancel terminates the threads in this and subordinate thread-groups.
Cancel()
// Context will Cancel when the parent context Cancels.
// Subordinate thread-groups do not Cancel this context.
Context() (ctx context.Context)
// the available data for all threads
Threads() (threads []ThreadData)
// threads that have been named ordered by name
NamedThreads() (threads []ThreadData)
// SetDebug enables debug logging on this particular instance
// - parl.NoDebug
// - parl.DebugPrint
// - parl.AggregateThread
SetDebug(debug GoDebug)
fmt.Stringer
}
type SubGo interface {
// Go returns a Go object to be provided as a go statement function argument.
Go() (g0 Go)
// SubGo returns athread-group whose fatal errors go to Go’s parent.
// - both non-fatal and fatal errors in SubGo threads are sent to Go’s parent
// like Go.AddError and Go.Done.
// - therefore, when a SubGo thread fails, the application will typically exit.
// - by awaiting SubGo, Go can delay its exit until SubGo has terminated
// - the SubGo thread-group terminates when the its thread exits
SubGo(onFirstFatal ...GoFatalCallback) (subGo SubGo)
// SubGroup creates a sub-ordinate GoGroup.
// - SubGroup fatal and non-fatal errors are sent to the parent GoGroup.
// - SubGroup-context initiated Cancel only affect threads in the SubGroup thread-group
// - parent-initiated Cancel terminates SubGroup threads
// - SubGroup only awaits SubGroup threads
// - parent await also awaits SubGroup threads
SubGroup(onFirstFatal ...GoFatalCallback) (subGroup SubGroup)
// Wait waits for all threads of this thread-group to terminate.
Wait()
// returns a channel that closes on subGo end similar to Wait
WaitCh() (ch AwaitableCh)
// EnableTermination false prevents the SubGo or GoGroup from terminating
// even if the number of threads is zero
EnableTermination(allowTermination bool)
IsEnableTermination() (mayTerminate bool)
// Cancel terminates the threads in this and subordinate thread-groups.
Cancel()
// Context will Cancel when the parent context Cancels.
// Subordinate thread-groups do not Cancel this context.
Context() (ctx context.Context)
// the available data for all threads
Threads() (threads []ThreadData)
// threads that have been named ordered by name
NamedThreads() (threads []ThreadData)
// SetDebug enables debug logging on this particular instance
SetDebug(debug GoDebug)
fmt.Stringer
}
type SubGroup interface {
SubGo
// Ch returns a receive channel for fatal errors if this SubGo has LocalChannel option.
Ch() (ch <-chan GoError)
// FirstFatal allows to await or inspect the first thread terminating with error.
// it is valid if this SubGo has LocalSubGo or LocalChannel options.
// To wait for first fatal error using multiple-semaphore mechanic:
// firstFatal := g0.FirstFatal()
// for {
// select {
// case <-firstFatal.Ch():
// …
// To inspect first fatal:
// if firstFatal.DidOccur() …
FirstFatal() (firstFatal *OnceWaiterRO)
}
type GoFactory interface {
// NewGo returns a light-weight thread-group.
// - GoGroup only receives Cancel from ctx, it does not cancel this context.
NewGoGroup(ctx context.Context, onFirstFatal ...GoFatalCallback) (g0 GoGroup)
}
// data types
// GoError is an error or a thread exit associated with a goroutine
// - GoError encapsulates the original unadulterated error
// - GoError provides context for taking action on the error
// - Go provides the thread associated with the error. All GoErrors are associated with
// a Go object
// - because GoError is both error and fmt.Stringer, to print its string representation
// requires using the String() method, otherwise fmt.Printf defaults to the Error()
// method
type GoError interface {
error // Error() string
// Err retrieves the original error value
Err() (err error)
// Time provides when this error occurred
Time() time.Time
// IsThreadExit determines if this error is a thread exit, ie. GeExit GePreDoneExit
// - thread exits may have err nil
// - fatals are non-nil thread exits that may require specific actions such as
// application termination
IsThreadExit() (isThreadExit bool)
// IsFatal determines if this error is a fatal thread-exit, ie. a thread exiting with non-nil error
IsFatal() (isThreadExit bool)
// ErrContext returns in what situation this error occurred
ErrContext() (errContext GoErrorContext)
// Go provides the thread and goroutine emitting this error
Go() (g0 Go)
fmt.Stringer
}
// ThreadData is information about a Go object thread.
// - initially, only Create is present
// - Name is only present for threads that have been named
type ThreadData interface {
// threadID is the ID of the running thread assigned by the go runtime
// - IsValid method checks if value is present
// - zero value is empty string
// - .ThreadID().String(): "5"
ThreadID() (threadID ThreadID)
// createLocation is the code line of the go statement function-call
// creating the goroutine thread
// - IsSet method checks if value is present
// - Create().Short(): "g0.(*SomeType).SomeCode()-thread-data_test.go:73"
Create() (createLocation *pruntime.CodeLocation)
// Func returns the code line of the function of the running thread.
// - IsSet method checks if value is present
// - .Func().Short(): "g0.(*SomeType).SomeFunction()-thread-data_test.go:80"
Func() (funcLocation *pruntime.CodeLocation)
// optional thread-name assigned by consumer
// - zero-value: empty string "" for threads that have not been named
Name() (label string)
// Short returns a short description of the thread "label:threadID" or fmt.Stringer
// - "myThreadName:4"
// - zero-value: "[empty]" ThreadDataEmpty
// - nil value: "threadData:nil" ThreadDataNil
Short() (short string)
// all non-empty fields: [label]:[threadID]_func:[Func]_cre:[Create]
// - "myThreadName:5_func:g0.(*SomeType).SomeFunction()-thread-data_test.go:80_cre:g0.(*SomeType).SomeCode()-thread-data_test.go:73"
// - zero-value: "[empty]" ThreadDataEmpty
fmt.Stringer
}
const (
// GeNonFatal indicates a non-fatal error ocurring during processing.
// err is non-nil
GeNonFatal GoErrorContext = iota + 1
// GePreDoneExit indicates an exit value of a subordinate goroutine,
// other than the final exit of the last running goroutine.
// err may be nil
GePreDoneExit
// A SubGroup with its own error channel is sending a
// locally fatal error not intended to terminate the app
GeLocalChan
// A thread is requesting app termination without a fatal error.
// - this could be a callback
GeTerminate
// GeExit indicates exit of the last goroutine.
// err may be nil.
// The error channel may close after GeExit.
GeExit
)
const (
NoDebug GoDebug = iota
DebugPrint
AggregateThread
)
type GoDebug uint8