Калина обнови решението на 15.11.2014 23:50 (преди над 3 години)
+package main
+
+import (
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+ "fmt"
+)
+
+type element struct {
+ value interface{}
+ expire *time.Time
+}
+
+type ExpireMap struct {
+ sync.RWMutex
+ elements map[string]*element
+ stop chan string
+}
+
+func NewExpireMap() *ExpireMap {
+ return &ExpireMap{elements: map[string]*element{}, stop: make(chan string)}
+}
+
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ em.Lock()
+ timeExpire := time.Now().Add(expire)
+ em.elements[key] = &element{value: value, expire: &timeExpire}
+ em.Unlock()
+ go em.run(key, expire)
+}
+
+func (em *ExpireMap) Get(key string) (interface{}, bool) {
+ em.RLock()
+ elem, found := em.elements[key]
+ _, expire := em.Expires(key)
+ if !found || expire {
+ em.RUnlock()
+ return nil, false
+ }
+ switch elem.value.(type) {
+ case int:
+ em.RUnlock()
+ return em.GetInt(key)
+ case float64:
+ em.RUnlock()
+ return em.GetFloat64(key)
+ case string:
+ em.RUnlock()
+ return em.GetString(key)
+ case bool:
+ em.RUnlock()
+ return em.GetBool(key)
+ }
+ em.RUnlock()
+ return elem.value, true
+}
+
+func (em *ExpireMap) GetInt(key string) (int, bool) {
+ em.RLock()
+ elem, found := em.elements[key]
+ _, expire := em.Expires(key)
+ if !found || expire {
+ em.RUnlock()
+ return 0, false
+ }
+ switch elem.value.(type) {
+ case int:
+ em.RUnlock()
+ return elem.value.(int), true
+ }
+ em.RUnlock()
+ return 0, false
+}
+
+func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
+ em.RLock()
+ elem, found := em.elements[key]
+ _, expire := em.Expires(key)
+ if !found || expire {
+ em.RUnlock()
+ return 0, false
+ }
+ switch elem.value.(type) {
+ case float64:
+ em.RUnlock()
+ return elem.value.(float64), true
+ }
+ em.RUnlock()
+ return 0, false
+}
+
+func (em *ExpireMap) GetString(key string) (string, bool) {
+ em.RLock()
+ elem, found := em.elements[key]
+ _, expire := em.Expires(key)
+ if !found || expire {
+ em.RUnlock()
+ return "", false
+ }
+ switch elem.value.(type) {
+ case string:
+ em.RUnlock()
+ return elem.value.(string), true
+ }
+ em.RUnlock()
+ return "", false
+}
+
+func (em *ExpireMap) GetBool(key string) (bool, bool) {
+ em.RLock()
+ elem, found := em.elements[key]
+ _, expire := em.Expires(key)
+ if !found || expire {
+ em.RUnlock()
+ return false, false
+ }
+ switch elem.value.(type) {
+ case bool:
+ em.RUnlock()
+ return elem.value.(bool), true
+ }
+ em.RUnlock()
+ return false, false
+}
+
+func (em *ExpireMap) Expires(key string) (time.Time, bool) {
+ elem, found := em.elements[key]
+ if found && elem.expire == nil {
+ return *elem.expire, false
+ }
+ return *elem.expire, elem.expire.Before(time.Now())
+}
+
+func (em *ExpireMap) Delete(key string) {
+ em.Lock()
+ delete(em.elements, key)
+ em.Unlock()
+}
+
+func (em *ExpireMap) Contains(key string) bool {
+ em.Lock()
+ if _, ok := em.elements[key]; ok {
+ em.Unlock()
+ return true
+ }
+ em.Unlock()
+ return false
+}
+
+func (em *ExpireMap) Size() int {
+ em.RLock()
+ length := len(em.elements)
+ em.RUnlock()
+ return length
+}
+
+func (em *ExpireMap) Increment(key string) error {
+ em.Lock()
+ elem, found := em.elements[key]
+ if _, expire := em.Expires(key); !found || expire {
+ em.Unlock()
+ return fmt.Errorf("Not Found")
+ }
+ switch elem.value.(type) {
+ case int:
+ elem.value = elem.value.(int) + int(1)
+ case string:
+ if val, err := strconv.Atoi(elem.value.(string)); err == nil {
+ elem.value = strconv.Itoa(val + 1)
+ } else {
+ em.Unlock()
+ return fmt.Errorf("Not Increment by 1")
+ }
+ default:
+ em.Unlock()
+ return fmt.Errorf("Not Increment by 1")
+ }
+ em.Unlock()
+ return nil
+}
+
+func (em *ExpireMap) Decrement(key string) error {
+ em.Lock()
+ elem, found := em.elements[key]
+ _, expire := em.Expires(key)
+ if !found || expire {
+ em.Unlock()
+ return fmt.Errorf("Not Found")
+ }
+ switch elem.value.(type) {
+ case int:
+ elem.value = elem.value.(int) - int(1)
+ case string:
+ if val, err := strconv.Atoi(elem.value.(string)); err == nil {
+ elem.value = strconv.Itoa(val - 1)
+ } else {
+ em.Unlock()
+ return fmt.Errorf("Not Decrement by 1")
+ }
+ default:
+ em.Unlock()
+ return fmt.Errorf("Not Decrement by 1")
+ }
+ em.Unlock()
+ return nil
+}
+
+func (em *ExpireMap) ToUpper(key string) error {
+ em.Lock()
+ _, expires := em.Expires(key)
+ elem, found := em.elements[key]
+ if !found || expires {
+ em.Unlock()
+ return fmt.Errorf("Not Found")
+ }
+ switch elem.value.(type) {
+ case string:
+ elem.value = strings.ToUpper(elem.value.(string))
+ default:
+ em.Unlock()
+ return fmt.Errorf("error converting the string.")
+ }
+ em.Unlock()
+ return nil
+}
+
+func (em *ExpireMap) ToLower(key string) error {
+ em.Lock()
+ _, expires := em.Expires(key)
+ elem, found := em.elements[key]
+ if !found || expires {
+ em.Unlock()
+ return fmt.Errorf("Not Found")
+ }
+ switch elem.value.(type) {
+ case string:
+ elem.value = strings.ToLower(elem.value.(string))
+ default:
+ em.Unlock()
+ return fmt.Errorf("error converting the string.")
+ }
+ em.Unlock()
+ return nil
+}
+
+func (em *ExpireMap) run(key string, expire time.Duration) {
+ time.Sleep(expire)
+ em.Lock()
+ go func(out chan string, value string) {
+ out <- value
+ }(em.stop, key)
+ em.Unlock()
+}
+
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ return em.stop
+}
+
+func (em *ExpireMap) CleanUp() {
+ em.Lock()
+ em.elements = map[string]*element{}
+ em.Unlock()
+}
+
+func (em *ExpireMap) Destroy() {
+ for key, _ := range em.elements {
+ em.Delete(key)
+ }
+}
- CleanUp - трябва да е Cleanup - тоест не си си пускала тестовете >.>
- В допълнение ползвай defer за всичките Unlock-овете които може и някъде имаш deadlock който навярно е защото не ползваш defer.
Иначе на мен решението ми харесва ... малко не триеш като expire-не и Destroy не спира вървящи горутини и браво че си се сетила за type switch-а.