Skip to content

Commit

Permalink
This closes:
Browse files Browse the repository at this point in the history
* [issue 2](#2)
* [issue 3](#3)

Changes:
* added a stop channel and Close() function to prevent orphaned goroutine
* use maps.Copy() in All() function to prevent unprotected access
* create test for All() function
  • Loading branch information
jftuga committed Nov 7, 2023
1 parent 9f62fb5 commit 725f7d4
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 37 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func main() {
pruneInterval := 1 // search for expired items every 'pruneInterval' seconds
refreshLastAccessOnGet := true // update item's 'lastAccessTime' on a .Get()
t := TtlMap.New(maxTTL, startSize, pruneInterval, refreshLastAccessOnGet)
defer t.Close()

// populate the TtlMap
t.Put("myString", "a b c")
Expand Down
1 change: 1 addition & 0 deletions example/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func main() {
pruneInterval := 1 // search for expired items every 'pruneInterval' seconds
refreshLastAccessOnGet := true // update item's lastAccessTime on a .Get()
t := TtlMap.New(maxTTL, startSize, pruneInterval, refreshLastAccessOnGet)
defer t.Close()

// populate the TtlMap
t.Put("string", "a b c")
Expand Down
1 change: 1 addition & 0 deletions example/small/small.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func main() {
pruneInterval := 1 // search for expired items every 'pruneInterval' seconds
refreshLastAccessOnGet := true // update item's 'lastAccessTime' on a .Get()
t := TtlMap.New(maxTTL, startSize, pruneInterval, refreshLastAccessOnGet)
defer t.Close()

// populate the TtlMap
t.Put("myString", "a b c")
Expand Down
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
module github.com/jftuga/TtlMap

go 1.21.3

require github.com/LastPossum/kamino v0.0.1 // indirect
2 changes: 0 additions & 2 deletions go.sum

This file was deleted.

48 changes: 28 additions & 20 deletions ttlMap.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,12 @@ Changes from the referenced implementation
package TtlMap

import (
"maps"
"sync"
"time"

"github.com/LastPossum/kamino"
)

const version string = "1.2.0"
const version string = "1.3.0"

type CustomKeyType string

Expand All @@ -46,27 +45,33 @@ type TtlMap struct {
m map[CustomKeyType]*item
l sync.Mutex
refresh bool
stop chan bool
}

func New(maxTTL int, ln int, pruneInterval int, refreshLastAccessOnGet bool) (m *TtlMap) {
// if pruneInterval > maxTTL {
// print("WARNING: TtlMap: pruneInterval > maxTTL\n")
// }
m = &TtlMap{m: make(map[CustomKeyType]*item, ln)}
m = &TtlMap{m: make(map[CustomKeyType]*item, ln), stop: make(chan bool)}
m.refresh = refreshLastAccessOnGet
go func() {
for now := range time.Tick(time.Second * time.Duration(pruneInterval)) {
currentTime := now.Unix()
m.l.Lock()
for k, v := range m.m {
// print("TICK:", currentTime, " ", v.lastAccess, " ", (currentTime - v.lastAccess), " ", maxTTL, " ", k, "\n")
if currentTime-v.lastAccess >= int64(maxTTL) {
delete(m.m, k)
// print("deleting: ", k, "\n")
for {
select {
case <-m.stop:
return
case now := <-time.Tick(time.Second * time.Duration(pruneInterval)):
currentTime := now.Unix()
m.l.Lock()
for k, v := range m.m {
//print("TICK:", currentTime, " ", v.lastAccess, " ", (currentTime - v.lastAccess), " ", maxTTL, " ", k, "\n")
if currentTime-v.lastAccess >= int64(maxTTL) {
delete(m.m, k)
// print("deleting: ", k, "\n")
}
}
// print("\n")
m.l.Unlock()
}
// print("\n")
m.l.Unlock()
}
}()
return
Expand Down Expand Up @@ -118,10 +123,13 @@ func (m *TtlMap) Clear() {
}

func (m *TtlMap) All() map[CustomKeyType]*item {
copy, err := kamino.Clone(m.m)
if err != nil {
print(err)
return nil
}
return copy
m.l.Lock()
dst := make(map[CustomKeyType]*item, len(m.m))
maps.Copy(dst, m.m)
m.l.Unlock()
return dst
}

func (m *TtlMap) Close() {
m.stop <- true
}
16 changes: 3 additions & 13 deletions ttlMap_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package TtlMap

import (
"sort"
"strings"
"maps"
"testing"
"time"
)
Expand Down Expand Up @@ -173,17 +172,8 @@ func TestAllFunc(t *testing.T) {
var u = uint64(123456789)
tm.Put("uint64", u)

var allKeys []string

allItems := tm.All()
for k := range allItems {
//t.Logf("%v\n", k)
allKeys = append(allKeys, string(k))
}
sort.Strings(allKeys)
joined := strings.Join(allKeys[:], ",")
correct := "boolean,byte,int,int_array,myString,uint64"
if joined != correct {
t.Fatalf("joined should equal correct...\njoined : %v\ncorrect: %v\n", joined, correct)
if !maps.Equal(allItems, tm.m) {
t.Fatalf("allItems and tm.m are not equal\n")
}
}

0 comments on commit 725f7d4

Please sign in to comment.