mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
123 lines
2.2 KiB
Go
123 lines
2.2 KiB
Go
// Copyright 2017 Pilosa Corp.
|
|
//
|
|
// 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 egpool
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
type Group struct {
|
|
PoolSize int
|
|
|
|
jobs chan func() error
|
|
|
|
sema chan struct{}
|
|
errMu sync.Mutex
|
|
firstErr error
|
|
errs []error
|
|
}
|
|
|
|
func (eg *Group) Go(f func() error) {
|
|
if eg.PoolSize <= 0 {
|
|
eg.PoolSize = 1
|
|
}
|
|
|
|
if eg.jobs == nil {
|
|
eg.jobs = make(chan func() error)
|
|
eg.sema = make(chan struct{}, eg.PoolSize)
|
|
}
|
|
|
|
// Start the job in an idle worker if possible.
|
|
select {
|
|
case eg.jobs <- f:
|
|
return
|
|
default:
|
|
}
|
|
|
|
// Start a new worker if necessary.
|
|
select {
|
|
case eg.jobs <- f:
|
|
// A worker finished its previous job and took this one over.
|
|
return
|
|
case eg.sema <- struct{}{}:
|
|
// Start a new worker.
|
|
go eg.processJobs()
|
|
eg.jobs <- f
|
|
}
|
|
}
|
|
|
|
func (eg *Group) err(err error) {
|
|
eg.errMu.Lock()
|
|
defer eg.errMu.Unlock()
|
|
|
|
if eg.firstErr == nil {
|
|
eg.firstErr = err
|
|
}
|
|
eg.errs = append(eg.errs, err)
|
|
}
|
|
|
|
type ErrPanic struct {
|
|
Value interface{}
|
|
}
|
|
|
|
func (p ErrPanic) Error() string {
|
|
return fmt.Sprintf("panic: %v", p.Value)
|
|
}
|
|
|
|
var ErrGoexit = errors.New("runtime.Goexit used in job function")
|
|
|
|
func (eg *Group) processJobs() {
|
|
// Notify pool of shutdown.
|
|
defer func() { <-eg.sema }()
|
|
|
|
// Handle panic and Goexit.
|
|
var finished bool
|
|
defer func() {
|
|
if !finished {
|
|
if p := recover(); p != nil {
|
|
eg.err(ErrPanic{p})
|
|
} else {
|
|
eg.err(ErrGoexit)
|
|
}
|
|
}
|
|
}()
|
|
|
|
// Run jobs from queue.
|
|
for jobFn := range eg.jobs {
|
|
err := jobFn()
|
|
if err != nil {
|
|
eg.err(err)
|
|
}
|
|
}
|
|
|
|
finished = true
|
|
}
|
|
|
|
func (eg *Group) Wait() error {
|
|
if eg.jobs == nil {
|
|
return nil
|
|
}
|
|
close(eg.jobs)
|
|
for i := 0; i < eg.PoolSize; i++ {
|
|
eg.sema <- struct{}{}
|
|
}
|
|
return eg.firstErr
|
|
}
|
|
|
|
func (eg *Group) Errors() []error {
|
|
return eg.errs
|
|
}
|