github.com/biogo/biogo@v1.0.4/concurrent/processor.go (about)

     1  // Copyright ©2011-2012 The bíogo Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package concurrent
     6  
     7  import (
     8  	"fmt"
     9  	"runtime"
    10  	"sync"
    11  )
    12  
    13  // Interface is a type that performs an operation on itself, returning any error.
    14  type Operator interface {
    15  	Operation() (interface{}, error)
    16  }
    17  
    18  // The Processor type manages a number of concurrent Processes.
    19  type Processor struct {
    20  	in      chan Operator
    21  	out     chan Result
    22  	stop    chan struct{}
    23  	work    chan struct{}
    24  	threads int
    25  	wg      *sync.WaitGroup
    26  }
    27  
    28  // Return a new Processor to operate the function f over the number of threads specified taking
    29  // input from queue and placing the result in buffer. Threads is limited by GOMAXPROCS, if threads is greater
    30  // GOMAXPROCS or less than 1 then threads is set to GOMAXPROCS.
    31  func NewProcessor(queue chan Operator, buffer int, threads int) (p *Processor) {
    32  	if available := runtime.GOMAXPROCS(0); threads > available || threads < 1 {
    33  		threads = available
    34  	}
    35  
    36  	p = &Processor{
    37  		in:      queue,
    38  		out:     make(chan Result, buffer),
    39  		stop:    make(chan struct{}),
    40  		work:    make(chan struct{}, threads),
    41  		threads: threads,
    42  		wg:      &sync.WaitGroup{},
    43  	}
    44  	for i := 0; i < threads; i++ {
    45  		p.work <- struct{}{}
    46  	}
    47  
    48  	for i := 0; i < threads; i++ {
    49  		p.wg.Add(1)
    50  		go func() {
    51  			<-p.work
    52  			defer func() {
    53  				if err := recover(); err != nil {
    54  					p.out <- Result{nil, fmt.Errorf("concurrent: processor panic: %v", err)}
    55  				}
    56  				p.work <- struct{}{}
    57  				if len(p.work) == p.threads {
    58  					close(p.out)
    59  				}
    60  				p.wg.Done()
    61  			}()
    62  
    63  			for input := range p.in {
    64  				v, e := input.Operation()
    65  				if p.out != nil {
    66  					p.out <- Result{v, e}
    67  				}
    68  				select {
    69  				case <-p.stop:
    70  					return
    71  				default:
    72  				}
    73  			}
    74  		}()
    75  	}
    76  
    77  	return
    78  }
    79  
    80  // Submit values for processing.
    81  func (p *Processor) Process(value ...Operator) {
    82  	for _, v := range value {
    83  		p.in <- v
    84  	}
    85  }
    86  
    87  // Get the next available result.
    88  func (p *Processor) Result() (interface{}, error) {
    89  	r := <-p.out
    90  	return r.Value, r.Err
    91  }
    92  
    93  // Close the queue.
    94  func (p *Processor) Close() {
    95  	close(p.in)
    96  }
    97  
    98  // Return the number of working goroutines.
    99  func (p *Processor) Working() int {
   100  	return p.threads - len(p.work)
   101  }
   102  
   103  // Terminate the goroutines.
   104  func (p *Processor) Stop() {
   105  	close(p.stop)
   106  }
   107  
   108  // Wait for all running processes to finish.
   109  func (p *Processor) Wait() {
   110  	p.wg.Wait()
   111  }
   112  
   113  type Result struct {
   114  	Value interface{}
   115  	Err   error
   116  }