Решение на ExpireMap от Александър Ваканин

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

Към профила на Александър Ваканин

Резултати

  • 9 точки от тестове
  • 0 бонус точки
  • 9 точки общо
  • 22 успешни тест(а)
  • 3 неуспешни тест(а)

Код

package main
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
)
const DEFAULT_TIME = 10 * time.Millisecond
var (
errIncr = fmt.Errorf("Incrementing did not succeed")
errDecr = fmt.Errorf("Decrementing did not succeed")
errToUpper = fmt.Errorf("ToUpper did not succeed")
errToLower = fmt.Errorf("ToLower did not succeed")
)
type Item struct {
Value interface{}
Expiration time.Time
}
func (i *Item) Expired() bool {
return i.Expiration.Before(time.Now())
}
func NewItem(v interface{}, t time.Time, em *ExpireMap) *Item {
i := new(Item)
i.Value = v
i.Expiration = t
go func(it *Item, expm *ExpireMap) {
for {
if it.Expiration.Before(time.Now()) {
expm.ExpiredChan()
expm.DeleteExpired()
return
} else {
time.Sleep(it.Expiration.Sub(time.Now()))
}
}
}(i, em)
return i
}
type ExpireMap struct {
sync.Mutex
items map[string]*Item
expires chan string
}
func NewExpireMap() *ExpireMap {
em := new(ExpireMap)
em.items = make(map[string]*Item)
em.expires = make(chan string, 20)
return em
}
func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
em.Lock()
defer em.Unlock()
if expire == 0 {
expire = DEFAULT_TIME
}
var e time.Time
if expire > 0 {
e = time.Now().Add(expire)
}
i := NewItem(value, e, em)
em.items[key] = i
}
func (em *ExpireMap) Get(key string) (interface{}, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found {
return nil, false
}
if item.Expired() {
return nil, false
}
return item.Value, true
}
func (em *ExpireMap) GetInt(key string) (int, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
var v int
if !found || item.Expired() {
return 0, false
}
v, ok := item.Value.(int)
return v, ok
}
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
var v float64
if !found || item.Expired() {
return v, false
}
v, ok := item.Value.(float64)
return v, ok
}
func (em *ExpireMap) GetString(key string) (string, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
var v string
if !found || item.Expired() {
return v, false
}
v, ok := item.Value.(string)
return v, ok
}
func (em *ExpireMap) GetBool(key string) (bool, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
var v bool
if !found || item.Expired() {
return v, false
}
v, ok := item.Value.(bool)
return v, ok
}
// return time when key expires
func (em *ExpireMap) Expires(key string) (time.Time, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
var t time.Time
if !found || item.Expired() {
return t, false
}
t = item.Expiration
return t, true
}
func (em *ExpireMap) Delete(key string) {
em.Lock()
defer em.Unlock()
delete(em.items, key)
return
}
func (em *ExpireMap) DeleteExpired() {
em.Lock()
defer em.Unlock()
for k, v := range em.items {
if v.Expired() {
delete(em.items, k)
}
}
}
func (em *ExpireMap) Contains(key string) bool {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found || item.Expired() {
return false
}
return true
}
func (em *ExpireMap) Size() int {
em.Lock()
defer em.Unlock()
var size int
for _, item := range em.items {
if !item.Expired() {
size++
}
}
return size
}
func (em *ExpireMap) Increment(key string) error {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found || item.Expired() {
return errIncr
}
if v, ok := item.Value.(int); ok {
v++
item.Value = v
return nil
}
if str, ok := item.Value.(string); ok {
if v, err := strconv.Atoi(str); err == nil {
v++
item.Value = strconv.Itoa(v)
return nil
} else {
return errIncr
}
}
return errIncr
}
func (em *ExpireMap) Decrement(key string) error {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found || item.Expired() {
return errDecr
}
if v, ok := item.Value.(int); ok {
v--
item.Value = v
return nil
}
if str, ok := item.Value.(string); ok {
if v, err := strconv.Atoi(str); err == nil {
v--
item.Value = strconv.Itoa(v)
return nil
} else {
return errDecr
}
}
return errDecr
}
func (em *ExpireMap) ToUpper(key string) error {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found || item.Expired() {
return errToUpper
}
if str, ok := item.Value.(string); ok {
item.Value = strings.ToUpper(str)
return nil
}
return errToUpper
}
func (em *ExpireMap) ToLower(key string) error {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found || item.Expired() {
return errToLower
}
if str, ok := item.Value.(string); ok {
item.Value = strings.ToLower(str)
return nil
}
return errToLower
}
func (em *ExpireMap) ExpiredChan() <-chan string {
em.Lock()
defer em.Unlock()
for k, v := range em.items {
if v.Expired() {
em.expires <- k
}
}
return em.expires
}
func (em *ExpireMap) Cleanup() {
em.Lock()
defer em.Unlock()
for key, _ := range em.items {
delete(em.items, key)
}
}
func (em *ExpireMap) Destroy() {
em.Lock()
defer em.Unlock()
close(em.expires)
for key, _ := range em.items {
delete(em.items, key)
}
em = nil
}

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

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

