mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-03 21:08:25 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			196 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			196 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
 | 
						|
// All rights reserved.
 | 
						|
//
 | 
						|
// Use of this source code is governed by a BSD-style license that can be
 | 
						|
// found in the LICENSE file.
 | 
						|
 | 
						|
package cache
 | 
						|
 | 
						|
import (
 | 
						|
	"sync"
 | 
						|
	"unsafe"
 | 
						|
)
 | 
						|
 | 
						|
type lruNode struct {
 | 
						|
	n   *Node
 | 
						|
	h   *Handle
 | 
						|
	ban bool
 | 
						|
 | 
						|
	next, prev *lruNode
 | 
						|
}
 | 
						|
 | 
						|
func (n *lruNode) insert(at *lruNode) {
 | 
						|
	x := at.next
 | 
						|
	at.next = n
 | 
						|
	n.prev = at
 | 
						|
	n.next = x
 | 
						|
	x.prev = n
 | 
						|
}
 | 
						|
 | 
						|
func (n *lruNode) remove() {
 | 
						|
	if n.prev != nil {
 | 
						|
		n.prev.next = n.next
 | 
						|
		n.next.prev = n.prev
 | 
						|
		n.prev = nil
 | 
						|
		n.next = nil
 | 
						|
	} else {
 | 
						|
		panic("BUG: removing removed node")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type lru struct {
 | 
						|
	mu       sync.Mutex
 | 
						|
	capacity int
 | 
						|
	used     int
 | 
						|
	recent   lruNode
 | 
						|
}
 | 
						|
 | 
						|
func (r *lru) reset() {
 | 
						|
	r.recent.next = &r.recent
 | 
						|
	r.recent.prev = &r.recent
 | 
						|
	r.used = 0
 | 
						|
}
 | 
						|
 | 
						|
func (r *lru) Capacity() int {
 | 
						|
	r.mu.Lock()
 | 
						|
	defer r.mu.Unlock()
 | 
						|
	return r.capacity
 | 
						|
}
 | 
						|
 | 
						|
func (r *lru) SetCapacity(capacity int) {
 | 
						|
	var evicted []*lruNode
 | 
						|
 | 
						|
	r.mu.Lock()
 | 
						|
	r.capacity = capacity
 | 
						|
	for r.used > r.capacity {
 | 
						|
		rn := r.recent.prev
 | 
						|
		if rn == nil {
 | 
						|
			panic("BUG: invalid LRU used or capacity counter")
 | 
						|
		}
 | 
						|
		rn.remove()
 | 
						|
		rn.n.CacheData = nil
 | 
						|
		r.used -= rn.n.Size()
 | 
						|
		evicted = append(evicted, rn)
 | 
						|
	}
 | 
						|
	r.mu.Unlock()
 | 
						|
 | 
						|
	for _, rn := range evicted {
 | 
						|
		rn.h.Release()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (r *lru) Promote(n *Node) {
 | 
						|
	var evicted []*lruNode
 | 
						|
 | 
						|
	r.mu.Lock()
 | 
						|
	if n.CacheData == nil {
 | 
						|
		if n.Size() <= r.capacity {
 | 
						|
			rn := &lruNode{n: n, h: n.GetHandle()}
 | 
						|
			rn.insert(&r.recent)
 | 
						|
			n.CacheData = unsafe.Pointer(rn)
 | 
						|
			r.used += n.Size()
 | 
						|
 | 
						|
			for r.used > r.capacity {
 | 
						|
				rn := r.recent.prev
 | 
						|
				if rn == nil {
 | 
						|
					panic("BUG: invalid LRU used or capacity counter")
 | 
						|
				}
 | 
						|
				rn.remove()
 | 
						|
				rn.n.CacheData = nil
 | 
						|
				r.used -= rn.n.Size()
 | 
						|
				evicted = append(evicted, rn)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		rn := (*lruNode)(n.CacheData)
 | 
						|
		if !rn.ban {
 | 
						|
			rn.remove()
 | 
						|
			rn.insert(&r.recent)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	r.mu.Unlock()
 | 
						|
 | 
						|
	for _, rn := range evicted {
 | 
						|
		rn.h.Release()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (r *lru) Ban(n *Node) {
 | 
						|
	r.mu.Lock()
 | 
						|
	if n.CacheData == nil {
 | 
						|
		n.CacheData = unsafe.Pointer(&lruNode{n: n, ban: true})
 | 
						|
	} else {
 | 
						|
		rn := (*lruNode)(n.CacheData)
 | 
						|
		if !rn.ban {
 | 
						|
			rn.remove()
 | 
						|
			rn.ban = true
 | 
						|
			r.used -= rn.n.Size()
 | 
						|
			r.mu.Unlock()
 | 
						|
 | 
						|
			rn.h.Release()
 | 
						|
			rn.h = nil
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
	r.mu.Unlock()
 | 
						|
}
 | 
						|
 | 
						|
func (r *lru) Evict(n *Node) {
 | 
						|
	r.mu.Lock()
 | 
						|
	rn := (*lruNode)(n.CacheData)
 | 
						|
	if rn == nil || rn.ban {
 | 
						|
		r.mu.Unlock()
 | 
						|
		return
 | 
						|
	}
 | 
						|
	n.CacheData = nil
 | 
						|
	r.mu.Unlock()
 | 
						|
 | 
						|
	rn.h.Release()
 | 
						|
}
 | 
						|
 | 
						|
func (r *lru) EvictNS(ns uint64) {
 | 
						|
	var evicted []*lruNode
 | 
						|
 | 
						|
	r.mu.Lock()
 | 
						|
	for e := r.recent.prev; e != &r.recent; {
 | 
						|
		rn := e
 | 
						|
		e = e.prev
 | 
						|
		if rn.n.NS() == ns {
 | 
						|
			rn.remove()
 | 
						|
			rn.n.CacheData = nil
 | 
						|
			r.used -= rn.n.Size()
 | 
						|
			evicted = append(evicted, rn)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	r.mu.Unlock()
 | 
						|
 | 
						|
	for _, rn := range evicted {
 | 
						|
		rn.h.Release()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (r *lru) EvictAll() {
 | 
						|
	r.mu.Lock()
 | 
						|
	back := r.recent.prev
 | 
						|
	for rn := back; rn != &r.recent; rn = rn.prev {
 | 
						|
		rn.n.CacheData = nil
 | 
						|
	}
 | 
						|
	r.reset()
 | 
						|
	r.mu.Unlock()
 | 
						|
 | 
						|
	for rn := back; rn != &r.recent; rn = rn.prev {
 | 
						|
		rn.h.Release()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (r *lru) Close() error {
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// NewLRU create a new LRU-cache.
 | 
						|
func NewLRU(capacity int) Cacher {
 | 
						|
	r := &lru{capacity: capacity}
 | 
						|
	r.reset()
 | 
						|
	return r
 | 
						|
}
 |