mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-27 11:41:39 +00:00
Parallel returns a TaskResultSet
This commit is contained in:
@ -4,6 +4,9 @@ import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
//----------------------------------------
|
||||
// Task
|
||||
|
||||
// val: the value returned after task execution.
|
||||
// err: the error returned during task completion.
|
||||
// abort: tells Parallel to return, whether or not all tasks have completed.
|
||||
@ -17,12 +20,97 @@ type TaskResult struct {
|
||||
|
||||
type TaskResultCh <-chan TaskResult
|
||||
|
||||
type taskResultOK struct {
|
||||
TaskResult
|
||||
OK bool
|
||||
}
|
||||
|
||||
type TaskResultSet struct {
|
||||
chz []TaskResultCh
|
||||
results []taskResultOK
|
||||
}
|
||||
|
||||
func newTaskResultSet(chz []TaskResultCh) *TaskResultSet {
|
||||
return &TaskResultSet{
|
||||
chz: chz,
|
||||
results: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (trs *TaskResultSet) Channels() []TaskResultCh {
|
||||
return trs.chz
|
||||
}
|
||||
|
||||
func (trs *TaskResultSet) LastResult(index int) (TaskResult, bool) {
|
||||
if len(trs.results) <= index {
|
||||
return TaskResult{}, false
|
||||
}
|
||||
resultOK := trs.results[index]
|
||||
return resultOK.TaskResult, resultOK.OK
|
||||
}
|
||||
|
||||
// NOTE: Not concurrency safe.
|
||||
func (trs *TaskResultSet) Reap() {
|
||||
if trs.results == nil {
|
||||
trs.results = make([]taskResultOK, len(trs.chz))
|
||||
}
|
||||
for i := 0; i < len(trs.results); i++ {
|
||||
var trch = trs.chz[i]
|
||||
select {
|
||||
case result := <-trch:
|
||||
// Overwrite result.
|
||||
trs.results[i] = taskResultOK{
|
||||
TaskResult: result,
|
||||
OK: true,
|
||||
}
|
||||
default:
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the firstmost (by task index) error as
|
||||
// discovered by all previous Reap() calls.
|
||||
func (trs *TaskResultSet) FirstValue() interface{} {
|
||||
for _, result := range trs.results {
|
||||
if result.Value != nil {
|
||||
return result.Value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the firstmost (by task index) error as
|
||||
// discovered by all previous Reap() calls.
|
||||
func (trs *TaskResultSet) FirstError() error {
|
||||
for _, result := range trs.results {
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the firstmost (by task index) panic as
|
||||
// discovered by all previous Reap() calls.
|
||||
func (trs *TaskResultSet) FirstPanic() interface{} {
|
||||
for _, result := range trs.results {
|
||||
if result.Panic != nil {
|
||||
return result.Panic
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Parallel
|
||||
|
||||
// Run tasks in parallel, with ability to abort early.
|
||||
// Returns ok=false iff any of the tasks returned abort=true.
|
||||
// NOTE: Do not implement quit features here. Instead, provide convenient
|
||||
// concurrent quit-like primitives, passed implicitly via Task closures. (e.g.
|
||||
// it's not Parallel's concern how you quit/abort your tasks).
|
||||
func Parallel(tasks ...Task) (chz []TaskResultCh, ok bool) {
|
||||
func Parallel(tasks ...Task) (trs *TaskResultSet, ok bool) {
|
||||
var taskResultChz = make([]TaskResultCh, len(tasks)) // To return.
|
||||
var taskDoneCh = make(chan bool, len(tasks)) // A "wait group" channel, early abort if any true received.
|
||||
var numPanics = new(int32) // Keep track of panics to set ok=false later.
|
||||
@ -67,8 +155,5 @@ func Parallel(tasks ...Task) (chz []TaskResultCh, ok bool) {
|
||||
// We must do this check here (after DONE_LOOP).
|
||||
ok = ok && (atomic.LoadInt32(numPanics) == 0)
|
||||
|
||||
// Caller can use this however they want.
|
||||
// TODO: implement convenience functions to
|
||||
// make sense of this structure safely.
|
||||
return taskResultChz, ok
|
||||
return newTaskResultSet(taskResultChz), ok
|
||||
}
|
||||
|
Reference in New Issue
Block a user