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 }