goroutine 15 [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(0x8147ff4, 0x81c1ba0, 0x19, 0x19, 0x1, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:434 +0x69f
testing.Main(0x8147ff4, 0x81c1ba0, 0x19, 0x19, 0x81c4600, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:365 +0x69
main.main()
	_/tmp/d20141204-6466-mzi036/_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-mzi036.TestSizes(0x1837a1e0)
	/tmp/d20141204-6466-mzi036/solution_test.go:111 +0x38f
testing.tRunner(0x1837a1e0, 0x81c1bb8)
	/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
exit status 2
FAIL	_/tmp/d20141204-6466-mzi036	1.012s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.127s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.172s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.012s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.012s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.012s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.012s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.052s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.012s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.018s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.012s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.012s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.112s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.062s
--- FAIL: TestExpiredChanWhenNoOneIsReading-2 (0.06 seconds)
	solution_test.go:554: Wrong key expired
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-mzi036	0.072s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.112s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.132s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.226s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.012s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.012s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.012s
panic: test timed out

goroutine 20 [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(0x8147ff4, 0x81c1ba0, 0x19, 0x19, 0x1, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:434 +0x69f
testing.Main(0x8147ff4, 0x81c1ba0, 0x19, 0x19, 0x81c4600, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:365 +0x69
main.main()
	_/tmp/d20141204-6466-mzi036/_test/_testmain.go:91 +0x81

goroutine 4 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).Get(0x18351200, 0x18351210, 0xf, 0x0, 0x0, ...)
	/tmp/d20141204-6466-mzi036/solution.go:75 +0x3e
_/tmp/d20141204-6466-mzi036.TestMultipleKeysWithTheSameExpireTime(0x1837c120)
	/tmp/d20141204-6466-mzi036/solution_test.go:893 +0x53a
testing.tRunner(0x1837c120, 0x81c1cb4)
	/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 5 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x183318e0, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 6 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x18331900, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 7 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x18331920, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 8 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x18331940, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 9 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x18331960, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 10 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x18331980, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 11 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x183319a0, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 12 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).DeleteExpired(0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:156 +0x29
_/tmp/d20141204-6466-mzi036.func·001(0x183319c0, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:37 +0xc2
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 13 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x183319e0, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 14 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x18331a00, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 15 [chan send]:
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:268 +0xd2
_/tmp/d20141204-6466-mzi036.func·001(0x18331a20, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 16 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x18331a40, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 17 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x18331a60, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 18 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x18331a80, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68

goroutine 19 [semacquire]:
sync.runtime_Semacquire(0x18351204)
	/usr/local/lib/go/src/pkg/runtime/zsema_linux_386.c:165 +0x32
sync.(*Mutex).Lock(0x18351200)
	/usr/local/lib/go/src/pkg/sync/mutex.go:66 +0xb6
_/tmp/d20141204-6466-mzi036.(*ExpireMap).ExpiredChan(0x18351200, 0x0)
	/tmp/d20141204-6466-mzi036/solution.go:264 +0x31
_/tmp/d20141204-6466-mzi036.func·001(0x18331aa0, 0x18351200)
	/tmp/d20141204-6466-mzi036/solution.go:36 +0xb6
created by _/tmp/d20141204-6466-mzi036.NewItem
	/tmp/d20141204-6466-mzi036/solution.go:43 +0x68
exit status 2
FAIL	_/tmp/d20141204-6466-mzi036	1.019s
PASS
ok  	_/tmp/d20141204-6466-mzi036	0.012s

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

Александър обнови решението на 17.11.2014 11:39 (преди над 3 години)

+package main
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+const DEFAULT_TIME = 10 * time.Millisecond
+
+var (
+ errIncr = fmt.Errorf("Incrementing did not succeed")
+ errDecr = fmt.Errorf("Decrementing did not succeed")
+ errToUpper = fmt.Errorf("ToUpper did not succeed")
+ errToLower = fmt.Errorf("ToLower did not succeed")
+)
+
+type Item struct {
+ Value interface{}
+ Expiration time.Time
+}
+
+func (i *Item) Expired() bool {
+ return i.Expiration.Before(time.Now())
+}
+
+func NewItem(v interface{}, t time.Time, em *ExpireMap) *Item {
+ i := new(Item)
+ i.Value = v
+ i.Expiration = t
+ go func(it *Item, expm *ExpireMap) {
+ for {
+ if it.Expiration.Before(time.Now()) {
+ expm.Lock()
+ expm.Unlock()
+ expm.ExpiredChan()
+ expm.DeleteExpired()
+ return
+ }
+ }
+ }(i, em)
+ return i
+}
+
+type ExpireMap struct {
+ sync.Mutex
+ items map[string]*Item
+ expires chan string
+}
+
+func NewExpireMap() *ExpireMap {
+ em := new(ExpireMap)
+ em.items = make(map[string]*Item)
+ em.expires = make(chan string, 20)
+ return em
+}
+
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ em.Lock()
+ defer em.Unlock()
+ if expire == 0 {
+ expire = DEFAULT_TIME
+ }
+ var e time.Time
+ if expire > 0 {
+ e = time.Now().Add(expire)
+ }
+ i := NewItem(value, e, em)
+ em.items[key] = i
+}
+
+func (em *ExpireMap) Get(key string) (interface{}, bool) {
+ em.Lock()
+ defer em.Unlock()
+ item, found := em.items[key]
+ if !found {
+ return nil, false
+ }
+ if item.Expired() {
+ return nil, false
+ }
+ return item.Value, true
+}
+
+func (em *ExpireMap) GetInt(key string) (int, bool) {
+ em.Lock()
+ defer em.Unlock()
+ item, found := em.items[key]
+ var v int
+ if !found || item.Expired() {
+ return 0, false
+ }
+ v, ok := item.Value.(int)
+ return v, ok
+}
+
+func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
+ em.Lock()
+ defer em.Unlock()
+ item, found := em.items[key]
+ var v float64
+ if !found || item.Expired() {
+ return v, false
+ }
+ v, ok := item.Value.(float64)
+ return v, ok
+}
+
+func (em *ExpireMap) GetString(key string) (string, bool) {
+ em.Lock()
+ defer em.Unlock()
+ item, found := em.items[key]
+ var v string
+ if !found || item.Expired() {
+ return v, false
+ }
+ v, ok := item.Value.(string)
+ return v, ok
+}
+
+func (em *ExpireMap) GetBool(key string) (bool, bool) {
+ em.Lock()
+ defer em.Unlock()
+ item, found := em.items[key]
+ var v bool
+ if !found || item.Expired() {
+ return v, false
+ }
+ v, ok := item.Value.(bool)
+ return v, ok
+}
+
+// return time when key expires
+func (em *ExpireMap) Expires(key string) (time.Time, bool) {
+ em.Lock()
+ defer em.Unlock()
+ item, found := em.items[key]
+ var t time.Time
+ if !found || item.Expired() {
+ return t, false
+ }
+ t = item.Expiration
+ return t, true
+}
+
+func (em *ExpireMap) Delete(key string) {
+ em.Lock()
+ defer em.Unlock()
+ delete(em.items, key)
+ return
+}
+
+func (em *ExpireMap) DeleteExpired() {
+ em.Lock()
+ defer em.Unlock()
+ for k, v := range em.items {
+ if v.Expired() {
+ delete(em.items, k)
+ }
+ }
+}
+
+func (em *ExpireMap) Contains(key string) bool {
+ em.Lock()
+ defer em.Unlock()
+ item, found := em.items[key]
+ if !found || item.Expired() {
+ return false
+ }
+ return true
+}
+
+func (em *ExpireMap) Size() int {
+ em.Lock()
+ defer em.Unlock()
+ var size int
+ for _, item := range em.items {
+ if !item.Expired() {
+ size++
+ }
+ }
+ return size
+}
+
+func (em *ExpireMap) Increment(key string) error {
+ em.Lock()
+ defer em.Unlock()
+ item, found := em.items[key]
+ if !found || item.Expired() {
+ return errIncr
+ }
+ if v, ok := item.Value.(int); ok {
+ v++
+ item.Value = v
+ return nil
+ }
+ if str, ok := item.Value.(string); ok {
+ if v, err := strconv.Atoi(str); err == nil {
+ v++
+ item.Value = strconv.Itoa(v)
+ return nil
+ } else {
+ return errIncr
+ }
+ }
+ return errIncr
+}
+
+func (em *ExpireMap) Decrement(key string) error {
+ em.Lock()
+ defer em.Unlock()
+ item, found := em.items[key]
+ if !found || item.Expired() {
+ return errDecr
+ }
+ if v, ok := item.Value.(int); ok {
+ v--
+ item.Value = v
+ return nil
+ }
+ if str, ok := item.Value.(string); ok {
+ if v, err := strconv.Atoi(str); err == nil {
+ v--
+ item.Value = strconv.Itoa(v)
+ return nil
+ } else {
+ return errDecr
+ }
+ }
+ return errDecr
+}
+
+func (em *ExpireMap) ToUpper(key string) error {
+ em.Lock()
+ defer em.Unlock()
+ item, found := em.items[key]
+ if !found || item.Expired() {
+ return errToUpper
+ }
+ if str, ok := item.Value.(string); ok {
+ item.Value = strings.ToUpper(str)
+ return nil
+ }
+ return errToUpper
+}
+
+func (em *ExpireMap) ToLower(key string) error {
+ em.Lock()
+ defer em.Unlock()
+ item, found := em.items[key]
+ if !found || item.Expired() {
+ return errToLower
+ }
+ if str, ok := item.Value.(string); ok {
+ item.Value = strings.ToLower(str)
+ return nil
+ }
+ return errToLower
+}
+
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ for k, v := range em.items {
+ if v.Expired() {
+ em.expires <- k
+ }
+ }
+ return em.expires
+}
+
+func (em *ExpireMap) Cleanup() {
+ em.Lock()
+ defer em.Unlock()
+ for key, _ := range em.items {
+ delete(em.items, key)
+ }
+}
+
+func (em *ExpireMap) Destroy() {
+ em.Lock()
+ defer em.Unlock()
+ close(em.expires)
+ for key, _ := range em.items {
+ delete(em.items, key)
+ }
+ em = nil
+}

Александър обнови решението на 17.11.2014 14:04 (преди над 3 години)

package main
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
)
const DEFAULT_TIME = 10 * time.Millisecond
var (
errIncr = fmt.Errorf("Incrementing did not succeed")
errDecr = fmt.Errorf("Decrementing did not succeed")
errToUpper = fmt.Errorf("ToUpper did not succeed")
errToLower = fmt.Errorf("ToLower did not succeed")
)
type Item struct {
Value interface{}
Expiration time.Time
}
func (i *Item) Expired() bool {
return i.Expiration.Before(time.Now())
}
func NewItem(v interface{}, t time.Time, em *ExpireMap) *Item {
i := new(Item)
i.Value = v
i.Expiration = t
go func(it *Item, expm *ExpireMap) {
for {
if it.Expiration.Before(time.Now()) {
- expm.Lock()
- expm.Unlock()
expm.ExpiredChan()
expm.DeleteExpired()
return
+ } else {
+ time.Sleep(it.Expiration.Sub(time.Now()))
}
}
}(i, em)
return i
}
type ExpireMap struct {
sync.Mutex
items map[string]*Item
expires chan string
}
func NewExpireMap() *ExpireMap {
em := new(ExpireMap)
em.items = make(map[string]*Item)
em.expires = make(chan string, 20)
return em
}
func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
em.Lock()
defer em.Unlock()
if expire == 0 {
expire = DEFAULT_TIME
}
var e time.Time
if expire > 0 {
e = time.Now().Add(expire)
}
i := NewItem(value, e, em)
em.items[key] = i
}
func (em *ExpireMap) Get(key string) (interface{}, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found {
return nil, false
}
if item.Expired() {
return nil, false
}
return item.Value, true
}
func (em *ExpireMap) GetInt(key string) (int, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
var v int
if !found || item.Expired() {
return 0, false
}
v, ok := item.Value.(int)
return v, ok
}
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
var v float64
if !found || item.Expired() {
return v, false
}
v, ok := item.Value.(float64)
return v, ok
}
func (em *ExpireMap) GetString(key string) (string, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
var v string
if !found || item.Expired() {
return v, false
}
v, ok := item.Value.(string)
return v, ok
}
func (em *ExpireMap) GetBool(key string) (bool, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
var v bool
if !found || item.Expired() {
return v, false
}
v, ok := item.Value.(bool)
return v, ok
}
// return time when key expires
func (em *ExpireMap) Expires(key string) (time.Time, bool) {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
var t time.Time
if !found || item.Expired() {
return t, false
}
t = item.Expiration
return t, true
}
func (em *ExpireMap) Delete(key string) {
em.Lock()
defer em.Unlock()
delete(em.items, key)
return
}
func (em *ExpireMap) DeleteExpired() {
em.Lock()
defer em.Unlock()
for k, v := range em.items {
if v.Expired() {
delete(em.items, k)
}
}
}
func (em *ExpireMap) Contains(key string) bool {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found || item.Expired() {
return false
}
return true
}
func (em *ExpireMap) Size() int {
em.Lock()
defer em.Unlock()
var size int
for _, item := range em.items {
if !item.Expired() {
size++
}
}
return size
}
func (em *ExpireMap) Increment(key string) error {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found || item.Expired() {
return errIncr
}
if v, ok := item.Value.(int); ok {
v++
item.Value = v
return nil
}
if str, ok := item.Value.(string); ok {
if v, err := strconv.Atoi(str); err == nil {
v++
item.Value = strconv.Itoa(v)
return nil
} else {
return errIncr
}
}
return errIncr
}
func (em *ExpireMap) Decrement(key string) error {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found || item.Expired() {
return errDecr
}
if v, ok := item.Value.(int); ok {
v--
item.Value = v
return nil
}
if str, ok := item.Value.(string); ok {
if v, err := strconv.Atoi(str); err == nil {
v--
item.Value = strconv.Itoa(v)
return nil
} else {
return errDecr
}
}
return errDecr
}
func (em *ExpireMap) ToUpper(key string) error {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found || item.Expired() {
return errToUpper
}
if str, ok := item.Value.(string); ok {
item.Value = strings.ToUpper(str)
return nil
}
return errToUpper
}
func (em *ExpireMap) ToLower(key string) error {
em.Lock()
defer em.Unlock()
item, found := em.items[key]
if !found || item.Expired() {
return errToLower
}
if str, ok := item.Value.(string); ok {
item.Value = strings.ToLower(str)
return nil
}
return errToLower
}
func (em *ExpireMap) ExpiredChan() <-chan string {
+ em.Lock()
+ defer em.Unlock()
for k, v := range em.items {
if v.Expired() {
em.expires <- k
}
}
return em.expires
}
func (em *ExpireMap) Cleanup() {
em.Lock()
defer em.Unlock()
for key, _ := range em.items {
delete(em.items, key)
}
}
func (em *ExpireMap) Destroy() {
em.Lock()
defer em.Unlock()
close(em.expires)
for key, _ := range em.items {
delete(em.items, key)
}
em = nil
}