Решение на ExpireMap от Ангел Цанев

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

Към профила на Ангел Цанев

Резултати

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

Код

package main
import (
"strconv"
"strings"
"sync"
"time"
)
type ExpireMap struct {
mutex *sync.Mutex
cache map[string]interface{}
durations map[string]time.Time
channel chan string
quit map[string]chan struct{}
}
type ExpireMapError struct {
message string
}
func (e *ExpireMapError) Error() string {
return e.message
}
func NewExpireMap() *ExpireMap {
var em ExpireMap
em.cache = make(map[string]interface{})
em.mutex = &sync.Mutex{}
em.durations = make(map[string]time.Time)
em.channel = make(chan string)
em.quit = make(map[string]chan struct{})
return &em
}
func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
em.mutex.Lock()
defer em.mutex.Unlock()
if _, ok := em.cache[key]; ok {
em.quit[key] <- struct{}{}
} else {
em.quit[key] = make(chan struct{})
}
em.cache[key] = value
em.durations[key] = time.Now().Add(expire)
go func(em *ExpireMap, key string, expire time.Duration) {
select {
case <-em.quit[key]:
em.mutex.Lock()
delete(em.quit, key)
em.mutex.Unlock()
return
case <-time.After(expire):
em.mutex.Lock()
_, ok := em.cache[key]
em.mutex.Unlock()
em.Delete(key)
if ok {
select {
case em.channel <- key:
em.mutex.Lock()
delete(em.quit, key)
em.mutex.Unlock()
return
case <-em.quit[key]:
em.mutex.Lock()
delete(em.quit, key)
em.mutex.Unlock()
return
}
}
return
}
}(em, key, expire)
}
func (em *ExpireMap) Get(key string) (interface{}, bool) {
em.mutex.Lock()
defer em.mutex.Unlock()
value, ok := em.cache[key]
return value, ok
}
func (em *ExpireMap) GetInt(key string) (int, bool) {
value, status := em.Get(key)
if value, ok := value.(int); status && ok {
return value, status
} else {
return 0, false
}
}
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
value, status := em.Get(key)
if value, ok := value.(float64); status && ok {
return value, status
} else {
return 0.0, false
}
}
func (em *ExpireMap) GetString(key string) (string, bool) {
value, status := em.Get(key)
if value, ok := value.(string); status && ok {
return value, status
} else {
return "", false
}
}
func (em *ExpireMap) GetBool(key string) (bool, bool) {
value, status := em.Get(key)
if value, ok := value.(bool); status && ok {
return value, status
} else {
return false, false
}
}
func (em *ExpireMap) Expires(key string) (time.Time, bool) {
em.mutex.Lock()
defer em.mutex.Unlock()
time, status := em.durations[key]
return time, status
}
func (em *ExpireMap) Delete(key string) {
em.mutex.Lock()
defer em.mutex.Unlock()
delete(em.cache, key)
delete(em.durations, key)
}
func (em *ExpireMap) Contains(key string) bool {
em.mutex.Lock()
defer em.mutex.Unlock()
_, ok := em.cache[key]
return ok
}
func (em *ExpireMap) Size() int {
em.mutex.Lock()
defer em.mutex.Unlock()
return len(em.cache)
}
func (em *ExpireMap) Increment(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if number, ok := em.cache[key]; ok == true {
switch value := number.(type) {
case int:
em.cache[key] = value + 1
return nil
case string:
if number, ok := strconv.Atoi(value); ok == nil {
em.cache[key] = strconv.Itoa(number + 1)
return nil
}
default:
return &ExpireMapError{"Value is not of type Int or String."}
}
}
return &ExpireMapError{"Element not found !"}
}
func (em *ExpireMap) Decrement(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if number, ok := em.cache[key]; ok == true {
switch value := number.(type) {
case int:
em.cache[key] = value - 1
return nil
case string:
if number, ok := strconv.Atoi(value); ok == nil {
em.cache[key] = strconv.Itoa(number - 1)
return nil
}
default:
return &ExpireMapError{"Value is not of type Int or String."}
}
}
return &ExpireMapError{"Element not found !"}
}
func (em *ExpireMap) ToUpper(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if value, ok := em.cache[key].(string); ok == true {
em.cache[key] = strings.ToUpper(value)
return nil
} else {
return &ExpireMapError{"Value is not of type String."}
}
}
func (em *ExpireMap) ToLower(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if value, ok := em.cache[key].(string); ok == true {
em.cache[key] = strings.ToLower(value)
return nil
} else {
return &ExpireMapError{"Value is not of type String."}
}
}
func (em *ExpireMap) ExpiredChan() <-chan string {
em.mutex.Lock()
defer em.mutex.Unlock()
return em.channel
}
func (em *ExpireMap) Cleanup() {
em.mutex.Lock()
for key, _ := range em.quit {
em.quit[key] <- struct{}{}
}
defer em.mutex.Unlock()
em.cache = make(map[string]interface{})
em.durations = make(map[string]time.Time)
em.channel = make(chan string)
em.quit = make(map[string]chan struct{})
}
func (em *ExpireMap) Destroy() {
em.mutex.Lock()
defer em.mutex.Unlock()
for key, _ := range em.quit {
em.quit[key] <- struct{}{}
}
em.cache = nil
em.durations = nil
close(em.channel)
for key, _ := range em.quit {
close(em.quit[key])
}
em.quit = nil
}

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

PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.012s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	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(0x8148d54, 0x81c2ba0, 0x19, 0x19, 0x1, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:434 +0x69f
testing.Main(0x8148d54, 0x81c2ba0, 0x19, 0x19, 0x81c5600, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:365 +0x69
main.main()
	_/tmp/d20141204-6466-1mz93il/_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-1mz93il.TestSizes(0x1837a1e0)
	/tmp/d20141204-6466-1mz93il/solution_test.go:111 +0x3f9
testing.tRunner(0x1837a1e0, 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 5 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300310, 0x5, 0x3b9aca00, 0x0, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 6 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300328, 0x5, 0x3b9aca00, 0x0, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 7 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300340, 0x5, 0x3b9aca00, 0x0, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 8 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300358, 0x5, 0x3b9aca00, 0x0, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 9 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300370, 0x5, 0x3b9aca00, 0x0, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 10 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300388, 0x5, 0x3b9aca00, 0x0, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 11 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x183003a0, 0x5, 0x3b9aca00, 0x0, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 12 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x183003b8, 0x5, 0x3b9aca00, 0x0, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 13 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x183003d0, 0x5, 0x3b9aca00, 0x0, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 14 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x183003e8, 0x5, 0x3b9aca00, 0x0, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc
exit status 2
FAIL	_/tmp/d20141204-6466-1mz93il	1.013s
panic: runtime error: send on closed channel

goroutine 6 [running]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x8123448, PASS
0x3, 0x5f5e100, 0x0, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:61 +0x2a9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 1 [panicwait]:
exit status 2
FAIL	_/tmp/d20141204-6466-1mz93il	0.122s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.172s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.012s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.012s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.014s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.012s
--- FAIL: TestIncAndDecInManyRoutines-2 (0.03 seconds)
	solution_test.go:388: Expected 666 but found 1666
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-1mz93il	0.108s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.012s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.012s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.012s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.016s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.112s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.063s
--- FAIL: TestExpiredChanWhenNoOneIsReading-2 (0.06 seconds)
	solution_test.go:554: Wrong key expired
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-1mz93il	0.072s
panic: test timed out

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

goroutine 4 [chan send]:
_/tmp/d20141204-6466-1mz93il.(*ExpireMap).Destroy(0x18331800)
	/tmp/d20141204-6466-1mz93il/solution.go:238 +0xd0
_/tmp/d20141204-6466-1mz93il.TestExpiredChanDoesNotReturnDeletedKeys(0x1837c120)
	/tmp/d20141204-6466-1mz93il/solution_test.go:593 +0x36a
testing.tRunner(0x1837c120, 0x81c2c6c)
	/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-1mz93il	1.013s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.132s
panic: test timed out

goroutine 51 [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(0x8148d54, 0x81c2ba0, 0x19, 0x19, 0x1, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:434 +0x69f
testing.Main(0x8148d54, 0x81c2ba0, 0x19, 0x19, 0x81c5600, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:365 +0x69
main.main()
	_/tmp/d20141204-6466-1mz93il/_test/_testmain.go:91 +0x81

goroutine 4 [chan send (nil chan)]:
_/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set(0x18331800, 0x8125b88, 0x7, 0x80f93e0, 0x1, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:40 +0xdf
_/tmp/d20141204-6466-1mz93il.TestConcurrentOperations(0x1837b120)
	/tmp/d20141204-6466-1mz93il/solution_test.go:626 +0x1fc
testing.tRunner(0x1837b120, 0x81c2c84)
	/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 50 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x8126698, 0x6, 0x7e11d600, 0x3, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 11 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300328, 0x5, 0x8c2e2800, 0xd1, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 15 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300340, 0x5, 0x8c2e2800, 0xd1, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 19 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300358, 0x5, 0x8c2e2800, 0xd1, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 23 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300370, 0x5, 0x8c2e2800, 0xd1, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 27 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300388, 0x5, 0x8c2e2800, 0xd1, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 49 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x8125b88, 0x7, 0x7e11d600, 0x3, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 31 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300270, 0x5, 0x8c2e2800, 0xd1, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 35 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x18300288, 0x5, 0x8c2e2800, 0xd1, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 39 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x183002a0, 0x5, 0x8c2e2800, 0xd1, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 43 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x183002b8, 0x5, 0x8c2e2800, 0xd1, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc

goroutine 47 [select]:
_/tmp/d20141204-6466-1mz93il.func·001(0x18331800, 0x183002d0, 0x5, 0x8c2e2800, 0xd1, ...)
	/tmp/d20141204-6466-1mz93il/solution.go:49 +0x2b9
created by _/tmp/d20141204-6466-1mz93il.(*ExpireMap).Set
	/tmp/d20141204-6466-1mz93il/solution.go:76 +0x1cc
exit status 2
FAIL	_/tmp/d20141204-6466-1mz93il	1.012s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.012s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.012s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.012s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.272s
PASS
ok  	_/tmp/d20141204-6466-1mz93il	0.012s

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

Ангел обнови решението на 16.11.2014 21:52 (преди над 3 години)

+package main
+
+import (
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+type ExpireMap struct {
+ mutex *sync.Mutex
+ cache map[string]interface{}
+ durations map[string]time.Time
+ channel chan string
+ goroutines int
+ quit chan struct{}
+ quitCleanup chan struct{}
+}
+
+type ExpireMapError struct {
+ message string
+}
+
+func (e *ExpireMapError) Error() string {
+ return e.message
+}
+
+func NewExpireMap() *ExpireMap {
+ var em ExpireMap
+ em.cache = make(map[string]interface{})
+ em.mutex = &sync.Mutex{}
+ em.durations = make(map[string]time.Time)
+ em.channel = make(chan string)
+ em.goroutines = 0
+ em.quit = make(chan struct{}, 1)
+ em.quitCleanup = make(chan struct{}, 1)
+ return &em
+}
+
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ em.mutex.Lock()
+
+ em.cache[key] = value
+ em.goroutines++
+
+ em.durations[key] = time.Now().Add(expire)
+ defer em.mutex.Unlock()
+
+ go func(em *ExpireMap, key string, expire time.Duration) {
+ select {
+ case <-em.quit:
+ em.mutex.Lock()
+ em.goroutines--
+ number := em.goroutines
+ em.mutex.Unlock()
+ if number == 0 {
+ em.quitCleanup <- struct{}{}
+ return
+ } else {
+ em.quit <- struct{}{}
+ return
+ }
+ case <-time.After(expire):
+ em.mutex.Lock()
+ _, ok := em.cache[key]
+ em.mutex.Unlock()
+ em.Delete(key)
+ if ok {
+ em.mutex.Lock()
+ em.goroutines++
+ em.mutex.Unlock()
+ select {
+ case em.channel <- key:
+ em.mutex.Lock()
+ em.goroutines--
+ em.mutex.Unlock()
+ return
+ case <-em.quit:
+ em.mutex.Lock()
+ em.goroutines--
+ number := em.goroutines
+ em.mutex.Unlock()
+ if number == 0 {
+ em.quitCleanup <- struct{}{}
+ } else {
+ em.quit <- struct{}{}
+ return
+ }
+ }
+ }
+ return
+ }
+ }(em, key, expire)
+}
+
+func (em *ExpireMap) Get(key string) (interface{}, bool) {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ value, ok := em.cache[key]
+ return value, ok
+}
+
+func (em *ExpireMap) GetInt(key string) (int, bool) {
+ value, status := em.Get(key)
+
+ if value, ok := value.(int); status && ok {
+ return value, status
+ } else {
+ return 0, false
+ }
+}
+
+func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
+ value, status := em.Get(key)
+ if value, ok := value.(float64); status && ok {
+ return value, status
+ } else {
+ return 0.0, false
+ }
+}
+
+func (em *ExpireMap) GetString(key string) (string, bool) {
+ value, status := em.Get(key)
+ if value, ok := value.(string); status && ok {
+ return value, status
+ } else {
+ return "", false
+ }
+}
+
+func (em *ExpireMap) GetBool(key string) (bool, bool) {
+ value, status := em.Get(key)
+ if value, ok := value.(bool); status && ok {
+ return value, status
+ } else {
+ return false, false
+ }
+}
+
+func (em *ExpireMap) Expires(key string) (time.Time, bool) {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ time, status := em.durations[key]
+ return time, status
+}
+
+func (em *ExpireMap) Delete(key string) {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+
+ delete(em.cache, key)
+ delete(em.durations, key)
+ em.goroutines--
+}
+
+func (em *ExpireMap) Contains(key string) bool {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ _, ok := em.cache[key]
+ return ok
+}
+
+func (em *ExpireMap) Size() int {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ return em.goroutines
+}
+
+func (em *ExpireMap) Increment(key string) error {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+
+ if number, ok := em.cache[key].(int); ok == true {
+ em.cache[key] = number + 1
+ return nil
+ } else if number, ok := strconv.Atoi(em.cache[key].(string)); ok == nil {
+ em.cache[key] = strconv.Itoa(number + 1)
+ return nil
+ }
+ return &ExpireMapError{"Value is not of type Int or String."}
+}
+
+func (em *ExpireMap) Decrement(key string) error {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+
+ if number, ok := em.cache[key].(int); ok == true {
+ em.cache[key] = number - 1
+ return nil
+ } else if number, ok := strconv.Atoi(em.cache[key].(string)); ok == nil {
+ em.cache[key] = strconv.Itoa(number - 1)
+ return nil
+ }
+
+ return &ExpireMapError{"Value is not of type Int or String."}
+}
+
+func (em *ExpireMap) ToUpper(key string) error {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ if value, ok := em.cache[key].(string); ok == true {
+ em.cache[key] = strings.ToUpper(value)
+ return nil
+ } else {
+ return &ExpireMapError{"Value is not of type String."}
+ }
+}
+
+func (em *ExpireMap) ToLower(key string) error {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ if value, ok := em.cache[key].(string); ok == true {
+ em.cache[key] = strings.ToLower(value)
+ return nil
+ } else {
+ return &ExpireMapError{"Value is not of type String."}
+ }
+}
+
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ return em.channel
+}
+
+func (em *ExpireMap) Cleanup() {
+ em.quit <- struct{}{}
+ <-em.quitCleanup
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+
+ em.cache = make(map[string]interface{})
+ em.durations = make(map[string]time.Time)
+ em.channel = make(chan string)
+ em.goroutines = 0
+ em.quit = make(chan struct{}, 1)
+ em.quitCleanup = make(chan struct{})
+}
+
+func (em *ExpireMap) Destroy() {
+ em.mutex.Lock()
+ number := em.goroutines
+ em.mutex.Unlock()
+ if number != 0 {
+ em.quit <- struct{}{}
+ <-em.quitCleanup
+ }
+
+ em.cache = nil
+ em.durations = nil
+ em.goroutines = 0
+ close(em.channel)
+ close(em.quit)
+ close(em.quitCleanup)
+}

ах ... не е зле - мисля че е малко сложно ....

И в никаква последователност(виж колко е часа) моите забележки:

това на 70 ред е заради ? Ако отговора е ред 75 и 80 ... помисли пак.

Не е задължително някой да слуша на ExpiredChan-а, покрай което имаш и възможността някой да ти Destroy-не мапа без да слуша (и да ти се затвори ExpiredChan-а)

Това em.goroutines и особено ползването му за Size не ми харесва ...

type switch

ползвай defer за unlock където може ... и мисля че много си играеш с ключалките.

Можеш да минеш с един map.

Ангел обнови решението на 17.11.2014 22:48 (преди над 3 години)

package main
import (
"strconv"
"strings"
"sync"
"time"
)
type ExpireMap struct {
- mutex *sync.Mutex
- cache map[string]interface{}
- durations map[string]time.Time
- channel chan string
- goroutines int
- quit chan struct{}
- quitCleanup chan struct{}
+ mutex *sync.Mutex
+ cache map[string]interface{}
+ durations map[string]time.Time
+ channel chan string
+ //goroutines int
+ quit map[string]chan struct{}
+ //quitCleanup chan struct{}
}
type ExpireMapError struct {
message string
}
func (e *ExpireMapError) Error() string {
return e.message
}
func NewExpireMap() *ExpireMap {
var em ExpireMap
em.cache = make(map[string]interface{})
em.mutex = &sync.Mutex{}
em.durations = make(map[string]time.Time)
em.channel = make(chan string)
- em.goroutines = 0
- em.quit = make(chan struct{}, 1)
- em.quitCleanup = make(chan struct{}, 1)
+ //em.goroutines = 0
+ em.quit = make(map[string]chan struct{})
return &em
}
func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
em.mutex.Lock()
+ defer em.mutex.Unlock()
+ if _, ok := em.cache[key]; ok {
+ em.quit[key] <- struct{}{}
+ } else {
+ em.quit[key] = make(chan struct{})
+ }
em.cache[key] = value
- em.goroutines++
+ //em.goroutines++
em.durations[key] = time.Now().Add(expire)
- defer em.mutex.Unlock()
go func(em *ExpireMap, key string, expire time.Duration) {
select {
- case <-em.quit:
+ case <-em.quit[key]:
em.mutex.Lock()
- em.goroutines--
- number := em.goroutines
+ //em.goroutines--
+ delete(em.quit, key)
em.mutex.Unlock()
- if number == 0 {
- em.quitCleanup <- struct{}{}
- return
- } else {
- em.quit <- struct{}{}
- return
- }
+ return
case <-time.After(expire):
em.mutex.Lock()
_, ok := em.cache[key]
em.mutex.Unlock()
em.Delete(key)
if ok {
em.mutex.Lock()
- em.goroutines++
+ //em.goroutines++
em.mutex.Unlock()
select {
case em.channel <- key:
em.mutex.Lock()
- em.goroutines--
+ //em.goroutines--
+ delete(em.quit, key)
em.mutex.Unlock()
return
- case <-em.quit:
+ case <-em.quit[key]:
em.mutex.Lock()
- em.goroutines--
- number := em.goroutines
+ //em.goroutines--
+ delete(em.quit, key)
em.mutex.Unlock()
- if number == 0 {
- em.quitCleanup <- struct{}{}
- } else {
- em.quit <- struct{}{}
- return
- }
+ return
}
}
return
}
}(em, key, expire)
}
func (em *ExpireMap) Get(key string) (interface{}, bool) {
em.mutex.Lock()
defer em.mutex.Unlock()
value, ok := em.cache[key]
return value, ok
}
func (em *ExpireMap) GetInt(key string) (int, bool) {
value, status := em.Get(key)
if value, ok := value.(int); status && ok {
return value, status
} else {
return 0, false
}
}
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
value, status := em.Get(key)
if value, ok := value.(float64); status && ok {
return value, status
} else {
return 0.0, false
}
}
func (em *ExpireMap) GetString(key string) (string, bool) {
value, status := em.Get(key)
if value, ok := value.(string); status && ok {
return value, status
} else {
return "", false
}
}
func (em *ExpireMap) GetBool(key string) (bool, bool) {
value, status := em.Get(key)
if value, ok := value.(bool); status && ok {
return value, status
} else {
return false, false
}
}
func (em *ExpireMap) Expires(key string) (time.Time, bool) {
em.mutex.Lock()
defer em.mutex.Unlock()
time, status := em.durations[key]
return time, status
}
func (em *ExpireMap) Delete(key string) {
em.mutex.Lock()
defer em.mutex.Unlock()
delete(em.cache, key)
delete(em.durations, key)
- em.goroutines--
+ //em.goroutines--
}
func (em *ExpireMap) Contains(key string) bool {
em.mutex.Lock()
defer em.mutex.Unlock()
_, ok := em.cache[key]
return ok
}
func (em *ExpireMap) Size() int {
em.mutex.Lock()
defer em.mutex.Unlock()
- return em.goroutines
+ return len(em.cache)
}
func (em *ExpireMap) Increment(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if number, ok := em.cache[key].(int); ok == true {
em.cache[key] = number + 1
return nil
} else if number, ok := strconv.Atoi(em.cache[key].(string)); ok == nil {
em.cache[key] = strconv.Itoa(number + 1)
return nil
}
return &ExpireMapError{"Value is not of type Int or String."}
}
func (em *ExpireMap) Decrement(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if number, ok := em.cache[key].(int); ok == true {
em.cache[key] = number - 1
return nil
} else if number, ok := strconv.Atoi(em.cache[key].(string)); ok == nil {
em.cache[key] = strconv.Itoa(number - 1)
return nil
}
return &ExpireMapError{"Value is not of type Int or String."}
}
func (em *ExpireMap) ToUpper(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if value, ok := em.cache[key].(string); ok == true {
em.cache[key] = strings.ToUpper(value)
return nil
} else {
return &ExpireMapError{"Value is not of type String."}
}
}
func (em *ExpireMap) ToLower(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if value, ok := em.cache[key].(string); ok == true {
em.cache[key] = strings.ToLower(value)
return nil
} else {
return &ExpireMapError{"Value is not of type String."}
}
}
func (em *ExpireMap) ExpiredChan() <-chan string {
em.mutex.Lock()
defer em.mutex.Unlock()
return em.channel
}
func (em *ExpireMap) Cleanup() {
- em.quit <- struct{}{}
- <-em.quitCleanup
em.mutex.Lock()
+ for key, _ := range em.quit {
+ em.quit[key] <- struct{}{}
+ }
defer em.mutex.Unlock()
em.cache = make(map[string]interface{})
em.durations = make(map[string]time.Time)
em.channel = make(chan string)
- em.goroutines = 0
- em.quit = make(chan struct{}, 1)
- em.quitCleanup = make(chan struct{})
+ //em.goroutines = 0
+ em.quit = make(map[string]chan struct{})
}
func (em *ExpireMap) Destroy() {
em.mutex.Lock()
- number := em.goroutines
- em.mutex.Unlock()
- if number != 0 {
- em.quit <- struct{}{}
- <-em.quitCleanup
+
+ for key, _ := range em.quit {
+ em.quit[key] <- struct{}{}
}
+ em.mutex.Unlock()
em.cache = nil
em.durations = nil
- em.goroutines = 0
close(em.channel)
- close(em.quit)
- close(em.quitCleanup)
+ for key, _ := range em.quit {
-}
+ close(em.quit[key])
+ }
+ em.quit = nil
+}

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

package main
import (
"strconv"
"strings"
"sync"
"time"
)
type ExpireMap struct {
mutex *sync.Mutex
cache map[string]interface{}
durations map[string]time.Time
channel chan string
- //goroutines int
quit map[string]chan struct{}
- //quitCleanup chan struct{}
}
type ExpireMapError struct {
message string
}
func (e *ExpireMapError) Error() string {
return e.message
}
func NewExpireMap() *ExpireMap {
var em ExpireMap
em.cache = make(map[string]interface{})
em.mutex = &sync.Mutex{}
em.durations = make(map[string]time.Time)
em.channel = make(chan string)
- //em.goroutines = 0
em.quit = make(map[string]chan struct{})
return &em
}
func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
em.mutex.Lock()
defer em.mutex.Unlock()
if _, ok := em.cache[key]; ok {
em.quit[key] <- struct{}{}
} else {
em.quit[key] = make(chan struct{})
}
em.cache[key] = value
- //em.goroutines++
-
em.durations[key] = time.Now().Add(expire)
go func(em *ExpireMap, key string, expire time.Duration) {
select {
case <-em.quit[key]:
em.mutex.Lock()
- //em.goroutines--
delete(em.quit, key)
em.mutex.Unlock()
return
case <-time.After(expire):
em.mutex.Lock()
_, ok := em.cache[key]
em.mutex.Unlock()
em.Delete(key)
if ok {
- em.mutex.Lock()
- //em.goroutines++
- em.mutex.Unlock()
select {
case em.channel <- key:
em.mutex.Lock()
- //em.goroutines--
delete(em.quit, key)
em.mutex.Unlock()
return
case <-em.quit[key]:
em.mutex.Lock()
- //em.goroutines--
delete(em.quit, key)
em.mutex.Unlock()
return
}
}
return
}
}(em, key, expire)
}
func (em *ExpireMap) Get(key string) (interface{}, bool) {
em.mutex.Lock()
defer em.mutex.Unlock()
value, ok := em.cache[key]
return value, ok
}
func (em *ExpireMap) GetInt(key string) (int, bool) {
value, status := em.Get(key)
if value, ok := value.(int); status && ok {
return value, status
} else {
return 0, false
}
}
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
value, status := em.Get(key)
if value, ok := value.(float64); status && ok {
return value, status
} else {
return 0.0, false
}
}
func (em *ExpireMap) GetString(key string) (string, bool) {
value, status := em.Get(key)
if value, ok := value.(string); status && ok {
return value, status
} else {
return "", false
}
}
func (em *ExpireMap) GetBool(key string) (bool, bool) {
value, status := em.Get(key)
if value, ok := value.(bool); status && ok {
return value, status
} else {
return false, false
}
}
func (em *ExpireMap) Expires(key string) (time.Time, bool) {
em.mutex.Lock()
defer em.mutex.Unlock()
time, status := em.durations[key]
return time, status
}
func (em *ExpireMap) Delete(key string) {
em.mutex.Lock()
defer em.mutex.Unlock()
delete(em.cache, key)
delete(em.durations, key)
- //em.goroutines--
}
func (em *ExpireMap) Contains(key string) bool {
em.mutex.Lock()
defer em.mutex.Unlock()
_, ok := em.cache[key]
return ok
}
func (em *ExpireMap) Size() int {
em.mutex.Lock()
defer em.mutex.Unlock()
return len(em.cache)
}
func (em *ExpireMap) Increment(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if number, ok := em.cache[key].(int); ok == true {
em.cache[key] = number + 1
return nil
} else if number, ok := strconv.Atoi(em.cache[key].(string)); ok == nil {
em.cache[key] = strconv.Itoa(number + 1)
return nil
}
return &ExpireMapError{"Value is not of type Int or String."}
}
func (em *ExpireMap) Decrement(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if number, ok := em.cache[key].(int); ok == true {
em.cache[key] = number - 1
return nil
} else if number, ok := strconv.Atoi(em.cache[key].(string)); ok == nil {
em.cache[key] = strconv.Itoa(number - 1)
return nil
}
return &ExpireMapError{"Value is not of type Int or String."}
}
func (em *ExpireMap) ToUpper(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if value, ok := em.cache[key].(string); ok == true {
em.cache[key] = strings.ToUpper(value)
return nil
} else {
return &ExpireMapError{"Value is not of type String."}
}
}
func (em *ExpireMap) ToLower(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if value, ok := em.cache[key].(string); ok == true {
em.cache[key] = strings.ToLower(value)
return nil
} else {
return &ExpireMapError{"Value is not of type String."}
}
}
func (em *ExpireMap) ExpiredChan() <-chan string {
em.mutex.Lock()
defer em.mutex.Unlock()
return em.channel
}
func (em *ExpireMap) Cleanup() {
em.mutex.Lock()
for key, _ := range em.quit {
em.quit[key] <- struct{}{}
}
defer em.mutex.Unlock()
em.cache = make(map[string]interface{})
em.durations = make(map[string]time.Time)
em.channel = make(chan string)
- //em.goroutines = 0
em.quit = make(map[string]chan struct{})
}
func (em *ExpireMap) Destroy() {
em.mutex.Lock()
-
+ defer em.mutex.Unlock()
for key, _ := range em.quit {
em.quit[key] <- struct{}{}
}
- em.mutex.Unlock()
em.cache = nil
em.durations = nil
close(em.channel)
for key, _ := range em.quit {
close(em.quit[key])
}
em.quit = nil
}

Ангел обнови решението на 18.11.2014 16:25 (преди над 3 години)

package main
import (
"strconv"
"strings"
"sync"
"time"
)
type ExpireMap struct {
mutex *sync.Mutex
cache map[string]interface{}
durations map[string]time.Time
channel chan string
- quit map[string]chan struct{}
+ quit map[string]chan struct{}
}
type ExpireMapError struct {
message string
}
func (e *ExpireMapError) Error() string {
return e.message
}
func NewExpireMap() *ExpireMap {
var em ExpireMap
em.cache = make(map[string]interface{})
em.mutex = &sync.Mutex{}
em.durations = make(map[string]time.Time)
em.channel = make(chan string)
em.quit = make(map[string]chan struct{})
return &em
}
func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
em.mutex.Lock()
defer em.mutex.Unlock()
if _, ok := em.cache[key]; ok {
em.quit[key] <- struct{}{}
} else {
em.quit[key] = make(chan struct{})
}
em.cache[key] = value
em.durations[key] = time.Now().Add(expire)
go func(em *ExpireMap, key string, expire time.Duration) {
select {
case <-em.quit[key]:
em.mutex.Lock()
delete(em.quit, key)
em.mutex.Unlock()
return
case <-time.After(expire):
em.mutex.Lock()
_, ok := em.cache[key]
em.mutex.Unlock()
em.Delete(key)
if ok {
select {
case em.channel <- key:
em.mutex.Lock()
delete(em.quit, key)
em.mutex.Unlock()
return
case <-em.quit[key]:
em.mutex.Lock()
delete(em.quit, key)
em.mutex.Unlock()
return
}
}
return
}
}(em, key, expire)
}
func (em *ExpireMap) Get(key string) (interface{}, bool) {
em.mutex.Lock()
defer em.mutex.Unlock()
value, ok := em.cache[key]
return value, ok
}
func (em *ExpireMap) GetInt(key string) (int, bool) {
value, status := em.Get(key)
if value, ok := value.(int); status && ok {
return value, status
} else {
return 0, false
}
}
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
value, status := em.Get(key)
if value, ok := value.(float64); status && ok {
return value, status
} else {
return 0.0, false
}
}
func (em *ExpireMap) GetString(key string) (string, bool) {
value, status := em.Get(key)
if value, ok := value.(string); status && ok {
return value, status
} else {
return "", false
}
}
func (em *ExpireMap) GetBool(key string) (bool, bool) {
value, status := em.Get(key)
if value, ok := value.(bool); status && ok {
return value, status
} else {
return false, false
}
}
func (em *ExpireMap) Expires(key string) (time.Time, bool) {
em.mutex.Lock()
defer em.mutex.Unlock()
time, status := em.durations[key]
return time, status
}
func (em *ExpireMap) Delete(key string) {
em.mutex.Lock()
defer em.mutex.Unlock()
delete(em.cache, key)
delete(em.durations, key)
}
func (em *ExpireMap) Contains(key string) bool {
em.mutex.Lock()
defer em.mutex.Unlock()
_, ok := em.cache[key]
return ok
}
func (em *ExpireMap) Size() int {
em.mutex.Lock()
defer em.mutex.Unlock()
return len(em.cache)
}
func (em *ExpireMap) Increment(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
- if number, ok := em.cache[key].(int); ok == true {
- em.cache[key] = number + 1
- return nil
- } else if number, ok := strconv.Atoi(em.cache[key].(string)); ok == nil {
- em.cache[key] = strconv.Itoa(number + 1)
- return nil
+ if number, ok := em.cache[key]; ok == true {
+ switch value := number.(type) {
+ case int:
+ em.cache[key] = value + 1
+ return nil
+ case string:
+ if number, ok := strconv.Atoi(value); ok == nil {
+ em.cache[key] = strconv.Itoa(number + 1)
+ return nil
+ }
+ default:
+ return &ExpireMapError{"Value is not of type Int or String."}
+ }
}
- return &ExpireMapError{"Value is not of type Int or String."}
+ return &ExpireMapError{"Element not found !"}
}
func (em *ExpireMap) Decrement(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
- if number, ok := em.cache[key].(int); ok == true {
- em.cache[key] = number - 1
- return nil
- } else if number, ok := strconv.Atoi(em.cache[key].(string)); ok == nil {
- em.cache[key] = strconv.Itoa(number - 1)
- return nil
+ if number, ok := em.cache[key]; ok == true {
+ switch value := number.(type) {
+ case int:
+ em.cache[key] = value - 1
+ return nil
+ case string:
+ if number, ok := strconv.Atoi(value); ok == nil {
+ em.cache[key] = strconv.Itoa(number - 1)
+ return nil
+ }
+ default:
+ return &ExpireMapError{"Value is not of type Int or String."}
+ }
}
-
- return &ExpireMapError{"Value is not of type Int or String."}
+ return &ExpireMapError{"Element not found !"}
}
func (em *ExpireMap) ToUpper(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if value, ok := em.cache[key].(string); ok == true {
em.cache[key] = strings.ToUpper(value)
return nil
} else {
return &ExpireMapError{"Value is not of type String."}
}
}
func (em *ExpireMap) ToLower(key string) error {
em.mutex.Lock()
defer em.mutex.Unlock()
if value, ok := em.cache[key].(string); ok == true {
em.cache[key] = strings.ToLower(value)
return nil
} else {
return &ExpireMapError{"Value is not of type String."}
}
}
func (em *ExpireMap) ExpiredChan() <-chan string {
em.mutex.Lock()
defer em.mutex.Unlock()
return em.channel
}
func (em *ExpireMap) Cleanup() {
em.mutex.Lock()
for key, _ := range em.quit {
em.quit[key] <- struct{}{}
}
defer em.mutex.Unlock()
em.cache = make(map[string]interface{})
em.durations = make(map[string]time.Time)
em.channel = make(chan string)
em.quit = make(map[string]chan struct{})
}
func (em *ExpireMap) Destroy() {
em.mutex.Lock()
defer em.mutex.Unlock()
for key, _ := range em.quit {
em.quit[key] <- struct{}{}
}
em.cache = nil
em.durations = nil
close(em.channel)
for key, _ := range em.quit {
close(em.quit[key])
}
em.quit = nil
}