1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-07-01 03:25:23 +00:00
v2fly/app/stats/stats.go

199 lines
4.5 KiB
Go
Raw Normal View History

2019-02-01 19:08:21 +00:00
// +build !confonly
2018-03-30 17:56:59 +00:00
package stats
2018-09-30 21:08:41 +00:00
//go:generate errorgen
2018-03-30 17:56:59 +00:00
import (
"context"
"sync"
"sync/atomic"
"time"
2018-03-30 17:56:59 +00:00
"v2ray.com/core/features/stats"
2018-03-30 17:56:59 +00:00
)
// Counter is an implementation of stats.Counter.
2018-03-30 17:56:59 +00:00
type Counter struct {
value int64
}
// Value implements stats.Counter.
2018-03-30 17:56:59 +00:00
func (c *Counter) Value() int64 {
return atomic.LoadInt64(&c.value)
}
// Set implements stats.Counter.
2018-03-30 21:17:28 +00:00
func (c *Counter) Set(newValue int64) int64 {
2018-03-30 17:56:59 +00:00
return atomic.SwapInt64(&c.value, newValue)
}
// Add implements stats.Counter.
2018-03-30 17:56:59 +00:00
func (c *Counter) Add(delta int64) int64 {
return atomic.AddInt64(&c.value, delta)
}
// Channel is an implementation of stats.Channel
type Channel struct {
channel chan interface{}
subscribers []chan interface{}
access sync.RWMutex
}
// Channel implements stats.Channel
func (c *Channel) Channel() chan interface{} {
return c.channel
}
// Subscribers implements stats.Channel
func (c *Channel) Subscribers() []chan interface{} {
c.access.RLock()
defer c.access.RUnlock()
return c.subscribers
}
// Subscribe implements stats.Channel
func (c *Channel) Subscribe() chan interface{} {
c.access.Lock()
defer c.access.Unlock()
ch := make(chan interface{})
c.subscribers = append(c.subscribers, ch)
return ch
}
// Unsubscribe implements stats.Channel
func (c *Channel) Unsubscribe(ch chan interface{}) {
c.access.Lock()
defer c.access.Unlock()
for i, s := range c.subscribers {
if s == ch {
// Copy to new memory block to prevent modifying original data
subscribers := make([]chan interface{}, len(c.subscribers)-1)
copy(subscribers[:i], c.subscribers[:i])
copy(subscribers[i:], c.subscribers[i+1:])
c.subscribers = subscribers
return
}
}
}
// Start starts the channel for listening to messsages
func (c *Channel) Start() {
for message := range c.Channel() {
subscribers := c.Subscribers() // Store a copy of slice value for concurrency safety
for _, sub := range subscribers {
select {
case sub <- message: // Successfully sent message
case <-time.After(100 * time.Millisecond):
c.Unsubscribe(sub) // Remove timeout subscriber
close(sub) // Actively close subscriber as notification
}
}
}
}
// Manager is an implementation of stats.Manager.
2018-03-30 17:56:59 +00:00
type Manager struct {
access sync.RWMutex
counters map[string]*Counter
channels map[string]*Channel
2018-03-30 17:56:59 +00:00
}
func NewManager(ctx context.Context, config *Config) (*Manager, error) {
2018-03-31 08:30:12 +00:00
m := &Manager{
2018-03-30 17:56:59 +00:00
counters: make(map[string]*Counter),
channels: make(map[string]*Channel),
2018-03-31 08:30:12 +00:00
}
return m, nil
2018-03-30 17:56:59 +00:00
}
2018-10-12 21:57:56 +00:00
func (*Manager) Type() interface{} {
return stats.ManagerType()
}
// RegisterCounter implements stats.Manager.
func (m *Manager) RegisterCounter(name string) (stats.Counter, error) {
2018-03-30 17:56:59 +00:00
m.access.Lock()
defer m.access.Unlock()
if _, found := m.counters[name]; found {
return nil, newError("Counter ", name, " already registered.")
}
2018-03-31 08:30:12 +00:00
newError("create new counter ", name).AtDebug().WriteToLog()
2018-03-30 17:56:59 +00:00
c := new(Counter)
m.counters[name] = c
return c, nil
}
// UnregisterCounter implements stats.Manager.
func (m *Manager) UnregisterCounter(name string) error {
m.access.Lock()
defer m.access.Unlock()
if _, found := m.counters[name]; !found {
return newError("Counter ", name, " was not found.")
}
newError("remove counter ", name).AtDebug().WriteToLog()
delete(m.counters, name)
return nil
}
// GetCounter implements stats.Manager.
func (m *Manager) GetCounter(name string) stats.Counter {
2018-03-30 17:56:59 +00:00
m.access.RLock()
defer m.access.RUnlock()
if c, found := m.counters[name]; found {
return c
}
return nil
}
// VisitCounters calls visitor function on all managed counters.
func (m *Manager) VisitCounters(visitor func(string, stats.Counter) bool) {
2018-07-10 21:40:58 +00:00
m.access.RLock()
defer m.access.RUnlock()
for name, c := range m.counters {
if !visitor(name, c) {
break
}
}
}
// RegisterChannel implements stats.Manager.
func (m *Manager) RegisterChannel(name string) (stats.Channel, error) {
m.access.Lock()
defer m.access.Unlock()
if _, found := m.channels[name]; found {
return nil, newError("Channel ", name, " already registered.")
}
newError("create new channel ", name).AtDebug().WriteToLog()
c := &Channel{channel: make(chan interface{})}
m.channels[name] = c
go c.Start()
return c, nil
}
// GetChannel implements stats.Manager.
func (m *Manager) GetChannel(name string) stats.Channel {
m.access.RLock()
defer m.access.RUnlock()
if c, found := m.channels[name]; found {
return c
}
return nil
}
2018-05-25 21:20:24 +00:00
// Start implements common.Runnable.
2018-03-30 17:56:59 +00:00
func (m *Manager) Start() error {
return nil
}
2018-05-25 21:20:24 +00:00
// Close implement common.Closable.
2018-03-30 17:56:59 +00:00
func (m *Manager) Close() error {
return nil
}