Иван обнови решението на 18.11.2014 16:36 (преди над 3 години)
+package main
+
+import (
+ "sync"
+ "time"
+ //"fmt"
+ "errors"
+ "strconv"
+ "strings"
+)
+
+type MapItem struct {
+ data interface{}
+ expirationTime time.Time
+ wg sync.WaitGroup
+ rwm sync.RWMutex
+ del chan struct{}
+}
+
+type ExpireMap struct {
+ keys map[string]*MapItem
+ expired chan string
+ m sync.RWMutex
+ delWG sync.WaitGroup
+ expWG sync.WaitGroup
+}
+
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ item := MapItem{data: value, expirationTime: time.Now().Add(expire), del: make(chan struct{})}
+ _, ok := em.keys[key]
+
+ if ok {
+ em.Delete(key)
+ }
+
+ em.keys[key] = &item
+ destroy := func() {
+ delete(em.keys, key)
+ close(item.del)
+ // waiting for completion of all operations that are taking place when the timer is running out. Thats ok because timer was still on when they started
+ item.wg.Wait()
+ }
+
+ go func() {
+ timer := time.NewTimer(expire)
+
+ select {
+ case <-timer.C:
+ em.expWG.Add(1)
+ defer em.expWG.Done()
+ em.expired <- key
+ destroy()
+ return
+ case <-item.del:
+ em.delWG.Add(1)
+ defer em.delWG.Done()
+ timer.Stop()
+ destroy()
+ return
+ }
+ return
+ }()
+}
+
+func (em *ExpireMap) Get(key string) (d interface{}, k bool) {
+ em.m.RLock()
+ defer em.m.RUnlock()
+ data, ok := em.keys[key]
+ if ok {
+ data.wg.Add(1)
+ defer data.wg.Done()
+ d = data.data
+ }
+ k = ok
+ return
+}
+
+func (em *ExpireMap) GetInt(key string) (d int, k bool) {
+ data, ok := em.Get(key)
+ if ok {
+ d, k = data.(int)
+ }
+ return
+}
+
+func (em *ExpireMap) GetFloat64(key string) (d float64, k bool) {
+ data, ok := em.Get(key)
+ if ok {
+ d, k = data.(float64)
+ }
+ return
+}
+
+func (em *ExpireMap) GetString(key string) (d string, k bool) {
+ data, ok := em.Get(key)
+ if ok {
+ d, k = data.(string)
+ }
+ return
+}
+
+func (em *ExpireMap) GetBool(key string) (d bool, k bool) {
+ data, ok := em.Get(key)
+ if ok {
+ d, k = data.(bool)
+ }
+ return
+}
+
+func (em *ExpireMap) Expires(key string) (t time.Time, k bool) {
+ em.m.RLock()
+ defer em.m.RUnlock()
+ data, ok := em.keys[key]
+ if ok {
+ t = data.expirationTime
+ }
+ return
+}
+
+func (em *ExpireMap) Delete(key string) {
+ em.m.Lock()
+ defer em.m.Unlock()
+ data, ok := em.keys[key]
+ if ok {
+ data.wg.Add(1)
+ defer data.wg.Done()
+ data.del <- struct{}{}
+ }
+}
+
+func (em *ExpireMap) Contains(key string) bool {
+ em.m.RLock()
+ defer em.m.RUnlock()
+ _, ok := em.keys[key]
+ return ok
+}
+
+func (em *ExpireMap) Size() int {
+ em.m.RLock()
+ defer em.m.RUnlock()
+ return len(em.keys)
+}
+
+func (em *ExpireMap) plusMinus(key string, sign bool) error { // sign is true for +
+ num, ok := em.GetInt(key)
+ if !ok {
+ str, ok := em.GetString(key)
+ if !ok {
+ return errors.New("random error")
+ }
+ strint, err := strconv.ParseInt(str, 10, 64)
+ if err != nil {
+ return err
+ }
+ d := em.keys[key]
+ // ZA6TO NA MAIKA MU V PUTKATA NQMA TERNAREN OPERATOR V GO?? TOQ EZIK SUXVA QKO!!!
+ // d.data = strconv.FormatInt( sign ? strint + 1 : strint - 1, 10)
+ if sign {
+ d.data = strconv.FormatInt(strint+1, 10)
+ } else {
+ d.data = strconv.FormatInt(strint-1, 10)
+ }
+ } else {
+ d := em.keys[key]
+ // d.data = sign ? d.data.(int) + 1 : d.data.(int) - 1
+ if sign {
+ d.data = num + 1
+ } else {
+ d.data = num - 1
+ }
+ }
+ return nil
+}
+
+func (em *ExpireMap) Increment(key string) error {
+ em.m.RLock()
+ defer em.m.RUnlock()
+ return em.plusMinus(key, true)
+}
+
+func (em *ExpireMap) Decrement(key string) error {
+ em.m.RLock()
+ defer em.m.RUnlock()
+ return em.plusMinus(key, false)
+}
+
+func (em *ExpireMap) stringUpDown(key string, up bool) error {
+ _, ok := em.GetString(key)
+ if !ok {
+ return errors.New("random error")
+ }
+ data := em.keys[key]
+ if up {
+ data.data = strings.ToUpper(data.data.(string))
+ } else {
+ data.data = strings.ToLower(data.data.(string))
+ }
+ return nil
+}
+
+func (em *ExpireMap) ToUpper(key string) error {
+ em.m.RLock()
+ defer em.m.RUnlock()
+ return em.stringUpDown(key, true)
+}
+
+func (em *ExpireMap) ToLower(key string) error {
+ em.m.RLock()
+ defer em.m.RUnlock()
+ _, ok := em.GetString(key)
+ if !ok {
+ return errors.New("random error")
+ }
+ data := em.keys[key]
+ data.data = strings.ToLower(data.data.(string))
+ return nil
+}
+
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ return em.expired
+}
+
+func (em *ExpireMap) Cleanup() {
+
+ for item := range em.keys {
+ em.Delete(item)
+ }
+ em.delWG.Wait()
+}
+
+func (em *ExpireMap) Destroy() {
+ em.Cleanup()
+ em.expWG.Wait()
+ close(em.expired)
+}
+
+func NewExpireMap() *ExpireMap {
+ em := ExpireMap{}
+ em.keys = make(map[string]*MapItem)
+ em.expired = make(chan string, 1000)
+ return &em
+}