gitee.com/go-spring2/spring-base@v1.1.3/cache/engine.go (about) 1 /* 2 * Copyright 2012-2019 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * https://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cache 18 19 import ( 20 "context" 21 "sync" 22 "time" 23 ) 24 25 type cacheItem struct { 26 value Result 27 writeTime time.Time 28 locker sync.Mutex 29 loader ResultLoader 30 expireAfterWrite time.Duration 31 loading *cacheItemLoading 32 } 33 34 func (e *cacheItem) expired() bool { 35 if e.expireAfterWrite <= 0 { 36 return false 37 } 38 return time.Since(e.writeTime)-e.expireAfterWrite > 0 39 } 40 41 type cacheItemLoading struct { 42 v Result 43 err error 44 wg sync.WaitGroup 45 } 46 47 func (e *cacheItem) load(ctx context.Context, key string) (LoadType, Result, error) { 48 49 e.locker.Lock() 50 if p := e.loading; p != nil { 51 e.locker.Unlock() 52 p.wg.Wait() 53 if p.err != nil { 54 return LoadNone, nil, p.err 55 } 56 return LoadCache, p.v, nil 57 } 58 if e.value != nil && !e.expired() { 59 e.locker.Unlock() 60 return LoadCache, e.value, nil 61 } 62 c := &cacheItemLoading{} 63 c.wg.Add(1) 64 e.loading = c 65 e.locker.Unlock() 66 67 c.v, c.err = e.loader(ctx, key) 68 c.wg.Done() 69 70 e.locker.Lock() 71 e.value = c.v 72 e.writeTime = time.Now() 73 e.loading = nil 74 e.locker.Unlock() 75 76 if c.err != nil { 77 return LoadNone, nil, c.err 78 } 79 return LoadSource, c.v, nil 80 } 81 82 type engine struct{} 83 84 // Load loads value from m, if there is no cached value, call the loader 85 // to get value, and then stores it into m. 86 func (d *engine) Load(ctx context.Context, m *sync.Map, key string, loader ResultLoader, arg OptionArg) (loadType LoadType, result Result, err error) { 87 actual, _ := m.LoadOrStore(key, &cacheItem{ 88 expireAfterWrite: arg.ExpireAfterWrite, 89 loader: loader, 90 }) 91 return actual.(*cacheItem).load(ctx, key) 92 }