github.com/anishathalye/periscope@v0.3.5/internal/par/map.go (about)

     1  package par
     2  
     3  import (
     4  	"reflect"
     5  	"runtime"
     6  	"sync"
     7  )
     8  
     9  var defaultWorkers = runtime.NumCPU()
    10  
    11  type MapFunc = func(key, value interface{}, emit func(x interface{}))
    12  
    13  func Map(v interface{}, mapper MapFunc) <-chan interface{} {
    14  	return MapN(v, defaultWorkers, mapper)
    15  }
    16  
    17  func MapN(v interface{}, workers int, mapper MapFunc) <-chan interface{} {
    18  	type task struct {
    19  		key   interface{}
    20  		value interface{}
    21  	}
    22  	r := reflect.ValueOf(v)
    23  	kind := r.Kind()
    24  	if !(kind == reflect.Map || kind == reflect.Slice || kind == reflect.Array || kind == reflect.Chan) {
    25  		panic("not a map, slice, array, or channel")
    26  	}
    27  	tasks := make(chan task)
    28  	results := make(chan interface{})
    29  	emit := func(x interface{}) {
    30  		results <- x
    31  	}
    32  	var wg sync.WaitGroup
    33  	// spawn workers
    34  	for i := 0; i < workers; i++ {
    35  		wg.Add(1)
    36  		go func() {
    37  			defer wg.Done()
    38  			for t := range tasks {
    39  				mapper(t.key, t.value, emit)
    40  			}
    41  		}()
    42  	}
    43  	// feed workers
    44  	go func() {
    45  		switch kind {
    46  		case reflect.Map:
    47  			iter := r.MapRange()
    48  			for iter.Next() {
    49  				k := iter.Key()
    50  				v := iter.Value()
    51  				tasks <- task{k.Interface(), v.Interface()}
    52  			}
    53  		case reflect.Slice, reflect.Array:
    54  			for i := 0; i < r.Len(); i++ {
    55  				tasks <- task{i, r.Index(i).Interface()}
    56  			}
    57  		case reflect.Chan:
    58  			for i := 0; ; i++ {
    59  				x, ok := r.Recv()
    60  				if !ok {
    61  					break
    62  				}
    63  				tasks <- task{i, x.Interface()}
    64  			}
    65  		}
    66  		close(tasks)
    67  		// wait for workers, close results when done
    68  		wg.Wait()
    69  		close(results)
    70  	}()
    71  	return results
    72  }