feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
125
vendor/k8s.io/apiserver/pkg/storage/cacher/ready.go
generated
vendored
125
vendor/k8s.io/apiserver/pkg/storage/cacher/ready.go
generated
vendored
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package cacher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
@@ -30,67 +31,127 @@ const (
|
||||
)
|
||||
|
||||
// ready is a three state condition variable that blocks until is Ready if is not Stopped.
|
||||
// Its initial state is Pending.
|
||||
// Its initial state is Pending and its state machine diagram is as follow.
|
||||
//
|
||||
// Pending <------> Ready -----> Stopped
|
||||
//
|
||||
// | ^
|
||||
// └---------------------------┘
|
||||
type ready struct {
|
||||
state status
|
||||
c *sync.Cond
|
||||
state status // represent the state of the variable
|
||||
generation int // represent the number of times we have transtioned to ready
|
||||
lock sync.RWMutex // protect the state and generation variables
|
||||
restartLock sync.Mutex // protect the transition from ready to pending where the channel is recreated
|
||||
waitCh chan struct{} // blocks until is ready or stopped
|
||||
}
|
||||
|
||||
func newReady() *ready {
|
||||
return &ready{
|
||||
c: sync.NewCond(&sync.RWMutex{}),
|
||||
state: Pending,
|
||||
waitCh: make(chan struct{}),
|
||||
state: Pending,
|
||||
}
|
||||
}
|
||||
|
||||
// done close the channel once the state is Ready or Stopped
|
||||
func (r *ready) done() chan struct{} {
|
||||
r.restartLock.Lock()
|
||||
defer r.restartLock.Unlock()
|
||||
return r.waitCh
|
||||
}
|
||||
|
||||
// wait blocks until it is Ready or Stopped, it returns an error if is Stopped.
|
||||
func (r *ready) wait() error {
|
||||
r.c.L.Lock()
|
||||
defer r.c.L.Unlock()
|
||||
for r.state == Pending {
|
||||
r.c.Wait()
|
||||
}
|
||||
switch r.state {
|
||||
case Ready:
|
||||
return nil
|
||||
case Stopped:
|
||||
return fmt.Errorf("apiserver cacher is stopped")
|
||||
default:
|
||||
return fmt.Errorf("unexpected apiserver cache state: %v", r.state)
|
||||
func (r *ready) wait(ctx context.Context) error {
|
||||
_, err := r.waitAndReadGeneration(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// waitAndReadGenration blocks until it is Ready or Stopped and returns number
|
||||
// of times we entered ready state if Ready and error otherwise.
|
||||
func (r *ready) waitAndReadGeneration(ctx context.Context) (int, error) {
|
||||
for {
|
||||
// r.done() only blocks if state is Pending
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
case <-r.done():
|
||||
}
|
||||
|
||||
r.lock.RLock()
|
||||
switch r.state {
|
||||
case Pending:
|
||||
// since we allow to switch between the states Pending and Ready
|
||||
// if there is a quick transition from Pending -> Ready -> Pending
|
||||
// a process that was waiting can get unblocked and see a Pending
|
||||
// state again. If the state is Pending we have to wait again to
|
||||
// avoid an inconsistent state on the system, with some processes not
|
||||
// waiting despite the state moved back to Pending.
|
||||
r.lock.RUnlock()
|
||||
case Ready:
|
||||
generation := r.generation
|
||||
r.lock.RUnlock()
|
||||
return generation, nil
|
||||
case Stopped:
|
||||
r.lock.RUnlock()
|
||||
return 0, fmt.Errorf("apiserver cacher is stopped")
|
||||
default:
|
||||
r.lock.RUnlock()
|
||||
return 0, fmt.Errorf("unexpected apiserver cache state: %v", r.state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check returns true only if it is Ready.
|
||||
func (r *ready) check() bool {
|
||||
// TODO: Make check() function more sophisticated, in particular
|
||||
// allow it to behave as "waitWithTimeout".
|
||||
rwMutex := r.c.L.(*sync.RWMutex)
|
||||
rwMutex.RLock()
|
||||
defer rwMutex.RUnlock()
|
||||
return r.state == Ready
|
||||
_, ok := r.checkAndReadGeneration()
|
||||
return ok
|
||||
}
|
||||
|
||||
// checkAndReadGeneration returns the current generation and whether it is Ready.
|
||||
func (r *ready) checkAndReadGeneration() (int, bool) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
return r.generation, r.state == Ready
|
||||
}
|
||||
|
||||
// set the state to Pending (false) or Ready (true), it does not have effect if the state is Stopped.
|
||||
func (r *ready) set(ok bool) {
|
||||
r.c.L.Lock()
|
||||
defer r.c.L.Unlock()
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
if r.state == Stopped {
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
if ok && r.state == Pending {
|
||||
r.state = Ready
|
||||
} else {
|
||||
r.generation++
|
||||
select {
|
||||
case <-r.waitCh:
|
||||
default:
|
||||
close(r.waitCh)
|
||||
}
|
||||
} else if !ok && r.state == Ready {
|
||||
// creating the waitCh can be racy if
|
||||
// something enter the wait() method
|
||||
select {
|
||||
case <-r.waitCh:
|
||||
r.restartLock.Lock()
|
||||
r.waitCh = make(chan struct{})
|
||||
r.restartLock.Unlock()
|
||||
default:
|
||||
}
|
||||
r.state = Pending
|
||||
}
|
||||
r.c.Broadcast()
|
||||
}
|
||||
|
||||
// stop the condition variable and set it as Stopped. This state is irreversible.
|
||||
func (r *ready) stop() {
|
||||
r.c.L.Lock()
|
||||
defer r.c.L.Unlock()
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
if r.state != Stopped {
|
||||
r.state = Stopped
|
||||
r.c.Broadcast()
|
||||
}
|
||||
select {
|
||||
case <-r.waitCh:
|
||||
default:
|
||||
close(r.waitCh)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user