- package v2
-
- import (
- "fmt"
- "sync/atomic"
-
- "github.com/tendermint/tendermint/libs/log"
- )
-
- // TODO
- // * revisit panic conditions
- // * audit log levels
- // * Convert routine to an interface with concrete implmentation
- // * determine the public interface
-
- type handleFunc = func(event Event) (Events, error)
-
- type Routine struct {
- name string
- input chan Event
- errors chan error
- out chan Event
- stopped chan struct{}
- finished chan error
- running *uint32
- handle handleFunc
- logger log.Logger
- metrics *Metrics
- }
-
- func newRoutine(name string, handleFunc handleFunc) *Routine {
- return &Routine{
- name: name,
- input: make(chan Event, 1),
- handle: handleFunc,
- errors: make(chan error, 1),
- out: make(chan Event, 1),
- stopped: make(chan struct{}, 1),
- finished: make(chan error, 1),
- running: new(uint32),
- logger: log.NewNopLogger(),
- metrics: NopMetrics(),
- }
- }
-
- func (rt *Routine) setLogger(logger log.Logger) {
- rt.logger = logger
- }
-
- func (rt *Routine) setMetrics(metrics *Metrics) {
- rt.metrics = metrics
- }
-
- func (rt *Routine) run() {
- rt.logger.Info(fmt.Sprintf("%s: run\n", rt.name))
- starting := atomic.CompareAndSwapUint32(rt.running, uint32(0), uint32(1))
- if !starting {
- panic("Routine has already started")
- }
- errorsDrained := false
- for {
- if !rt.isRunning() {
- break
- }
- select {
- case iEvent, ok := <-rt.input:
- rt.metrics.EventsIn.With("routine", rt.name).Add(1)
- if !ok {
- if !errorsDrained {
- continue // wait for errors to be drainned
- }
- rt.logger.Info(fmt.Sprintf("%s: stopping\n", rt.name))
- rt.stopped <- struct{}{}
- rt.terminate(fmt.Errorf("stopped"))
- return
- }
- oEvents, err := rt.handle(iEvent)
- rt.metrics.EventsHandled.With("routine", rt.name).Add(1)
- if err != nil {
- rt.terminate(err)
- return
- }
- rt.metrics.EventsOut.With("routine", rt.name).Add(float64(len(oEvents)))
- rt.logger.Info(fmt.Sprintf("%s handled %d events\n", rt.name, len(oEvents)))
- for _, event := range oEvents {
- rt.logger.Info(fmt.Sprintln("writting back to output"))
- rt.out <- event
- }
- case iEvent, ok := <-rt.errors:
- rt.metrics.ErrorsIn.With("routine", rt.name).Add(1)
- if !ok {
- rt.logger.Info(fmt.Sprintf("%s: errors closed\n", rt.name))
- errorsDrained = true
- continue
- }
- oEvents, err := rt.handle(iEvent)
- rt.metrics.ErrorsHandled.With("routine", rt.name).Add(1)
- if err != nil {
- rt.terminate(err)
- return
- }
- rt.metrics.ErrorsOut.With("routine", rt.name).Add(float64(len(oEvents)))
- for _, event := range oEvents {
- rt.out <- event
- }
- }
- }
- }
- func (rt *Routine) feedback() {
- for event := range rt.out {
- rt.send(event)
- }
- }
-
- // XXX: this should be called trySend for consistency
- func (rt *Routine) send(event Event) bool {
- if !rt.isRunning() {
- return false
- }
- rt.logger.Info(fmt.Sprintf("%s: sending %+v", rt.name, event))
- if err, ok := event.(error); ok {
- select {
- case rt.errors <- err:
- rt.metrics.ErrorsSent.With("routine", rt.name).Add(1)
- return true
- default:
- rt.metrics.ErrorsShed.With("routine", rt.name).Add(1)
- rt.logger.Info(fmt.Sprintf("%s: errors channel was full\n", rt.name))
- return false
- }
- } else {
- select {
- case rt.input <- event:
- rt.metrics.EventsSent.With("routine", rt.name).Add(1)
- return true
- default:
- rt.metrics.EventsShed.With("routine", rt.name).Add(1)
- rt.logger.Info(fmt.Sprintf("%s: channel was full\n", rt.name))
- return false
- }
- }
- }
-
- func (rt *Routine) isRunning() bool {
- return atomic.LoadUint32(rt.running) == 1
- }
-
- func (rt *Routine) output() chan Event {
- return rt.out
- }
-
- func (rt *Routine) stop() {
- if !rt.isRunning() {
- return
- }
- rt.logger.Info(fmt.Sprintf("%s: stop\n", rt.name))
- close(rt.input)
- close(rt.errors)
- <-rt.stopped
- }
-
- func (rt *Routine) terminate(reason error) {
- stopped := atomic.CompareAndSwapUint32(rt.running, uint32(1), uint32(0))
- if !stopped {
- panic("called stop but already stopped")
- }
- rt.finished <- reason
- }
-
- // XXX: this should probably produced the finished
- // channel and let the caller deicde how long to wait
- func (rt *Routine) wait() error {
- return <-rt.finished
- }
|