Skip to content

Commit

Permalink
Merge pull request #63 from webability-go/late-night
Browse files Browse the repository at this point in the history
patch v2.0.9
  • Loading branch information
metalwolf committed Nov 26, 2021
2 parents 18ebcd7 + a34aa51 commit 5d69614
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 39 deletions.
5 changes: 5 additions & 0 deletions v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ Some improvements to check, later:
Version Changes Control
=======================

v2.0.9 - 2021-11-25
-----------------------
- XCache modified to defer mutex unlocks instead of directly unlock into the code, to avoid dead locks in case of thread panic and crashes.
- XLanguage modified to defer mutex unlocks instead of directly unlock into the code, to avoid dead locks in case of thread panic and crashes.

v2.0.8 - 2021-05-18
-----------------------
- XTemplate is now clonable: newtemplate := template.Clone()
Expand Down
86 changes: 56 additions & 30 deletions v2/xcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,17 @@ func NewXCache(id string, maxitems int, expire time.Duration) *XCache {
// Returns nothing.
func (c *XCache) Set(key string, indata interface{}) {
c.mutex.Lock()
defer c.mutex.Unlock()
// check if the entry already exists
_, ok := c.items[key]
c.items[key] = &XCacheEntry{ctime: time.Now(), rtime: time.Now(), data: indata}
if ok {
c.removeFromPile(key)
}
c.pile = append(c.pile, key)
c.mutex.Unlock()
if c.Maxitems > 0 && len(c.items) >= c.Maxitems {
// We need a cleaning
c.Clean(10)
c.mClean(10)
}
}

Expand All @@ -83,11 +83,11 @@ func (c *XCache) Set(key string, indata interface{}) {
// Returns nothing.
func (c *XCache) SetTTL(key string, duration time.Duration) {
c.mutex.Lock()
defer c.mutex.Unlock()
_, ok := c.items[key]
if ok {
c.items[key].ttl = duration
}
c.mutex.Unlock()
}

// removeFromPile will remove an entry key from the ordered pile.
Expand All @@ -106,21 +106,30 @@ func (c *XCache) removeFromPile(key string) {
}
}

// mGet will get the entry of the cache.
func (c *XCache) mGet(key string) (*XCacheEntry, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
x, ok := c.items[key]
return x, ok
}

// Get will get the value of an entry.
// If the entry does not exists, returns nil, false.
// If the entry exists and is invalidated by time or validator function, then returns nil, true.
// If the entry is good, return <value>, false.
func (c *XCache) Get(key string) (interface{}, bool) {
c.mutex.RLock()
if x, ok := c.items[key]; ok {
c.mutex.RUnlock()
if x, ok := c.mGet(key); ok {
// expired by TTL ?
if x.ttl != 0 {
if x.ctime.Add(x.ttl).Before(time.Now()) {
c.mutex.Lock()
delete(c.items, key)
c.removeFromPile(key)
c.mutex.Unlock()
c.Del(key)
/*
c.mutex.Lock()
delete(c.items, key)
c.removeFromPile(key)
c.mutex.Unlock()
*/
return nil, true
}
}
Expand All @@ -129,10 +138,13 @@ func (c *XCache) Get(key string) (interface{}, bool) {
if LOG {
log.Println("Validator invalids entry: " + key)
}
c.mutex.Lock()
delete(c.items, key)
c.removeFromPile(key)
c.mutex.Unlock()
c.Del(key)
/*
c.mutex.Lock()
delete(c.items, key)
c.removeFromPile(key)
c.mutex.Unlock()
*/
return nil, true
}
}
Expand All @@ -142,51 +154,52 @@ func (c *XCache) Get(key string) (interface{}, bool) {
if LOG {
log.Println("Cache timeout Expired: " + key)
}
c.mutex.Lock()
delete(c.items, key)
c.removeFromPile(key)
c.mutex.Unlock()
c.Del(key)
/*
c.mutex.Lock()
delete(c.items, key)
c.removeFromPile(key)
c.mutex.Unlock()
*/
return nil, true
}
}
x.rtime = time.Now()
c.mutex.Lock()
defer c.mutex.Unlock()
c.removeFromPile(key)
c.pile = append(c.pile, key)
c.mutex.Unlock()
return x.data, false
}
c.mutex.RUnlock()
return nil, false
}

// Del will delete the entry of the cache if it exists.
func (c *XCache) Del(key string) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.items, key)
// we should check if the entry exists before trying to removing
c.removeFromPile(key)
c.mutex.Unlock()
}

// Count will return the quantity of entries in the cache.
func (c *XCache) Count() int {
c.mutex.RLock()
defer c.mutex.RUnlock()
x := len(c.items)
c.mutex.RUnlock()
return x
}

