github.com/pawelgaczynski/gain@v0.4.0-alpha.0.20230821120126-41f1e60a18da/looper.go (about) 1 // Copyright (c) 2023 Paweł Gaczyński 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gain 16 17 import ( 18 "errors" 19 "runtime" 20 21 gainErrors "github.com/pawelgaczynski/gain/pkg/errors" 22 "github.com/pawelgaczynski/giouring" 23 ) 24 25 type eventProcessor func(*giouring.CompletionQueueEvent) error 26 27 type looper struct { 28 submitter 29 ring *giouring.Ring 30 cpuAffinity bool 31 processPriority bool 32 maxCQEvents int 33 startListener workerStartListener 34 35 prepareHandler func() error 36 loopFinisher func() 37 loopFinishCondition func() bool 38 shutdownHandler func() bool 39 running bool 40 } 41 42 func (l *looper) innerLoop(eventProcessor eventProcessor) error { 43 var err error 44 cqes := make([]*giouring.CompletionQueueEvent, l.maxCQEvents) 45 46 for { 47 if continueLoop := l.shutdownHandler(); !continueLoop { 48 return nil 49 } 50 51 if err = l.submit(); err != nil { 52 if errors.Is(err, gainErrors.ErrSkippable) { 53 if l.loopFinisher != nil { 54 l.loopFinisher() 55 } 56 57 if l.loopFinishCondition != nil && l.loopFinishCondition() { 58 return nil 59 } 60 61 continue 62 } 63 64 return err 65 } 66 numberOfCQEs := l.ring.PeekBatchCQE(cqes) 67 68 var i uint32 69 for i = 0; i < numberOfCQEs; i++ { 70 cqe := cqes[i] 71 72 err = eventProcessor(cqe) 73 if err != nil { 74 l.advance(i + 1) 75 76 return err 77 } 78 } 79 l.advance(numberOfCQEs) 80 81 if l.loopFinisher != nil { 82 l.loopFinisher() 83 } 84 85 if l.loopFinishCondition != nil && l.loopFinishCondition() { 86 return nil 87 } 88 } 89 } 90 91 func (l *looper) startLoop(index int, eventProcessor eventProcessor) error { 92 var err error 93 if l.processPriority { 94 err = setProcessPriority() 95 if err != nil { 96 return err 97 } 98 } 99 100 if l.cpuAffinity { 101 err = setCPUAffinity(index) 102 if err != nil { 103 return err 104 } 105 106 runtime.LockOSThread() 107 defer runtime.UnlockOSThread() 108 } 109 110 if l.prepareHandler != nil { 111 err = l.prepareHandler() 112 if err != nil { 113 return err 114 } 115 } 116 117 if l.startListener != nil { 118 l.running = true 119 l.startListener() 120 } 121 122 return l.innerLoop(eventProcessor) 123 } 124 125 func (l *looper) started() bool { 126 return l.running 127 } 128 129 func newLooper( 130 ring *giouring.Ring, cpuAffinity bool, processPriority bool, maxCQEvents int, 131 ) *looper { 132 return &looper{ 133 submitter: newBatchSubmitter(ring), 134 ring: ring, 135 cpuAffinity: cpuAffinity, 136 processPriority: processPriority, 137 maxCQEvents: maxCQEvents, 138 } 139 }