| // Copyright 2025 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a MIT |
| // license that can be found in the LICENSE file. |
| |
| /* |
| * Project: etcd |
| * Issue or PR : https://github.com/etcd-io/etcd/pull/7492 |
| * Buggy version: 51939650057d602bb5ab090633138fffe36854dc |
| * fix commit-id: 1b1fabef8ffec606909f01c3983300fff539f214 |
| * Flaky: 40/100 |
| */ |
| package main |
| |
| import ( |
| "os" |
| "runtime" |
| "runtime/pprof" |
| "sync" |
| "time" |
| ) |
| |
| func init() { |
| register("Etcd7492", Etcd7492) |
| } |
| |
| type TokenProvider_etcd7492 interface { |
| assign() |
| enable() |
| disable() |
| } |
| |
| type simpleTokenTTLKeeper_etcd7492 struct { |
| tokens map[string]time.Time |
| addSimpleTokenCh chan struct{} |
| stopCh chan chan struct{} |
| deleteTokenFunc func(string) |
| } |
| |
| type authStore_etcd7492 struct { |
| tokenProvider TokenProvider_etcd7492 |
| } |
| |
| func (as *authStore_etcd7492) Authenticate() { |
| as.tokenProvider.assign() |
| } |
| |
| func NewSimpleTokenTTLKeeper_etcd7492(deletefunc func(string)) *simpleTokenTTLKeeper_etcd7492 { |
| stk := &simpleTokenTTLKeeper_etcd7492{ |
| tokens: make(map[string]time.Time), |
| addSimpleTokenCh: make(chan struct{}, 1), |
| stopCh: make(chan chan struct{}), |
| deleteTokenFunc: deletefunc, |
| } |
| go stk.run() // G1 |
| return stk |
| } |
| |
| func (tm *simpleTokenTTLKeeper_etcd7492) run() { |
| tokenTicker := time.NewTicker(time.Nanosecond) |
| defer tokenTicker.Stop() |
| for { |
| select { |
| case <-tm.addSimpleTokenCh: |
| runtime.Gosched() |
| /// Make tm.tokens not empty is enough |
| tm.tokens["1"] = time.Now() |
| case <-tokenTicker.C: |
| runtime.Gosched() |
| for t, _ := range tm.tokens { |
| tm.deleteTokenFunc(t) |
| delete(tm.tokens, t) |
| } |
| case waitCh := <-tm.stopCh: |
| waitCh <- struct{}{} |
| return |
| } |
| } |
| } |
| |
| func (tm *simpleTokenTTLKeeper_etcd7492) addSimpleToken() { |
| tm.addSimpleTokenCh <- struct{}{} |
| runtime.Gosched() |
| } |
| |
| func (tm *simpleTokenTTLKeeper_etcd7492) stop() { |
| waitCh := make(chan struct{}) |
| tm.stopCh <- waitCh |
| <-waitCh |
| close(tm.stopCh) |
| } |
| |
| type tokenSimple_etcd7492 struct { |
| simpleTokenKeeper *simpleTokenTTLKeeper_etcd7492 |
| simpleTokensMu sync.RWMutex |
| } |
| |
| func (t *tokenSimple_etcd7492) assign() { |
| t.assignSimpleTokenToUser() |
| } |
| |
| func (t *tokenSimple_etcd7492) assignSimpleTokenToUser() { |
| t.simpleTokensMu.Lock() |
| runtime.Gosched() |
| t.simpleTokenKeeper.addSimpleToken() |
| t.simpleTokensMu.Unlock() |
| } |
| func newDeleterFunc(t *tokenSimple_etcd7492) func(string) { |
| return func(tk string) { |
| t.simpleTokensMu.Lock() |
| defer t.simpleTokensMu.Unlock() |
| } |
| } |
| |
| func (t *tokenSimple_etcd7492) enable() { |
| t.simpleTokenKeeper = NewSimpleTokenTTLKeeper_etcd7492(newDeleterFunc(t)) |
| } |
| |
| func (t *tokenSimple_etcd7492) disable() { |
| if t.simpleTokenKeeper != nil { |
| t.simpleTokenKeeper.stop() |
| t.simpleTokenKeeper = nil |
| } |
| t.simpleTokensMu.Lock() |
| t.simpleTokensMu.Unlock() |
| } |
| |
| func newTokenProviderSimple_etcd7492() *tokenSimple_etcd7492 { |
| return &tokenSimple_etcd7492{} |
| } |
| |
| func setupAuthStore_etcd7492() (store *authStore_etcd7492, teardownfunc func()) { |
| as := &authStore_etcd7492{ |
| tokenProvider: newTokenProviderSimple_etcd7492(), |
| } |
| as.tokenProvider.enable() |
| tearDown := func() { |
| as.tokenProvider.disable() |
| } |
| return as, tearDown |
| } |
| |
| func Etcd7492() { |
| prof := pprof.Lookup("goroutineleak") |
| defer func() { |
| time.Sleep(100 * time.Millisecond) |
| prof.WriteTo(os.Stdout, 2) |
| }() |
| for i := 0; i < 100; i++ { |
| go func() { |
| as, tearDown := setupAuthStore_etcd7492() |
| defer tearDown() |
| var wg sync.WaitGroup |
| wg.Add(3) |
| for i := 0; i < 3; i++ { |
| go func() { // G2 |
| as.Authenticate() |
| defer wg.Done() |
| }() |
| } |
| wg.Wait() |
| }() |
| } |
| } |