Решение на ExpireMap от Калина Бухлева

Обратно към всички решения

Към профила на Калина Бухлева

Резултати

  • 8 точки от тестове
  • 0 бонус точки
  • 8 точки общо
  • 19 успешни тест(а)
  • 6 неуспешни тест(а)

Код

package main
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
)
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()
defer em.Unlock()
if _, ok := em.elements[key]; ok {
delete(em.elements, key)
}
timeExpire := time.Now().Add(expire)
em.elements[key] = &element{value: value, expire: &timeExpire}
go em.run(key, expire)
}
func (em *ExpireMap) Get(key string) (interface{}, bool) {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return nil, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return nil, false
}
switch elem.value.(type) {
case int:
return em.GetInt(key)
case float64:
return em.GetFloat64(key)
case string:
return em.GetString(key)
case bool:
return em.GetBool(key)
}
return elem.value, true
}
func (em *ExpireMap) GetInt(key string) (int, bool) {
elem, found := em.elements[key]
if !found {
return 0, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return 0, false
}
switch elem.value.(type) {
case int:
return elem.value.(int), true
}
return 0, false
}
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
elem, found := em.elements[key]
if !found {
return 0, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return 0, false
}
switch elem.value.(type) {
case float64:
return elem.value.(float64), true
}
return 0, false
}
func (em *ExpireMap) GetString(key string) (string, bool) {
elem, found := em.elements[key]
if !found {
return "", false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return "", false
}
switch elem.value.(type) {
case string:
return elem.value.(string), true
}
return "", false
}
func (em *ExpireMap) GetBool(key string) (bool, bool) {
elem, found := em.elements[key]
if !found {
return false, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return false, false
}
switch elem.value.(type) {
case bool:
return elem.value.(bool), true
}
return false, false
}
func (em *ExpireMap) Expires(key string) (time.Time, bool) {
elem, found := em.elements[key]
if !found {
return time.Now(), true
}
if elem.expire == nil {
return time.Now(), true
}
return *elem.expire, elem.expire.Before(time.Now())
}
func (em *ExpireMap) Delete(key string) {
em.Lock()
defer em.Unlock()
delete(em.elements, key)
}
func (em *ExpireMap) Contains(key string) bool {
em.Lock()
defer em.Unlock()
if _, ok := em.elements[key]; ok {
return true
}
return false
}
func (em *ExpireMap) Size() int {
em.Lock()
defer em.Unlock()
length := len(em.elements)
return length
}
func (em *ExpireMap) Increment(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
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 {
return fmt.Errorf("Not Increment by 1")
}
default:
return fmt.Errorf("Not Increment by 1")
}
return nil
}
func (em *ExpireMap) Decrement(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
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 {
return fmt.Errorf("Not Decrement by 1")
}
default:
return fmt.Errorf("Not Decrement by 1")
}
return nil
}
func (em *ExpireMap) ToUpper(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return fmt.Errorf("Not Found")
}
switch elem.value.(type) {
case string:
elem.value = strings.ToUpper(elem.value.(string))
default:
return fmt.Errorf("error converting the string.")
}
return nil
}
func (em *ExpireMap) ToLower(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return fmt.Errorf("Not Found")
}
switch elem.value.(type) {
case string:
elem.value = strings.ToLower(elem.value.(string))
default:
return fmt.Errorf("error converting the string.")
}
return nil
}
func (em *ExpireMap) run(key string, expire time.Duration) {
timer := time.NewTimer(expire)
go func() {
select {
case <-timer.C:
em.Lock()
defer em.Unlock()
_, expire := em.Expires(key)
if _, ok := em.elements[key]; ok && expire {
go func(out chan string, value string) {
out <- value
}(em.stop, key)
}
}
}()
}
func (em *ExpireMap) ExpiredChan() <-chan string {
return em.stop
}
func (em *ExpireMap) Cleanup() {
em.Lock()
defer em.Unlock()
em.elements = map[string]*element{}
}
func (em *ExpireMap) Destroy() {
for key, _ := range em.elements {
em.Delete(key)
}
}

Лог от изпълнението

PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.012s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.012s
panic: test timed out

