Красимир обнови решението на 18.11.2014 16:26 (преди над 3 години)
+package main
+
+import (
+ "errors"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+type ExpireMap struct {
+ values map[string]interface{}
+ expirers map[string]time.Time
+ ch chan string
+ sync.RWMutex
+}
+
+func NewExpireMap() *ExpireMap {
+ return &ExpireMap{make(map[string]interface{}), make(map[string]time.Time), make(chan string, 1000), *new(sync.RWMutex)}
+}
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ em.Lock()
+ _, exist := em.values[key]
+ expireTime := time.Now().Add(expire)
+ em.values[key] = value
+ em.expirers[key] = expireTime
+ em.Unlock()
+ if !exist {
+ go func(key string) {
+ for {
+ em.Lock()
+ if time.Now().After(em.expirers[key]) {
+ delete(em.values, key)
+ delete(em.expirers, key)
+ if len(em.ch) == cap(em.ch) {
+ newCh := make(chan string, 2*cap(em.ch))
+ for i := 0; i < cap(em.ch); i++ {
+ newCh <- <-em.ch
+ }
+ em.ch = newCh
+ }
+ em.ch <- key
+ em.Unlock()
+ return
+ } else {
+ em.Unlock()
+ }
+ }
+ }(key)
+ }
+}
+func (em *ExpireMap) Get(key string) (interface{}, bool) {
+ em.RLock()
+ value, err := em.values[key]
+ em.RUnlock()
+ return value, err
+}
+func (em *ExpireMap) GetInt(key string) (int, bool) {
+ em.RLock()
+ value, err := em.values[key].(int)
+ em.RUnlock()
+ return value, err
+}
+func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
+ em.RLock()
+ value, err := em.values[key].(float64)
+ em.RUnlock()
+ return value, err
+}
+func (em *ExpireMap) GetString(key string) (string, bool) {
+ em.RLock()
+ value, err := em.values[key].(string)
+ em.RUnlock()
+ return value, err
+}
+func (em *ExpireMap) GetBool(key string) (bool, bool) {
+ em.RLock()
+ value, err := em.values[key].(bool)
+ em.RUnlock()
+ return value, err
+}
+func (em *ExpireMap) Expires(key string) (time.Time, bool) {
+ em.RLock()
+ value, err := em.expirers[key]
+ em.RUnlock()
+ return value, err
+}
+func (em *ExpireMap) Delete(key string) {
+ em.Lock()
+ delete(em.values, key)
+ delete(em.expirers, key)
+ em.Unlock()
+}
+func (em *ExpireMap) Contains(key string) bool {
+ em.RLock()
+ _, exist := em.expirers[key] //faster than map with interface{}?
+ em.RUnlock()
+ return exist
+}
+func (em *ExpireMap) Size() int {
+ return len(em.values)
+}
+func (em *ExpireMap) add(key string, n int) error {
+ em.Lock()
+ valueI, isInt := em.values[key].(int)
+ if isInt {
+ valueI += n
+ em.values[key] = valueI
+ em.Unlock()
+ return nil
+ }
+ valueS, isString := em.values[key].(string)
+ if isString {
+ num, err := strconv.Atoi(valueS)
+ if err == nil {
+ num += n
+ em.values[key] = strconv.Itoa(num)
+ em.Unlock()
+ return nil
+ }
+ }
+ em.Unlock()
+ return errors.New("Doesn't exist or not an incrementable type")
+}
+func (em *ExpireMap) Increment(key string) error {
+ return em.add(key, 1)
+}
+func (em *ExpireMap) Decrement(key string) error {
+ return em.add(key, -1)
+}
+func (em *ExpireMap) stringFunc(key string, f func(string) string) error {
+ em.Lock()
+ value, isString := em.values[key].(string)
+ if isString {
+ value = f(value)
+ em.values[key] = value
+ em.Unlock()
+ return nil
+ }
+ em.Unlock()
+ return errors.New("Doesn't exist or not a string")
+}
+func (em *ExpireMap) ToUpper(key string) error {
+ return em.stringFunc(key, strings.ToUpper)
+}
+func (em *ExpireMap) ToLower(key string) error {
+ return em.stringFunc(key, strings.ToLower)
+}
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ return em.ch
+}
+func (em *ExpireMap) Cleanup() {
+ em.Lock()
+ em.values = make(map[string]interface{})
+ em.expirers = make(map[string]time.Time)
+ em.ch = make(chan string)
+ em.Unlock()
+}
+func (em *ExpireMap) Destroy() {
+ go func() {
+ em.Lock()
+ em.values = nil
+ em.expirers = nil
+ close(em.ch)
+ }()
+}