// Clean will delete expired entries, and free perc% of max items based on time.
// mClean will delete expired entries, and free perc% of max items based on time. It does not set locks. The caller must do it
// perc = 0 to 100 (percentage to clean).
// Returns quantity of removed entries.
// It Will **not** verify the cache against its source (if Validator is set). If you want to scan that, use the Verify function.
func (c *XCache) Clean(perc int) int {
func (c *XCache) mClean(perc int) int {
if LOG {
log.Println("Cleaning cache")
}
i := 0
c.mutex.Lock()
// 1. clean all expired items
if c.Expire != 0 {
for k, x := range c.items {
Expand All @@ -210,10 +223,19 @@ func (c *XCache) Clean(perc int) int {
delete(c.items, c.pile[i])
}
c.pile = c.pile[i:]
c.mutex.Unlock()
return i
}

// Clean will delete expired entries, and free perc% of max items based on time.
// perc = 0 to 100 (percentage to clean).
// Returns quantity of removed entries.
// It Will **not** verify the cache against its source (if Validator is set). If you want to scan that, use the Verify function.
func (c *XCache) Clean(perc int) int {
c.mutex.Lock()
defer c.mutex.Unlock()
return c.mClean(perc)
}

// Verify will first, Clean(0) keeping all the entries, then will delete expired entries using the Validator function.
// Returns the quantity of removed entries.
// Based on what the validator function does, calling Verify can be **very** slow and cpu dependant. Be very careful.
Expand All @@ -227,9 +249,13 @@ func (c *XCache) Verify() int {
if LOG {
log.Println("Validator invalids entry: " + k)
}
c.mutex.Lock()
delete(c.items, k)
c.mutex.Unlock()
c.Del(k)
/*
c.mutex.Lock()
delete(c.items, k)
c.removeFromPile(k)
c.mutex.Unlock()
*/
i++
}
}
Expand All @@ -241,7 +267,7 @@ func (c *XCache) Verify() int {
// Returns nothing.
func (c *XCache) Flush() {
c.mutex.Lock()
defer c.mutex.Unlock()
// how to really deletes the data ? ( to free memory)
c.items = make(map[string]*XCacheEntry)
c.mutex.Unlock()
}
2 changes: 1 addition & 1 deletion v2/xcore.go
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,7 @@
package xcore

// VERSION is the used version nombre of the XCore library.
const VERSION = "2.0.8"
const VERSION = "2.0.9"

// LOG is the flag to activate logging on the library.
// if LOG is set to TRUE, LOG indicates to the XCore libraries to log a trace of functions called, with most important parameters.
Expand Down
16 changes: 8 additions & 8 deletions v2/xlanguage.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ func (l *XLanguage) LoadXMLString(data string) error {
l.Name = temp.Name
l.Language, _ = language.Parse(temp.Language)
l.mutex.Lock()
defer l.mutex.Unlock()
for _, e := range temp.Entries {
l.entries[e.ID] = e.Entry
}
l.mutex.Unlock()
return nil
}

Expand Down Expand Up @@ -157,8 +157,8 @@ func (l *XLanguage) LoadString(data string) error {
value = strings.TrimSpace(line[posequal+1:])
}
l.mutex.Lock()
defer l.mutex.Unlock()
l.entries[key] = value
l.mutex.Unlock()
}
if err := scanner.Err(); err != nil {
return err
Expand Down Expand Up @@ -189,15 +189,15 @@ func (l *XLanguage) GetLanguage() language.Tag {
// Set will add an entry id-value into the language table
func (l *XLanguage) Set(entry string, value string) {
l.mutex.Lock()
defer l.mutex.Unlock()
l.entries[entry] = value
l.mutex.Unlock()
}

// Get will read an entry id-value from the language table
func (l *XLanguage) Get(entry string) string {
l.mutex.RLock()
defer l.mutex.RUnlock()
v, ok := l.entries[entry]
l.mutex.RUnlock()
if ok {
return v
}
Expand All @@ -207,29 +207,29 @@ func (l *XLanguage) Get(entry string) string {
// Del will remove an entry id-value from the language table
func (l *XLanguage) Del(entry string) {
l.mutex.Lock()
defer l.mutex.Unlock()
delete(l.entries, entry)
l.mutex.Unlock()
}

// GetEntries will return a COPY of the key-values pairs of the language.
func (l *XLanguage) GetEntries() map[string]string {
clone := map[string]string{}
l.mutex.RLock()
defer l.mutex.RUnlock()
for k, v := range l.entries {
clone[k] = v
}
l.mutex.RUnlock()
return clone
}

// String will transform the XDataset into a readable string for humans
func (l *XLanguage) String() string {
sdata := []string{}
l.mutex.RLock()
defer l.mutex.RUnlock()
for key, val := range l.entries {
sdata = append(sdata, key+":"+fmt.Sprintf("%v", val))
}
l.mutex.RUnlock()
sort.Strings(sdata) // Lets be sure the print is always the same presentation
return "xcore.XLanguage{" + strings.Join(sdata, " ") + "}"
}
Expand All @@ -238,10 +238,10 @@ func (l *XLanguage) String() string {
func (l *XLanguage) GoString() string {
sdata := []string{}
l.mutex.RLock()
defer l.mutex.RUnlock()
for key, val := range l.entries {
sdata = append(sdata, key+":"+fmt.Sprintf("%#v", val))
}
l.mutex.RUnlock()
sort.Strings(sdata) // Lets be sure the print is always the same presentation
return "#xcore.XLanguage{" + strings.Join(sdata, " ") + "}"
}

0 comments on commit 5d69614

Please sign in to comment.