github.com/vanus-labs/vanus/lib@v0.0.0-20231221070800-1334a7b9605e/sync/semaphore.go (about) 1 // Copyright 2023 Linkall Inc. 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 sync 16 17 import ( 18 _ "unsafe" // for go:linkname 19 ) 20 21 type Semaphore struct { 22 sem uint32 23 handoff bool 24 } 25 26 func (s *Semaphore) Init(handoff bool) *Semaphore { 27 s.handoff = handoff 28 return s 29 } 30 31 func (s *Semaphore) Acquire() { 32 semacquire(&s.sem) 33 } 34 35 func (s *Semaphore) Release() { 36 semrelease(&s.sem, s.handoff, 0) 37 } 38 39 //go:linkname semacquire sync.runtime_Semacquire 40 func semacquire(addr *uint32) 41 42 //go:linkname semrelease sync.runtime_Semrelease 43 func semrelease(addr *uint32, handoff bool, skipframes int) 44 45 /* 46 import ( 47 // standard libraries. 48 stdrt "runtime" 49 "sync/atomic" 50 "unsafe" 51 52 // third-party libraries. 53 "gvisor.dev/gvisor/pkg/sync" 54 55 // this project. 56 "github.com/vanus-labs/vanus/lib/container/conque/unbounded" 57 "github.com/vanus-labs/vanus/lib/runtime" 58 ) 59 60 const ( 61 running uintptr = 0 62 runnable uintptr = 1 63 64 enableReloadPtr1 = false 65 66 activeSpin = 4 67 activeSpinCnt = 30 68 passiveSpin = 1 69 ) 70 71 var ncpu = stdrt.NumCPU() 72 73 type waiter struct { 74 // key is the status of waiter's goroutine. The value of key is running, runnable or gp (treats it as waiting). 75 key uintptr 76 } 77 78 type Semaphore struct { 79 sem int64 80 q unbounded.Queue[*waiter] 81 } 82 83 func (s *Semaphore) Acquire() { 84 if atomic.AddInt64(&s.sem, -1) >= 0 { 85 return 86 } 87 88 w := &waiter{} 89 s.q.Push(w) 90 91 sync.Gopark(semaphoreCommit, unsafe.Pointer(w), sync.WaitReasonSemacquire, sync.TraceEvGoBlockSync, 0) 92 } 93 94 //go:norace 95 func semaphoreCommit(gp uintptr, wp unsafe.Pointer) bool { 96 w := (*waiter)(wp) 97 // return atomic.CompareAndSwapUintptr(&w.key, running, gp) 98 return atomic.SwapUintptr(&w.key, gp) != runnable 99 } 100 101 func (s *Semaphore) Release() { 102 if atomic.AddInt64(&s.sem, 1) > 0 { 103 return 104 } 105 106 w := getWaiter(&s.q) 107 108 key := atomic.SwapUintptr(&w.key, runnable) 109 if key != running { 110 sync.Goready(key, 0, true) 111 } 112 } 113 114 func getWaiter(q *unbounded.Queue[*waiter]) *waiter { 115 if enableReloadPtr1 && ncpu <= 1 { 116 return getWaiter1(q) 117 } 118 return getWaiterN(q) 119 } 120 121 func getWaiter1(q *unbounded.Queue[*waiter]) *waiter { 122 for i := 0; ; i++ { 123 w, ok := q.SharedPop() 124 if ok { 125 return w 126 } 127 128 switch { 129 case i < passiveSpin: 130 runtime.OSYield() 131 default: 132 // TODO(james.yin): use condition variable? 133 runtime.OSYield() 134 } 135 } 136 } 137 138 func getWaiterN(q *unbounded.Queue[*waiter]) *waiter { 139 for i := 0; ; i++ { 140 w, ok := q.SharedPop() 141 if ok { 142 return w 143 } 144 145 switch { 146 case i < activeSpin: 147 runtime.ProcYield(activeSpinCnt) 148 case i < activeSpin+passiveSpin: 149 runtime.OSYield() 150 default: 151 // TODO(james.yin): use condition variable? 152 runtime.OSYield() 153 } 154 } 155 } 156 */