Skip to content

Commit

Permalink
try to be concrrent safe in reload
Browse files Browse the repository at this point in the history
so there is a simple way to reload config. need to create a sample
for this
  • Loading branch information
fzerorubigd committed May 8, 2017
1 parent ba095aa commit 520c6da
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 50 deletions.
101 changes: 59 additions & 42 deletions onion.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"
"sync"
"time"
"runtime"
)

// DefaultDelimiter is the default delimiter for the config scope
Expand Down Expand Up @@ -37,6 +38,7 @@ type layerList []singleLayer
type variable struct {
key string
ref interface{}
def interface{}
}

// Onion is a layer base configuration system
Expand Down Expand Up @@ -427,98 +429,113 @@ func (o *Onion) GetStringSlice(key string) []string {
return nil
}

func (o *Onion) addRef(key string, ref interface{}) {
func (o *Onion) addRef(key string, ref interface{}, def interface{}) {
o.refLock.Lock()
defer o.refLock.Unlock()

o.references = append(o.references, variable{key: key, ref: ref})
o.references = append(o.references, variable{key: key, ref: ref, def: def})
}

// RegisterInt return an int variable and set the value when the config is loaded
func (o *Onion) RegisterInt(key string, def int) *int {
var v = def
o.addRef(key, &v)
func (o *Onion) RegisterInt(key string, def int) Int {
var v = int64(def)
o.addRef(key, &v, def)

return &v
return intHolder{value: &v}
}

// RegisterInt64 return an int64 variable and set the value when the config is loaded
func (o *Onion) RegisterInt64(key string, def int64) *int64 {
func (o *Onion) RegisterInt64(key string, def int64) Int {
var v = def
o.addRef(key, &v)
o.addRef(key, &v, def)

return &v
return intHolder{value: &v}
}

// RegisterString return an string variable and set the value when the config is loaded
func (o *Onion) RegisterString(key string, def string) *string {
func (o *Onion) RegisterString(key string, def string) String {
var v = def
o.addRef(key, &v)
o.addRef(key, &v, def)

return &v
return stringHolder{value: &v}
}

// RegisterFloat64 return an float64 variable and set the value when the config is loaded
func (o *Onion) RegisterFloat64(key string, def float64) *float64 {
func (o *Onion) RegisterFloat64(key string, def float64) Float {
var v = def
o.addRef(key, &v)
o.addRef(key, &v, def)

return &v
return floatHolder{value: &v}
}

// RegisterFloat32 return an float32 variable and set the value when the config is loaded
func (o *Onion) RegisterFloat32(key string, def float32) *float32 {
var v = def
o.addRef(key, &v)
func (o *Onion) RegisterFloat32(key string, def float32) Float {
var v = float64(def)
o.addRef(key, &v, def)

return &v
return floatHolder{value: &v}
}

// RegisterBool return an bool variable and set the value when the config is loaded
func (o *Onion) RegisterBool(key string, def bool) *bool {
func (o *Onion) RegisterBool(key string, def bool) Bool {
var v = def
o.addRef(key, &v)
o.addRef(key, &v, def)

return &v
return boolHolder{value: &v}
}

// RegisterDuration return an duration variable and set the value when the config is loaded
func (o *Onion) RegisterDuration(key string, def time.Duration) *time.Duration {
var v = def
o.addRef(key, &v)
func (o *Onion) RegisterDuration(key string, def time.Duration) Int {
var v = int64(def)
o.addRef(key, &v, def)

return &v
return intHolder{value: &v}
}

// Load function is the new behavior of onion after version 3. after calling this all variables
// registered with Registered* function are loaded.
// this function is concurrent safe.
// also this function had no effect on getting variables directly from config by Get* functions
func (o *Onion) Load() {
o.refLock.RLock()
defer o.refLock.RUnlock()
runtime.Gosched()

// Make sure all variables are locked
// TODO : lock per onion instance
globalLock.Lock()
defer globalLock.Unlock()

for i := range o.references {
switch t := o.references[i].ref.(type) {
case *int:
v := o.GetIntDefault(o.references[i].key, *t)
*t = v
case *int64:
v := o.GetInt64Default(o.references[i].key, *t)
switch def := o.references[i].def.(type) {
case int:
v := o.GetInt64Default(o.references[i].key, int64(def))
t := o.references[i].ref.(*int64)
*t = v
case *string:
v := o.GetStringDefault(o.references[i].key, *t)
case int64:
v := o.GetInt64Default(o.references[i].key, def)
t := o.references[i].ref.(*int64)
*t = v
case *float64:
v := o.GetFloat64Default(o.references[i].key, *t)
case string:
v := o.GetStringDefault(o.references[i].key, def)
t := o.references[i].ref.(*string)
*t = v
case *float32:
v := o.GetFloat32Default(o.references[i].key, *t)
case float32:
v := o.GetFloat64Default(o.references[i].key, float64(def))
t := o.references[i].ref.(*float64)
*t = v
case *bool:
v := o.GetBoolDefault(o.references[i].key, *t)
case float64:
v := o.GetFloat64Default(o.references[i].key, def)
t := o.references[i].ref.(*float64)
*t = v
case *time.Duration:
v := o.GetDurationDefault(o.references[i].key, *t)
case bool:
v := o.GetBoolDefault(o.references[i].key, def)
t := o.references[i].ref.(*bool)
*t = v
case time.Duration:
v := o.GetDurationDefault(o.references[i].key, def)
t := o.references[i].ref.(*int64)
*t = int64(v)
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions onion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,19 +336,19 @@ func TestOnion(t *testing.T) {
def.SetDefault("test.float64", 100.11)
def.SetDefault("test.float32", 100.11)
def.SetDefault("test.bool", true)
def.SetDefault("test.duration", time.Second)
def.SetDefault("test.duration", "1s")

So(o.AddLayer(def), ShouldBeNil)

o.Load()

So(*intVar, ShouldEqual, 100)
So(*int64Var, ShouldEqual, 100)
So(*float64Var, ShouldEqual, 100.11)
So(*float32Var, ShouldEqual, 100.11)
So(*stringVar, ShouldEqual, "TEST_SET")
So(*boolVar, ShouldBeTrue)
So(*durationVar, ShouldEqual, time.Second)
So(intVar.Int(), ShouldEqual, 100)
So(int64Var.Int64(), ShouldEqual, 100)
So(float64Var.Float64(), ShouldEqual, 100.11)
So(float32Var.Float32(), ShouldEqual, 100.11)
So(stringVar.String(), ShouldEqual, "TEST_SET")
So(boolVar.Bool(), ShouldBeTrue)
So(durationVar.Duration(), ShouldEqual, time.Second)

o.Reset()
o.Load()
Expand Down
102 changes: 102 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package onion

import (
"sync"
"time"
)

var (
globalLock = &sync.RWMutex{}
)

// String is the string value holder, with safe lock for concurrent reload
type String interface {
String() string
}

// Int is the int value holder, with safe lock for concurrent reload
type Int interface {
// Return the int value
Int() int
// Return int64 value
Int64() int64
// Return duration value
Duration() time.Duration
}

// Bool is the bool value holder, with safe lock for concurrent reload
type Bool interface {
// The bool value
Bool() bool
}

// Bool is the bool value holder, with safe lock for concurrent reload
type Float interface {
Float32() float32
Float64() float64
}

type stringHolder struct {
value *string
}

func (sh stringHolder) String() string {
globalLock.RLock()
defer globalLock.RUnlock()

return *sh.value
}

type intHolder struct {
value *int64
}

func (ih intHolder) Int() int {
globalLock.RLock()
defer globalLock.RUnlock()

return int(*ih.value)
}

func (ih intHolder) Int64() int64 {
globalLock.RLock()
defer globalLock.RUnlock()

return *ih.value
}

func (ih intHolder) Duration() time.Duration {
globalLock.RLock()
defer globalLock.RUnlock()

return time.Duration(*ih.value)
}

type boolHolder struct {
value *bool
}

func (bh boolHolder) Bool() bool {
globalLock.RLock()
defer globalLock.RUnlock()

return *bh.value
}

type floatHolder struct {
value *float64
}

func (fh floatHolder) Float32() float32 {
globalLock.RLock()
defer globalLock.RUnlock()

return float32(*fh.value)
}

func (fh floatHolder) Float64() float64 {
globalLock.RLock()
defer globalLock.RUnlock()

return *fh.value
}

0 comments on commit 520c6da

Please sign in to comment.