Merge release-20210208.0-89-gba4dfa717 (automated)
This commit is contained in:
commit
0328d62d64
134
pkg/gate/gate.go
134
pkg/gate/gate.go
|
@ -1,134 +0,0 @@
|
|||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package gate provides a usage Gate synchronization primitive.
|
||||
package gate
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
// gateClosed is the bit set in the gate's user count to indicate that
|
||||
// it has been closed. It is the MSB of the 32-bit field; the other 31
|
||||
// bits carry the actual count.
|
||||
gateClosed = 0x80000000
|
||||
)
|
||||
|
||||
// Gate is a synchronization primitive that allows concurrent goroutines to
|
||||
// "enter" it as long as it hasn't been closed yet. Once it's been closed,
|
||||
// goroutines cannot enter it anymore, but are allowed to leave, and the closer
|
||||
// will be informed when all goroutines have left.
|
||||
//
|
||||
// Many goroutines are allowed to enter the gate concurrently, but only one is
|
||||
// allowed to close it.
|
||||
//
|
||||
// This is similar to a r/w critical section, except that goroutines "entering"
|
||||
// never block: they either enter immediately or fail to enter. The closer will
|
||||
// block waiting for all goroutines currently inside the gate to leave.
|
||||
//
|
||||
// This function is implemented efficiently. On x86, only one interlocked
|
||||
// operation is performed on enter, and one on leave.
|
||||
//
|
||||
// This is useful, for example, in cases when a goroutine is trying to clean up
|
||||
// an object for which multiple goroutines have pointers. In such a case, users
|
||||
// would be required to enter and leave the gates, and the cleaner would wait
|
||||
// until all users are gone (and no new ones are allowed) before proceeding.
|
||||
//
|
||||
// Users:
|
||||
//
|
||||
// if !g.Enter() {
|
||||
// // Gate is closed, we can't use the object.
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // Do something with object.
|
||||
// [...]
|
||||
//
|
||||
// g.Leave()
|
||||
//
|
||||
// Closer:
|
||||
//
|
||||
// // Prevent new users from using the object, and wait for the existing
|
||||
// // ones to complete.
|
||||
// g.Close()
|
||||
//
|
||||
// // Clean up the object.
|
||||
// [...]
|
||||
//
|
||||
type Gate struct {
|
||||
userCount uint32
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// Enter tries to enter the gate. It will succeed if it hasn't been closed yet,
|
||||
// in which case the caller must eventually call Leave().
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func (g *Gate) Enter() bool {
|
||||
if g == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for {
|
||||
v := atomic.LoadUint32(&g.userCount)
|
||||
if v&gateClosed != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if atomic.CompareAndSwapUint32(&g.userCount, v, v+1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Leave leaves the gate. This must only be called after a successful call to
|
||||
// Enter(). If the gate has been closed and this is the last one inside the
|
||||
// gate, it will notify the closer that the gate is done.
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func (g *Gate) Leave() {
|
||||
for {
|
||||
v := atomic.LoadUint32(&g.userCount)
|
||||
if v&^gateClosed == 0 {
|
||||
panic("leaving a gate with zero usage count")
|
||||
}
|
||||
|
||||
if atomic.CompareAndSwapUint32(&g.userCount, v, v-1) {
|
||||
if v == gateClosed+1 {
|
||||
close(g.done)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the gate for entering, and waits until all goroutines [that are
|
||||
// currently inside the gate] leave before returning.
|
||||
//
|
||||
// Only one goroutine can call this function.
|
||||
func (g *Gate) Close() {
|
||||
for {
|
||||
v := atomic.LoadUint32(&g.userCount)
|
||||
if v&^gateClosed != 0 && g.done == nil {
|
||||
g.done = make(chan struct{})
|
||||
}
|
||||
if atomic.CompareAndSwapUint32(&g.userCount, v, v|gateClosed) {
|
||||
if v&^gateClosed != 0 {
|
||||
<-g.done
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package gate
|
|
@ -0,0 +1,150 @@
|
|||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sync
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/gohacks"
|
||||
)
|
||||
|
||||
// Gate is a synchronization primitive that allows concurrent goroutines to
|
||||
// "enter" it as long as it hasn't been closed yet. Once it's been closed,
|
||||
// goroutines cannot enter it anymore, but are allowed to leave, and the closer
|
||||
// will be informed when all goroutines have left.
|
||||
//
|
||||
// Gate is similar to WaitGroup:
|
||||
//
|
||||
// - Gate.Enter() is analogous to WaitGroup.Add(1), but may be called even if
|
||||
// the Gate counter is 0 and fails if Gate.Close() has been called.
|
||||
//
|
||||
// - Gate.Leave() is equivalent to WaitGroup.Done().
|
||||
//
|
||||
// - Gate.Close() is analogous to WaitGroup.Wait(), but also causes future
|
||||
// calls to Gate.Enter() to fail and may only be called once, from a single
|
||||
// goroutine.
|
||||
//
|
||||
// This is useful, for example, in cases when a goroutine is trying to clean up
|
||||
// an object for which multiple goroutines have pointers. In such a case, users
|
||||
// would be required to enter and leave the Gate, and the cleaner would wait
|
||||
// until all users are gone (and no new ones are allowed) before proceeding.
|
||||
//
|
||||
// Users:
|
||||
//
|
||||
// if !g.Enter() {
|
||||
// // Gate is closed, we can't use the object.
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // Do something with object.
|
||||
// [...]
|
||||
//
|
||||
// g.Leave()
|
||||
//
|
||||
// Closer:
|
||||
//
|
||||
// // Prevent new users from using the object, and wait for the existing
|
||||
// // ones to complete.
|
||||
// g.Close()
|
||||
//
|
||||
// // Clean up the object.
|
||||
// [...]
|
||||
//
|
||||
type Gate struct {
|
||||
userCount int32
|
||||
closingG uintptr
|
||||
}
|
||||
|
||||
const preparingG = 1
|
||||
|
||||
// Enter tries to enter the gate. It will succeed if it hasn't been closed yet,
|
||||
// in which case the caller must eventually call Leave().
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func (g *Gate) Enter() bool {
|
||||
if atomic.AddInt32(&g.userCount, 1) > 0 {
|
||||
return true
|
||||
}
|
||||
g.leaveAfterFailedEnter()
|
||||
return false
|
||||
}
|
||||
|
||||
// leaveAfterFailedEnter is identical to Leave, but is marked noinline to
|
||||
// prevent it from being inlined into Enter, since as of this writing inlining
|
||||
// Leave into Enter prevents Enter from being inlined into its callers.
|
||||
//go:noinline
|
||||
func (g *Gate) leaveAfterFailedEnter() {
|
||||
if atomic.AddInt32(&g.userCount, -1) == math.MinInt32 {
|
||||
g.leaveClosed()
|
||||
}
|
||||
}
|
||||
|
||||
// Leave leaves the gate. This must only be called after a successful call to
|
||||
// Enter(). If the gate has been closed and this is the last one inside the
|
||||
// gate, it will notify the closer that the gate is done.
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func (g *Gate) Leave() {
|
||||
if atomic.AddInt32(&g.userCount, -1) == math.MinInt32 {
|
||||
g.leaveClosed()
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Gate) leaveClosed() {
|
||||
if atomic.LoadUintptr(&g.closingG) == 0 {
|
||||
return
|
||||
}
|
||||
if g := atomic.SwapUintptr(&g.closingG, 0); g > preparingG {
|
||||
goready(g, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the gate, causing future calls to Enter to fail, and waits
|
||||
// until all goroutines that are currently inside the gate leave before
|
||||
// returning.
|
||||
//
|
||||
// Only one goroutine can call this function.
|
||||
func (g *Gate) Close() {
|
||||
if atomic.LoadInt32(&g.userCount) == math.MinInt32 {
|
||||
// The gate is already closed, with no goroutines inside. For legacy
|
||||
// reasons, we have to allow Close to be called again in this case.
|
||||
return
|
||||
}
|
||||
if v := atomic.AddInt32(&g.userCount, math.MinInt32); v == math.MinInt32 {
|
||||
// userCount was already 0.
|
||||
return
|
||||
} else if v >= 0 {
|
||||
panic("concurrent Close of sync.Gate")
|
||||
}
|
||||
|
||||
if g := atomic.SwapUintptr(&g.closingG, preparingG); g != 0 {
|
||||
panic(fmt.Sprintf("invalid sync.Gate.closingG during Close: %#x", g))
|
||||
}
|
||||
if atomic.LoadInt32(&g.userCount) == math.MinInt32 {
|
||||
// The last call to Leave arrived while we were setting up closingG.
|
||||
return
|
||||
}
|
||||
// WaitReasonSemacquire/TraceEvGoBlockSync are consistent with WaitGroup.
|
||||
gopark(gateCommit, gohacks.Noescape(unsafe.Pointer(&g.closingG)), WaitReasonSemacquire, TraceEvGoBlockSync, 0)
|
||||
}
|
||||
|
||||
//go:norace
|
||||
//go:nosplit
|
||||
func gateCommit(g uintptr, closingG unsafe.Pointer) bool {
|
||||
return RaceUncheckedAtomicCompareAndSwapUintptr((*uintptr)(closingG), preparingG, g)
|
||||
}
|
|
@ -56,12 +56,16 @@ func goready(gp uintptr, traceskip int)
|
|||
|
||||
// Values for the reason argument to gopark, from Go's src/runtime/runtime2.go.
|
||||
const (
|
||||
WaitReasonSelect uint8 = 9
|
||||
WaitReasonSelect uint8 = 9
|
||||
WaitReasonChanReceive uint8 = 14
|
||||
WaitReasonSemacquire uint8 = 18
|
||||
)
|
||||
|
||||
// Values for the traceEv argument to gopark, from Go's src/runtime/trace.go.
|
||||
const (
|
||||
TraceEvGoBlockRecv byte = 23
|
||||
TraceEvGoBlockSelect byte = 24
|
||||
TraceEvGoBlockSync byte = 25
|
||||
)
|
||||
|
||||
// Rand32 returns a non-cryptographically-secure random uint32.
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
package waitable
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/gate"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
|
@ -30,10 +30,10 @@ import (
|
|||
|
||||
// Endpoint is a waitable link-layer endpoint.
|
||||
type Endpoint struct {
|
||||
dispatchGate gate.Gate
|
||||
dispatchGate sync.Gate
|
||||
dispatcher stack.NetworkDispatcher
|
||||
|
||||
writeGate gate.Gate
|
||||
writeGate sync.Gate
|
||||
lower stack.LinkEndpoint
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
"sync/atomic"
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/gate"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
)
|
||||
|
||||
// backlog is used for the listen request.
|
||||
|
@ -67,7 +67,7 @@ func eventFD() (int, error) {
|
|||
// Socket is a connected unix domain socket.
|
||||
type Socket struct {
|
||||
// gate protects use of fd.
|
||||
gate gate.Gate
|
||||
gate sync.Gate
|
||||
|
||||
// fd is the bound socket.
|
||||
//
|
||||
|
|
Loading…
Reference in New Issue