bilibili-backup/library/cache/cache.go
2019-04-22 02:59:20 +00:00

89 lines
1.4 KiB
Go

package cache
import (
"errors"
"runtime"
"sync"
"go-common/library/log"
"go-common/library/stat/prom"
)
var (
// ErrFull cache internal chan full.
ErrFull = errors.New("cache chan full")
stats = prom.BusinessInfoCount
)
// Cache async save data by chan.
type Cache struct {
ch chan func()
worker int
waiter sync.WaitGroup
}
// Deprecated: use library/sync/pipeline/fanout instead.
func New(worker, size int) *Cache {
if worker <= 0 {
worker = 1
}
c := &Cache{
ch: make(chan func(), size),
worker: worker,
}
c.waiter.Add(worker)
for i := 0; i < worker; i++ {
go c.proc()
}
return c
}
func (c *Cache) proc() {
defer c.waiter.Done()
for {
f := <-c.ch
if f == nil {
return
}
wrapFunc(f)()
stats.State("cache_channel", int64(len(c.ch)))
}
}
func wrapFunc(f func()) (res func()) {
res = func() {
defer func() {
if r := recover(); r != nil {
buf := make([]byte, 64*1024)
buf = buf[:runtime.Stack(buf, false)]
log.Error("panic in cache proc, err: %s, stack: %s", r, buf)
}
}()
f()
}
return
}
// Save save a callback cache func.
func (c *Cache) Save(f func()) (err error) {
if f == nil {
return
}
select {
case c.ch <- f:
default:
err = ErrFull
}
stats.State("cache_channel", int64(len(c.ch)))
return
}
// Close close cache
func (c *Cache) Close() (err error) {
for i := 0; i < c.worker; i++ {
c.ch <- nil
}
c.waiter.Wait()
return
}