github.com/richardwilkes/toolbox@v1.121.0/softref/softref.go (about) 1 // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved. 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, version 2.0. If a copy of the MPL was not distributed with 5 // this file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 // 7 // This Source Code Form is "Incompatible With Secondary Licenses", as 8 // defined by the Mozilla Public License, version 2.0. 9 10 package softref 11 12 import ( 13 "log/slog" 14 "runtime" 15 "sync" 16 ) 17 18 // Pool is used to track soft references to resources. 19 type Pool struct { 20 refs map[string]*softRef 21 lock sync.Mutex 22 } 23 24 // Resource is a resource that will be used with a pool. 25 type Resource interface { 26 // Key returns a unique key for this resource. Must never change. 27 Key() string 28 // Release is called when the resource is no longer being referenced by any remaining soft references. 29 Release() 30 } 31 32 // SoftRef is a soft reference to a given resource. 33 type SoftRef struct { 34 Resource Resource 35 Key string 36 } 37 38 type softRef struct { 39 resource Resource 40 count int 41 } 42 43 // DefaultPool is a global default soft reference pool. 44 var DefaultPool = NewPool() 45 46 // NewPool creates a new soft reference pool. 47 func NewPool() *Pool { 48 return &Pool{refs: make(map[string]*softRef)} 49 } 50 51 // NewSoftRef returns a SoftRef to the given resource, along with a flag indicating if a reference existed previously. 52 func (p *Pool) NewSoftRef(resource Resource) (ref *SoftRef, existedPreviously bool) { 53 key := resource.Key() 54 p.lock.Lock() 55 defer p.lock.Unlock() 56 r := p.refs[key] 57 if r != nil { 58 r.count++ 59 } else { 60 r = &softRef{ 61 resource: resource, 62 count: 1, 63 } 64 p.refs[key] = r 65 } 66 sr := &SoftRef{ 67 Key: key, 68 Resource: r.resource, 69 } 70 runtime.SetFinalizer(sr, p.finalizeSoftRef) 71 return sr, r.count > 1 72 } 73 74 func (p *Pool) finalizeSoftRef(ref *SoftRef) { 75 p.lock.Lock() 76 if r, ok := p.refs[ref.Key]; ok { 77 r.count-- 78 if r.count == 0 { 79 delete(p.refs, ref.Key) 80 r.resource.Release() 81 } else if r.count < 0 { 82 slog.Debug("SoftRef count is invalid", "key", ref.Key, "count", r.count) 83 } 84 } else { 85 slog.Debug("SoftRef finalized for unknown key", "key", ref.Key) 86 } 87 p.lock.Unlock() 88 }