1919package certprovider
2020
2121import (
22+ "context"
2223 "fmt"
2324 "sync"
25+ "sync/atomic"
2426)
2527
2628// provStore is the global singleton certificate provider store.
@@ -53,6 +55,22 @@ type wrappedProvider struct {
5355 store * store
5456}
5557
58+ // closedProvider always returns errProviderClosed error.
59+ type closedProvider struct {}
60+
61+ func (c closedProvider ) KeyMaterial (ctx context.Context ) (* KeyMaterial , error ) {
62+ return nil , errProviderClosed
63+ }
64+
65+ func (c closedProvider ) Close () {
66+ }
67+
68+ // singleCloseWrappedProvider wraps a provider instance with a reference count
69+ // to properly handle multiple calls to Close.
70+ type singleCloseWrappedProvider struct {
71+ provider atomic.Pointer [Provider ]
72+ }
73+
5674// store is a collection of provider instances, safe for concurrent access.
5775type store struct {
5876 mu sync.Mutex
@@ -75,6 +93,28 @@ func (wp *wrappedProvider) Close() {
7593 }
7694}
7795
96+ // Close overrides the Close method of the embedded provider to avoid release the
97+ // already released reference.
98+ func (w * singleCloseWrappedProvider ) Close () {
99+ newProvider := Provider (closedProvider {})
100+ oldProvider := w .provider .Swap (& newProvider )
101+ (* oldProvider ).Close ()
102+ }
103+
104+ // KeyMaterial returns the key material sourced by the Provider.
105+ // Callers are expected to use the returned value as read-only.
106+ func (w * singleCloseWrappedProvider ) KeyMaterial (ctx context.Context ) (* KeyMaterial , error ) {
107+ return (* w .provider .Load ()).KeyMaterial (ctx )
108+ }
109+
110+ // newSingleCloseWrappedProvider create wrapper a provider instance with a reference count
111+ // to properly handle multiple calls to Close.
112+ func newSingleCloseWrappedProvider (provider Provider ) * singleCloseWrappedProvider {
113+ w := & singleCloseWrappedProvider {}
114+ w .provider .Store (& provider )
115+ return w
116+ }
117+
78118// BuildableConfig wraps parsed provider configuration and functionality to
79119// instantiate provider instances.
80120type BuildableConfig struct {
@@ -112,7 +152,7 @@ func (bc *BuildableConfig) Build(opts BuildOptions) (Provider, error) {
112152 }
113153 if wp , ok := provStore .providers [sk ]; ok {
114154 wp .refCount ++
115- return wp , nil
155+ return newSingleCloseWrappedProvider ( wp ) , nil
116156 }
117157
118158 provider := bc .starter (opts )
@@ -126,7 +166,7 @@ func (bc *BuildableConfig) Build(opts BuildOptions) (Provider, error) {
126166 store : provStore ,
127167 }
128168 provStore .providers [sk ] = wp
129- return wp , nil
169+ return newSingleCloseWrappedProvider ( wp ) , nil
130170}
131171
132172// String returns the provider name and config as a colon separated string.
0 commit comments