| // 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() | |
| }() | |
| } | |
| } |