mirror of
https://github.com/anyproto/any-sync.git
synced 2025-06-08 05:57:03 +09:00
136 lines
2.4 KiB
Go
136 lines
2.4 KiB
Go
package ocache
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type entryState int
|
|
|
|
const (
|
|
entryStateLoading = iota
|
|
entryStateActive
|
|
entryStateClosing
|
|
entryStateClosed
|
|
)
|
|
|
|
type entry struct {
|
|
id string
|
|
state entryState
|
|
lastUsage time.Time
|
|
load chan struct{}
|
|
loadErr error
|
|
value Object
|
|
close chan struct{}
|
|
mx sync.Mutex
|
|
cancel context.CancelFunc
|
|
}
|
|
|
|
func newEntry(id string, value Object, state entryState) *entry {
|
|
return &entry{
|
|
id: id,
|
|
load: make(chan struct{}),
|
|
lastUsage: time.Now(),
|
|
state: state,
|
|
value: value,
|
|
}
|
|
}
|
|
|
|
func (e *entry) isActive() bool {
|
|
e.mx.Lock()
|
|
defer e.mx.Unlock()
|
|
return e.state == entryStateActive
|
|
}
|
|
|
|
func (e *entry) isClosing() bool {
|
|
e.mx.Lock()
|
|
defer e.mx.Unlock()
|
|
return e.state == entryStateClosed || e.state == entryStateClosing
|
|
}
|
|
|
|
func (e *entry) setCancel(cancel context.CancelFunc) {
|
|
e.mx.Lock()
|
|
defer e.mx.Unlock()
|
|
e.cancel = cancel
|
|
}
|
|
|
|
func (e *entry) cancelLoad() {
|
|
e.mx.Lock()
|
|
defer e.mx.Unlock()
|
|
if e.cancel != nil {
|
|
e.cancel()
|
|
}
|
|
}
|
|
|
|
func (e *entry) waitLoad(ctx context.Context, id string) (value Object, err error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
log.DebugCtx(ctx, "ctx done while waiting on object load", zap.String("id", id))
|
|
return nil, ctx.Err()
|
|
case <-e.load:
|
|
return e.value, e.loadErr
|
|
}
|
|
}
|
|
|
|
func (e *entry) waitClose(ctx context.Context, id string) (res bool, err error) {
|
|
e.mx.Lock()
|
|
switch e.state {
|
|
case entryStateClosing:
|
|
waitCh := e.close
|
|
e.mx.Unlock()
|
|
select {
|
|
case <-ctx.Done():
|
|
log.DebugCtx(ctx, "ctx done while waiting on object close", zap.String("id", id))
|
|
return false, ctx.Err()
|
|
case <-waitCh:
|
|
return true, nil
|
|
}
|
|
case entryStateClosed:
|
|
e.mx.Unlock()
|
|
return true, nil
|
|
default:
|
|
e.mx.Unlock()
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
func (e *entry) setClosing(wait bool) (prevState, curState entryState) {
|
|
e.mx.Lock()
|
|
prevState = e.state
|
|
curState = e.state
|
|
if e.state == entryStateClosing {
|
|
waitCh := e.close
|
|
e.mx.Unlock()
|
|
if !wait {
|
|
return
|
|
}
|
|
<-waitCh
|
|
e.mx.Lock()
|
|
}
|
|
if e.state != entryStateClosed {
|
|
e.state = entryStateClosing
|
|
e.close = make(chan struct{})
|
|
}
|
|
curState = e.state
|
|
e.mx.Unlock()
|
|
return
|
|
}
|
|
|
|
func (e *entry) setActive(chClose bool) {
|
|
e.mx.Lock()
|
|
defer e.mx.Unlock()
|
|
if chClose {
|
|
close(e.close)
|
|
}
|
|
e.state = entryStateActive
|
|
}
|
|
|
|
func (e *entry) setClosed() {
|
|
e.mx.Lock()
|
|
defer e.mx.Unlock()
|
|
close(e.close)
|
|
e.state = entryStateClosed
|
|
}
|