github.com/mithrandie/csvq@v1.18.1/lib/query/goroutine_manager.go (about)

     1  package query
     2  
     3  import (
     4  	"context"
     5  	"math"
     6  	"sync"
     7  )
     8  
     9  var (
    10  	gm    *GoroutineManager
    11  	getGm sync.Once
    12  )
    13  
    14  const MinimumRequiredPerCPUCore = 80
    15  
    16  func GetGoroutineManager() *GoroutineManager {
    17  	getGm.Do(func() {
    18  		gm = &GoroutineManager{
    19  			Count:                  0,
    20  			CountMutex:             &sync.Mutex{},
    21  			MinimumRequiredPerCore: MinimumRequiredPerCPUCore,
    22  		}
    23  	})
    24  	return gm
    25  }
    26  
    27  type GoroutineManager struct {
    28  	Count                  int
    29  	CountMutex             *sync.Mutex
    30  	MinimumRequiredPerCore int
    31  }
    32  
    33  func (m *GoroutineManager) AssignRoutineNumber(recordLen int, minimumRequiredPerCore int, cpuNum int) int {
    34  	var greaterThanZero = func(i int) int {
    35  		if i < 1 {
    36  			return 1
    37  		}
    38  		return i
    39  	}
    40  	var min = func(i1 int, i2 int) int {
    41  		if i1 < i2 {
    42  			return i1
    43  		}
    44  		return i2
    45  	}
    46  
    47  	number := cpuNum
    48  	if minimumRequiredPerCore < 1 {
    49  		minimumRequiredPerCore = m.MinimumRequiredPerCore
    50  	}
    51  
    52  	number = min(number, greaterThanZero(int(math.Floor(float64(recordLen)/float64(minimumRequiredPerCore)))))
    53  
    54  	m.CountMutex.Lock()
    55  	defer m.CountMutex.Unlock()
    56  
    57  	number = min(number, greaterThanZero(number-m.Count))
    58  
    59  	m.Count += number - 1
    60  	return number
    61  }
    62  
    63  func (m *GoroutineManager) Release() {
    64  	m.CountMutex.Lock()
    65  	if 0 < m.Count {
    66  		m.Count--
    67  	}
    68  	m.CountMutex.Unlock()
    69  }
    70  
    71  type GoroutineTaskManager struct {
    72  	Number int
    73  
    74  	grTaskMutex *sync.Mutex
    75  	grCount     int
    76  	recordLen   int
    77  	waitGroup   sync.WaitGroup
    78  	err         error
    79  }
    80  
    81  func NewGoroutineTaskManager(recordLen int, minimumRequiredPerCore int, cpuNum int) *GoroutineTaskManager {
    82  	number := GetGoroutineManager().AssignRoutineNumber(recordLen, minimumRequiredPerCore, cpuNum)
    83  
    84  	return &GoroutineTaskManager{
    85  		Number:      number,
    86  		grTaskMutex: &sync.Mutex{},
    87  		grCount:     number - 1,
    88  		recordLen:   recordLen,
    89  	}
    90  }
    91  
    92  func (m *GoroutineTaskManager) HasError() bool {
    93  	return m.err != nil
    94  }
    95  
    96  func (m *GoroutineTaskManager) SetError(e error) {
    97  	m.grTaskMutex.Lock()
    98  	if m.err == nil {
    99  		m.err = e
   100  	}
   101  	m.grTaskMutex.Unlock()
   102  }
   103  
   104  func (m *GoroutineTaskManager) Err() error {
   105  	return m.err
   106  }
   107  
   108  func (m *GoroutineTaskManager) RecordRange(routineIndex int) (int, int) {
   109  	calcLen := m.recordLen / m.Number
   110  
   111  	var start = routineIndex * calcLen
   112  
   113  	if m.recordLen <= start {
   114  		return 0, 0
   115  	}
   116  
   117  	var end int
   118  	if routineIndex == m.Number-1 {
   119  		end = m.recordLen
   120  	} else {
   121  		end = (routineIndex + 1) * calcLen
   122  	}
   123  	return start, end
   124  }
   125  
   126  func (m *GoroutineTaskManager) Add() {
   127  	m.waitGroup.Add(1)
   128  }
   129  
   130  func (m *GoroutineTaskManager) Done() {
   131  	m.grTaskMutex.Lock()
   132  	if 0 < m.grCount {
   133  		m.grCount--
   134  		GetGoroutineManager().Release()
   135  	}
   136  	m.grTaskMutex.Unlock()
   137  
   138  	m.waitGroup.Done()
   139  }
   140  
   141  func (m *GoroutineTaskManager) Wait() {
   142  	m.waitGroup.Wait()
   143  }
   144  
   145  func (m *GoroutineTaskManager) run(ctx context.Context, fn func(int) error, thIdx int) {
   146  	defer func() {
   147  		if !m.HasError() {
   148  			if panicReport := recover(); panicReport != nil {
   149  				m.SetError(NewFatalError(panicReport))
   150  			}
   151  		}
   152  
   153  		if 1 < m.Number {
   154  			m.Done()
   155  		}
   156  	}()
   157  
   158  	start, end := m.RecordRange(thIdx)
   159  
   160  	for i := start; i < end; i++ {
   161  		if m.HasError() {
   162  			break
   163  		}
   164  		if i&15 == 0 && ctx.Err() != nil {
   165  			break
   166  		}
   167  
   168  		if err := fn(i); err != nil {
   169  			m.SetError(err)
   170  			break
   171  		}
   172  	}
   173  }
   174  
   175  func (m *GoroutineTaskManager) Run(ctx context.Context, fn func(int) error) error {
   176  	if 1 < m.Number {
   177  		for i := 0; i < m.Number; i++ {
   178  			m.Add()
   179  			go m.run(ctx, fn, i)
   180  		}
   181  		m.Wait()
   182  	} else {
   183  		m.run(ctx, fn, 0)
   184  	}
   185  
   186  	if m.HasError() {
   187  		return m.Err()
   188  	}
   189  	if ctx.Err() != nil {
   190  		return ConvertContextError(ctx.Err())
   191  	}
   192  	return nil
   193  }