mirror of https://github.com/prometheus/prometheus
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
3.3 KiB
162 lines
3.3 KiB
// Copyright 2013 Prometheus Team |
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
|
// you may not use this file except in compliance with the License. |
|
// You may obtain a copy of the License at |
|
// |
|
// http://www.apache.org/licenses/LICENSE-2.0 |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
|
|
package utility |
|
|
|
import ( |
|
"fmt" |
|
"sync" |
|
) |
|
|
|
type state int |
|
|
|
func (s state) String() string { |
|
switch s { |
|
case unstarted: |
|
return "unstarted" |
|
case started: |
|
return "started" |
|
case finished: |
|
return "finished" |
|
} |
|
panic("unreachable") |
|
} |
|
|
|
const ( |
|
unstarted state = iota |
|
started |
|
finished |
|
) |
|
|
|
// An UncertaintyGroup models a group of operations whose result disposition is |
|
// tenuous and needs to be validated en masse in order to make a future |
|
// decision. |
|
type UncertaintyGroup interface { |
|
// Succeed makes a remark that a given action succeeded, in part. |
|
Succeed() |
|
// Fail makes a remark that a given action failed, in part. Nil values are |
|
// illegal. |
|
Fail(error) |
|
// MayFail makes a remark that a given action either succeeded or failed. The |
|
// determination is made by whether the error is nil. |
|
MayFail(error) |
|
// Wait waits for the group to have finished and emits the result of what |
|
// occurred for the group. |
|
Wait() (succeeded bool) |
|
// Errors emits any errors that could have occurred. |
|
Errors() []error |
|
} |
|
|
|
type uncertaintyGroup struct { |
|
state state |
|
remaining uint |
|
successes uint |
|
results chan error |
|
anomalies []error |
|
sync.Mutex |
|
} |
|
|
|
func (g *uncertaintyGroup) Succeed() { |
|
if g.isFinished() { |
|
panic("cannot remark when done") |
|
} |
|
|
|
g.results <- nil |
|
} |
|
|
|
func (g *uncertaintyGroup) Fail(err error) { |
|
if g.isFinished() { |
|
panic("cannot remark when done") |
|
} |
|
|
|
if err == nil { |
|
panic("expected a failure") |
|
} |
|
|
|
g.results <- err |
|
} |
|
|
|
func (g *uncertaintyGroup) MayFail(err error) { |
|
if g.isFinished() { |
|
panic("cannot remark when done") |
|
} |
|
|
|
g.results <- err |
|
} |
|
|
|
func (g *uncertaintyGroup) isFinished() bool { |
|
g.Lock() |
|
defer g.Unlock() |
|
|
|
return g.state == finished |
|
} |
|
|
|
func (g *uncertaintyGroup) finish() { |
|
g.Lock() |
|
defer g.Unlock() |
|
|
|
g.state = finished |
|
} |
|
|
|
func (g *uncertaintyGroup) start() { |
|
g.Lock() |
|
defer g.Unlock() |
|
|
|
if g.state != unstarted { |
|
panic("cannot restart") |
|
} |
|
|
|
g.state = started |
|
} |
|
|
|
func (g *uncertaintyGroup) Wait() bool { |
|
defer close(g.results) |
|
g.start() |
|
|
|
for g.remaining > 0 { |
|
result := <-g.results |
|
switch result { |
|
case nil: |
|
g.successes++ |
|
default: |
|
g.anomalies = append(g.anomalies, result) |
|
} |
|
|
|
g.remaining-- |
|
} |
|
|
|
g.finish() |
|
|
|
return len(g.anomalies) == 0 |
|
} |
|
|
|
func (g *uncertaintyGroup) Errors() []error { |
|
if g.state != finished { |
|
panic("cannot provide errors until finished") |
|
} |
|
|
|
return g.anomalies |
|
} |
|
|
|
func (g *uncertaintyGroup) String() string { |
|
return fmt.Sprintf("UncertaintyGroup %s with %s failures", g.state, g.anomalies) |
|
} |
|
|
|
// NewUncertaintyGroup furnishes an UncertaintyGroup for a given set of actions |
|
// where their quantity is known a priori. |
|
func NewUncertaintyGroup(count uint) UncertaintyGroup { |
|
return &uncertaintyGroup{ |
|
remaining: count, |
|
results: make(chan error), |
|
} |
|
}
|
|
|