goroutine 25 [running]:
testing.alarm()
	/usr/local/lib/go/src/pkg/testing/testing.go:533 +0x44
created by time.goFunc
	/usr/local/lib/go/src/pkg/time/sleep.go:122 +0x45

goroutine 1 [chan receive]:
testing.RunTests(0x8148858, 0x81c2ba0, 0x19, 0x19, 0x1, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:434 +0x69f
testing.Main(0x8148858, 0x81c2ba0, 0x19, 0x19, 0x81c5600, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:365 +0x69
main.main()
	_/tmp/d20141204-6466-19vakk3/_test/_testmain.go:91 +0x81

goroutine 4 [sleep]:
time.Sleep(0x3c336080, 0x0)
	/usr/local/lib/go/src/pkg/runtime/ztime_linux_386.c:19 +0x3a
_/tmp/d20141204-6466-19vakk3.TestSizes(0x183701e0)
	/tmp/d20141204-6466-19vakk3/solution_test.go:111 +0x395
testing.tRunner(0x183701e0, 0x81c2bb8)
	/usr/local/lib/go/src/pkg/testing/testing.go:353 +0x87
created by testing.RunTests
	/usr/local/lib/go/src/pkg/testing/testing.go:433 +0x684

goroutine 16 [chan receive]:
_/tmp/d20141204-6466-19vakk3.func·002()
	/tmp/d20141204-6466-19vakk3/solution.go:264 +0x53
created by _/tmp/d20141204-6466-19vakk3.(*ExpireMap).run
	/tmp/d20141204-6466-19vakk3/solution.go:274 +0xbb

goroutine 17 [chan receive]:
_/tmp/d20141204-6466-19vakk3.func·002()
	/tmp/d20141204-6466-19vakk3/solution.go:264 +0x53
created by _/tmp/d20141204-6466-19vakk3.(*ExpireMap).run
	/tmp/d20141204-6466-19vakk3/solution.go:274 +0xbb

goroutine 18 [chan receive]:
_/tmp/d20141204-6466-19vakk3.func·002()
	/tmp/d20141204-6466-19vakk3/solution.go:264 +0x53
created by _/tmp/d20141204-6466-19vakk3.(*ExpireMap).run
	/tmp/d20141204-6466-19vakk3/solution.go:274 +0xbb

goroutine 19 [chan receive]:
_/tmp/d20141204-6466-19vakk3.func·002()
	/tmp/d20141204-6466-19vakk3/solution.go:264 +0x53
created by _/tmp/d20141204-6466-19vakk3.(*ExpireMap).run
	/tmp/d20141204-6466-19vakk3/solution.go:274 +0xbb

goroutine 20 [chan receive]:
_/tmp/d20141204-6466-19vakk3.func·002()
	/tmp/d20141204-6466-19vakk3/solution.go:264 +0x53
created by _/tmp/d20141204-6466-19vakk3.(*ExpireMap).run
	/tmp/d20141204-6466-19vakk3/solution.go:274 +0xbb

goroutine 21 [chan receive]:
_/tmp/d20141204-6466-19vakk3.func·002()
	/tmp/d20141204-6466-19vakk3/solution.go:264 +0x53
created by _/tmp/d20141204-6466-19vakk3.(*ExpireMap).run
	/tmp/d20141204-6466-19vakk3/solution.go:274 +0xbb

goroutine 22 [chan receive]:
_/tmp/d20141204-6466-19vakk3.func·002()
	/tmp/d20141204-6466-19vakk3/solution.go:264 +0x53
created by _/tmp/d20141204-6466-19vakk3.(*ExpireMap).run
	/tmp/d20141204-6466-19vakk3/solution.go:274 +0xbb

goroutine 23 [chan receive]:
_/tmp/d20141204-6466-19vakk3.func·002()
	/tmp/d20141204-6466-19vakk3/solution.go:264 +0x53
created by _/tmp/d20141204-6466-19vakk3.(*ExpireMap).run
	/tmp/d20141204-6466-19vakk3/solution.go:274 +0xbb

goroutine 24 [chan receive]:
_/tmp/d20141204-6466-19vakk3.func·002()
	/tmp/d20141204-6466-19vakk3/solution.go:264 +0x53
created by _/tmp/d20141204-6466-19vakk3.(*ExpireMap).run
	/tmp/d20141204-6466-19vakk3/solution.go:274 +0xbb

goroutine 15 [chan receive]:
_/tmp/d20141204-6466-19vakk3.func·002()
	/tmp/d20141204-6466-19vakk3/solution.go:264 +0x53
created by _/tmp/d20141204-6466-19vakk3.(*ExpireMap).run
	/tmp/d20141204-6466-19vakk3/solution.go:274 +0xbb
exit status 2
FAIL	_/tmp/d20141204-6466-19vakk3	1.014s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.122s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.172s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.012s
--- FAIL: TestDeleteMethod-2 (0.00 seconds)
	solution_test.go:215: Key foo expires did not get deleted
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-19vakk3	0.012s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.015s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.012s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.053s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.012s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.012s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.012s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.012s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.112s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.062s
--- FAIL: TestExpiredChanWhenNoOneIsReading-2 (0.06 seconds)
	solution_test.go:554: Wrong key expired
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-19vakk3	0.072s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.112s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.132s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.238s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.012s
--- FAIL: TestDestroyMethodClosesExpireChannel-2 (0.05 seconds)
	solution_test.go:818: Expire channel was not closed in time
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-19vakk3	0.062s
--- FAIL: TestExpiresMethod-2 (0.00 seconds)
	solution_test.go:843: Did not find expires for key key1
	solution_test.go:843: Did not find expires for key key2
	solution_test.go:843: Did not find expires for key key3
	solution_test.go:869: Expires returnd ok for key which is not there
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-19vakk3	0.012s
panic: test timed out

goroutine 50 [running]:
testing.alarm()
	/usr/local/lib/go/src/pkg/testing/testing.go:533 +0x44
created by time.goFunc
	/usr/local/lib/go/src/pkg/time/sleep.go:122 +0x45

goroutine 1 [chan receive]:
testing.RunTests(0x8148858, 0x81c2ba0, 0x19, 0x19, 0x1, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:434 +0x69f
testing.Main(0x8148858, 0x81c2ba0, 0x19, 0x19, 0x81c5600, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:365 +0x69
main.main()
	_/tmp/d20141204-6466-19vakk3/_test/_testmain.go:91 +0x81

goroutine 4 [semacquire]:
sync.runtime_Semacquire(0x18331684)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18331680)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
sync.(*RWMutex).Lock(0x18331680)
	/usr/local/lib/go/src/pkg/sync/rwmutex.go:78 +0x29
_/tmp/d20141204-6466-19vakk3.(*ExpireMap).Delete(0x18331680, 0x18351060, 0xf)
	/tmp/d20141204-6466-19vakk3/solution.go:143 +0x29
_/tmp/d20141204-6466-19vakk3.(*ExpireMap).Get(0x18331680, 0x18351060, 0xf, 0x0, 0x0, ...)
	/tmp/d20141204-6466-19vakk3/solution.go:47 +0xf2
_/tmp/d20141204-6466-19vakk3.TestMultipleKeysWithTheSameExpireTime(0x18363120)
	/tmp/d20141204-6466-19vakk3/solution_test.go:893 +0x534
testing.tRunner(0x18363120, 0x81c2cb4)
	/usr/local/lib/go/src/pkg/testing/testing.go:353 +0x87
created by testing.RunTests
	/usr/local/lib/go/src/pkg/testing/testing.go:433 +0x684

goroutine 49 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x18351060, 0xf)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 48 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x18351070, 0xf)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 47 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x18351090, 0xf)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 46 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x183510a0, 0xf)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 45 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x183510b0, 0xf)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 44 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x183510c0, 0xf)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 43 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x183510d0, 0xf)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 42 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x183510e0, 0xf)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 40 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x18351100, 0xf)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 39 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x183316e0, 0x10)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 38 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x18331700, 0x10)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 37 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x18331720, 0x10)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 36 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x18331740, 0x10)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 41 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x183510f0, 0xf)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104

