| // 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. | |
| package main | |
| import ( | |
| "context" | |
| "io" | |
| "os" | |
| "runtime" | |
| "runtime/pprof" | |
| "sync" | |
| ) | |
| func init() { | |
| register("Etcd5509", Etcd5509) | |
| } | |
| var ErrConnClosed_etcd5509 error | |
| type Client_etcd5509 struct { | |
| mu sync.RWMutex | |
| ctx context.Context | |
| cancel context.CancelFunc | |
| } | |
| func (c *Client_etcd5509) Close() { | |
| c.mu.Lock() | |
| defer c.mu.Unlock() | |
| if c.cancel == nil { | |
| return | |
| } | |
| c.cancel() | |
| c.cancel = nil | |
| c.mu.Unlock() | |
| c.mu.Lock() | |
| } | |
| type remoteClient_etcd5509 struct { | |
| client *Client_etcd5509 | |
| mu sync.Mutex | |
| } | |
| func (r *remoteClient_etcd5509) acquire(ctx context.Context) error { | |
| for { | |
| r.client.mu.RLock() | |
| closed := r.client.cancel == nil | |
| r.mu.Lock() | |
| r.mu.Unlock() | |
| if closed { | |
| return ErrConnClosed_etcd5509 // Missing RUnlock before return | |
| } | |
| r.client.mu.RUnlock() | |
| } | |
| } | |
| type kv_etcd5509 struct { | |
| rc *remoteClient_etcd5509 | |
| } | |
| func (kv *kv_etcd5509) Get(ctx context.Context) error { | |
| return kv.Do(ctx) | |
| } | |
| func (kv *kv_etcd5509) Do(ctx context.Context) error { | |
| for { | |
| err := kv.do(ctx) | |
| if err == nil { | |
| return nil | |
| } | |
| return err | |
| } | |
| } | |
| func (kv *kv_etcd5509) do(ctx context.Context) error { | |
| err := kv.getRemote(ctx) | |
| return err | |
| } | |
| func (kv *kv_etcd5509) getRemote(ctx context.Context) error { | |
| return kv.rc.acquire(ctx) | |
| } | |
| type KV interface { | |
| Get(ctx context.Context) error | |
| Do(ctx context.Context) error | |
| } | |
| func NewKV_etcd5509(c *Client_etcd5509) KV { | |
| return &kv_etcd5509{rc: &remoteClient_etcd5509{ | |
| client: c, | |
| }} | |
| } | |
| func Etcd5509() { | |
| prof := pprof.Lookup("goroutineleak") | |
| defer func() { | |
| // Yield several times to allow the child goroutine to run. | |
| for i := 0; i < yieldCount; i++ { | |
| runtime.Gosched() | |
| } | |
| prof.WriteTo(os.Stdout, 2) | |
| }() | |
| go func() { | |
| ctx, _ := context.WithCancel(context.TODO()) | |
| cli := &Client_etcd5509{ | |
| ctx: ctx, | |
| } | |
| kv := NewKV_etcd5509(cli) | |
| donec := make(chan struct{}) | |
| go func() { | |
| defer close(donec) | |
| err := kv.Get(context.TODO()) | |
| if err != nil && err != ErrConnClosed_etcd5509 { | |
| io.Discard.Write([]byte("Expect ErrConnClosed")) | |
| } | |
| }() | |
| runtime.Gosched() | |
| cli.Close() | |
| <-donec | |
| }() | |
| } |