goroutine 35 [chan send]:
_/tmp/d20141204-6466-19vakk3.func·001(0x18348270, 0x18331760, 0x10)
	/tmp/d20141204-6466-19vakk3/solution.go:270 +0x41
created by _/tmp/d20141204-6466-19vakk3.func·002
	/tmp/d20141204-6466-19vakk3/solution.go:271 +0x104
exit status 2
FAIL	_/tmp/d20141204-6466-19vakk3	1.016s
PASS
ok  	_/tmp/d20141204-6466-19vakk3	0.022s

История (4 версии и 3 коментара)

Калина обнови решението на 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-а.

Калина обнови решението на 17.11.2014 10:17 (преди над 3 години)

package main
import (
+ "fmt"
"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()
+ defer em.Unlock()
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()
+ em.Lock()
+ defer em.Unlock()
elem, found := em.elements[key]
+ if !found {
+ return nil, false
+ }
_, expire := em.Expires(key)
- if !found || expire {
- em.RUnlock()
+ if expire {
+ em.Delete(key)
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]
+ if !found {
+ return 0, false
+ }
_, expire := em.Expires(key)
- if !found || expire {
- em.RUnlock()
+ if expire {
+ em.Delete(key)
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]
+ if !found {
+ return 0, false
+ }
_, expire := em.Expires(key)
- if !found || expire {
- em.RUnlock()
+ if expire {
+ em.Delete(key)
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]
+ if !found {
+ return "", false
+ }
_, expire := em.Expires(key)
- if !found || expire {
- em.RUnlock()
+ if expire {
+ em.Delete(key)
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]
+ if !found {
+ return false, false
+ }
_, expire := em.Expires(key)
- if !found || expire {
- em.RUnlock()
+ if expire {
+ em.Delete(key)
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()
+ defer em.Unlock()
delete(em.elements, key)
- em.Unlock()
}
func (em *ExpireMap) Contains(key string) bool {
em.Lock()
+ defer em.Unlock()
if _, ok := em.elements[key]; ok {
- em.Unlock()
return true
}
- em.Unlock()
return false
}
func (em *ExpireMap) Size() int {
- em.RLock()
+ em.Lock()
+ defer em.Unlock()
length := len(em.elements)
- em.RUnlock()
return length
}
func (em *ExpireMap) Increment(key string) error {
em.Lock()
+ defer em.Unlock()
elem, found := em.elements[key]
- if _, expire := em.Expires(key); !found || expire {
- em.Unlock()
+ if !found {
return fmt.Errorf("Not Found")
}
+ _, expire := em.Expires(key)
+ if expire {
+ em.Delete(key)
+ 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()
+ defer em.Unlock()
elem, found := em.elements[key]
+ if !found {
+ return fmt.Errorf("Not Found")
+ }
_, expire := em.Expires(key)
- if !found || expire {
- em.Unlock()
+ if expire {
+ em.Delete(key)
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)
+ defer em.Unlock()
elem, found := em.elements[key]
- if !found || expires {
- em.Unlock()
+ if !found {
return fmt.Errorf("Not Found")
}
+ _, expire := em.Expires(key)
+ if expire {
+ em.Delete(key)
+ 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)
+ defer em.Unlock()
elem, found := em.elements[key]
- if !found || expires {
- em.Unlock()
+ if !found {
return fmt.Errorf("Not Found")
}
+ _, expire := em.Expires(key)
+ if expire {
+ em.Delete(key)
+ 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()
+ timer := time.NewTimer(expire)
+ go func() {
+ select {
+ case <-timer.C:
+ em.Lock()
+ defer em.Unlock()
+ go func(out chan string, value string) {
+ out <- value
+ }(em.stop, key)
+ }
+ }()
}
func (em *ExpireMap) ExpiredChan() <-chan string {
return em.stop
}
-func (em *ExpireMap) CleanUp() {
+func (em *ExpireMap) Cleanup() {
em.Lock()
+ defer em.Unlock()
em.elements = map[string]*element{}
- em.Unlock()
}
func (em *ExpireMap) Destroy() {
+ quit := make(chan bool)
+ go func() {
+ for {
+ select {
+ case <- quit:
+ return
+ }
+ }
+ }()
+ quit <- true
for key, _ := range em.elements {
em.Delete(key)
}
}

Калина обнови решението на 17.11.2014 17:42 (преди над 3 години)

package main
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
)
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()
defer em.Unlock()
+ if _, ok := em.elements[key]; ok {
+ delete(em.elements, key)
+ }
timeExpire := time.Now().Add(expire)
em.elements[key] = &element{value: value, expire: &timeExpire}
go em.run(key, expire)
+
}
func (em *ExpireMap) Get(key string) (interface{}, bool) {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return nil, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return nil, false
}
switch elem.value.(type) {
case int:
return em.GetInt(key)
case float64:
return em.GetFloat64(key)
case string:
return em.GetString(key)
case bool:
return em.GetBool(key)
}
return elem.value, true
}
func (em *ExpireMap) GetInt(key string) (int, bool) {
elem, found := em.elements[key]
if !found {
return 0, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return 0, false
}
switch elem.value.(type) {
case int:
return elem.value.(int), true
}
return 0, false
}
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
elem, found := em.elements[key]
if !found {
return 0, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return 0, false
}
switch elem.value.(type) {
case float64:
return elem.value.(float64), true
}
return 0, false
}
func (em *ExpireMap) GetString(key string) (string, bool) {
elem, found := em.elements[key]
if !found {
return "", false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return "", false
}
switch elem.value.(type) {
case string:
return elem.value.(string), true
}
return "", false
}
func (em *ExpireMap) GetBool(key string) (bool, bool) {
elem, found := em.elements[key]
if !found {
return false, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return false, false
}
switch elem.value.(type) {
case bool:
return elem.value.(bool), true
}
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
+ if !found {
+ return time.Now(), true
}
+ if elem.expire == nil {
+ return time.Now(), true
+ }
return *elem.expire, elem.expire.Before(time.Now())
}
func (em *ExpireMap) Delete(key string) {
em.Lock()
defer em.Unlock()
delete(em.elements, key)
}
func (em *ExpireMap) Contains(key string) bool {
em.Lock()
defer em.Unlock()
if _, ok := em.elements[key]; ok {
return true
}
return false
}
func (em *ExpireMap) Size() int {
em.Lock()
defer em.Unlock()
length := len(em.elements)
return length
}
func (em *ExpireMap) Increment(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
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 {
return fmt.Errorf("Not Increment by 1")
}
default:
return fmt.Errorf("Not Increment by 1")
}
return nil
}
func (em *ExpireMap) Decrement(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
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 {
return fmt.Errorf("Not Decrement by 1")
}
default:
return fmt.Errorf("Not Decrement by 1")
}
return nil
}
func (em *ExpireMap) ToUpper(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return fmt.Errorf("Not Found")
}
switch elem.value.(type) {
case string:
elem.value = strings.ToUpper(elem.value.(string))
default:
return fmt.Errorf("error converting the string.")
}
return nil
}
func (em *ExpireMap) ToLower(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return fmt.Errorf("Not Found")
}
switch elem.value.(type) {
case string:
elem.value = strings.ToLower(elem.value.(string))
default:
return fmt.Errorf("error converting the string.")
}
return nil
}
func (em *ExpireMap) run(key string, expire time.Duration) {
timer := time.NewTimer(expire)
go func() {
select {
case <-timer.C:
em.Lock()
defer em.Unlock()
go func(out chan string, value string) {
out <- value
}(em.stop, key)
}
}()
}
func (em *ExpireMap) ExpiredChan() <-chan string {
return em.stop
}
func (em *ExpireMap) Cleanup() {
em.Lock()
defer em.Unlock()
em.elements = map[string]*element{}
}
func (em *ExpireMap) Destroy() {
- quit := make(chan bool)
- go func() {
- for {
- select {
- case <- quit:
- return
- }
- }
- }()
- quit <- true
for key, _ := range em.elements {
em.Delete(key)
}
}

Калина обнови решението на 18.11.2014 10:22 (преди над 3 години)

package main
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
)
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()
defer em.Unlock()
if _, ok := em.elements[key]; ok {
delete(em.elements, key)
}
timeExpire := time.Now().Add(expire)
em.elements[key] = &element{value: value, expire: &timeExpire}
go em.run(key, expire)
}
func (em *ExpireMap) Get(key string) (interface{}, bool) {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return nil, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return nil, false
}
switch elem.value.(type) {
case int:
return em.GetInt(key)
case float64:
return em.GetFloat64(key)
case string:
return em.GetString(key)
case bool:
return em.GetBool(key)
}
return elem.value, true
}
func (em *ExpireMap) GetInt(key string) (int, bool) {
elem, found := em.elements[key]
if !found {
return 0, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return 0, false
}
switch elem.value.(type) {
case int:
return elem.value.(int), true
}
return 0, false
}
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
elem, found := em.elements[key]
if !found {
return 0, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return 0, false
}
switch elem.value.(type) {
case float64:
return elem.value.(float64), true
}
return 0, false
}
func (em *ExpireMap) GetString(key string) (string, bool) {
elem, found := em.elements[key]
if !found {
return "", false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return "", false
}
switch elem.value.(type) {
case string:
return elem.value.(string), true
}
return "", false
}
func (em *ExpireMap) GetBool(key string) (bool, bool) {
elem, found := em.elements[key]
if !found {
return false, false
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return false, false
}
switch elem.value.(type) {
case bool:
return elem.value.(bool), true
}
return false, false
}
func (em *ExpireMap) Expires(key string) (time.Time, bool) {
elem, found := em.elements[key]
if !found {
return time.Now(), true
}
if elem.expire == nil {
return time.Now(), true
}
return *elem.expire, elem.expire.Before(time.Now())
}
func (em *ExpireMap) Delete(key string) {
em.Lock()
defer em.Unlock()
delete(em.elements, key)
}
func (em *ExpireMap) Contains(key string) bool {
em.Lock()
defer em.Unlock()
if _, ok := em.elements[key]; ok {
return true
}
return false
}
func (em *ExpireMap) Size() int {
em.Lock()
defer em.Unlock()
length := len(em.elements)
return length
}
func (em *ExpireMap) Increment(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
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 {
return fmt.Errorf("Not Increment by 1")
}
default:
return fmt.Errorf("Not Increment by 1")
}
return nil
}
func (em *ExpireMap) Decrement(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
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 {
return fmt.Errorf("Not Decrement by 1")
}
default:
return fmt.Errorf("Not Decrement by 1")
}
return nil
}
func (em *ExpireMap) ToUpper(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return fmt.Errorf("Not Found")
}
switch elem.value.(type) {
case string:
elem.value = strings.ToUpper(elem.value.(string))
default:
return fmt.Errorf("error converting the string.")
}
return nil
}
func (em *ExpireMap) ToLower(key string) error {
em.Lock()
defer em.Unlock()
elem, found := em.elements[key]
if !found {
return fmt.Errorf("Not Found")
}
_, expire := em.Expires(key)
if expire {
em.Delete(key)
return fmt.Errorf("Not Found")
}
switch elem.value.(type) {
case string:
elem.value = strings.ToLower(elem.value.(string))
default:
return fmt.Errorf("error converting the string.")
}
return nil
}
func (em *ExpireMap) run(key string, expire time.Duration) {
timer := time.NewTimer(expire)
go func() {
select {
case <-timer.C:
em.Lock()
defer em.Unlock()
+ _, expire := em.Expires(key)
+ if _, ok := em.elements[key]; ok && expire {
go func(out chan string, value string) {
out <- value
}(em.stop, key)
+ }
}
}()
}
func (em *ExpireMap) ExpiredChan() <-chan string {
return em.stop
}
func (em *ExpireMap) Cleanup() {
em.Lock()
defer em.Unlock()
em.elements = map[string]*element{}
}
func (em *ExpireMap) Destroy() {
for key, _ := range em.elements {
em.Delete(key)
}
-}
+}

Основния проблем в решението ти е, че очакваш някой винаги да чете от канала за изтекли ключове. Както при почти всички други решения, това води до увиснали рутини. Увасналите рутини водят то тази паника в резултата от тестовете.