Bladeren bron

cow cache is not same, as map read will modify the underlying map. use sync.Map for 1.9 and above, and mutex if sync.Map not available

Tao Wen 7 jaren geleden
bovenliggende
commit
28452fcdec
3 gewijzigde bestanden met toevoegingen van 106 en 56 verwijderingen
  1. 1 56
      feature_config.go
  2. 51 0
      feature_config_with_sync_map.go
  3. 54 0
      feature_config_without_sync_map.go

+ 1 - 56
feature_config.go

@@ -5,7 +5,6 @@ import (
 	"errors"
 	"io"
 	"reflect"
-	"sync/atomic"
 	"unsafe"
 )
 
@@ -23,19 +22,6 @@ type Config struct {
 	ObjectFieldMustBeSimpleString bool
 }
 
-type frozenConfig struct {
-	configBeforeFrozen            Config
-	sortMapKeys                   bool
-	indentionStep                 int
-	objectFieldMustBeSimpleString bool
-	onlyTaggedField               bool
-	decoderCache                  unsafe.Pointer
-	encoderCache                  unsafe.Pointer
-	extensions                    []Extension
-	streamPool                    chan *Stream
-	iteratorPool                  chan *Iterator
-}
-
 // API the public interface of this package.
 // Primary Marshal and Unmarshal.
 type API interface {
@@ -83,8 +69,7 @@ func (cfg Config) Froze() API {
 		streamPool:                    make(chan *Stream, 16),
 		iteratorPool:                  make(chan *Iterator, 16),
 	}
-	atomic.StorePointer(&frozenConfig.decoderCache, unsafe.Pointer(&map[string]ValDecoder{}))
-	atomic.StorePointer(&frozenConfig.encoderCache, unsafe.Pointer(&map[string]ValEncoder{}))
+	frozenConfig.initCache()
 	if cfg.MarshalFloatWith6Digits {
 		frozenConfig.marshalFloatWith6Digits()
 	}
@@ -198,46 +183,6 @@ func (cfg *frozenConfig) escapeHTML() {
 	cfg.addEncoderToCache(reflect.TypeOf((*string)(nil)).Elem(), &htmlEscapedStringEncoder{})
 }
 
-func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect.Type, decoder ValDecoder) {
-	done := false
-	for !done {
-		ptr := atomic.LoadPointer(&cfg.decoderCache)
-		cache := *(*map[reflect.Type]ValDecoder)(ptr)
-		copied := map[reflect.Type]ValDecoder{}
-		for k, v := range cache {
-			copied[k] = v
-		}
-		copied[cacheKey] = decoder
-		done = atomic.CompareAndSwapPointer(&cfg.decoderCache, ptr, unsafe.Pointer(&copied))
-	}
-}
-
-func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect.Type, encoder ValEncoder) {
-	done := false
-	for !done {
-		ptr := atomic.LoadPointer(&cfg.encoderCache)
-		cache := *(*map[reflect.Type]ValEncoder)(ptr)
-		copied := map[reflect.Type]ValEncoder{}
-		for k, v := range cache {
-			copied[k] = v
-		}
-		copied[cacheKey] = encoder
-		done = atomic.CompareAndSwapPointer(&cfg.encoderCache, ptr, unsafe.Pointer(&copied))
-	}
-}
-
-func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect.Type) ValDecoder {
-	ptr := atomic.LoadPointer(&cfg.decoderCache)
-	cache := *(*map[reflect.Type]ValDecoder)(ptr)
-	return cache[cacheKey]
-}
-
-func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
-	ptr := atomic.LoadPointer(&cfg.encoderCache)
-	cache := *(*map[reflect.Type]ValEncoder)(ptr)
-	return cache[cacheKey]
-}
-
 func (cfg *frozenConfig) cleanDecoders() {
 	typeDecoders = map[string]ValDecoder{}
 	fieldDecoders = map[string]ValDecoder{}

+ 51 - 0
feature_config_with_sync_map.go

@@ -0,0 +1,51 @@
+//+build go1.9
+
+package jsoniter
+
+import (
+	"reflect"
+	"sync"
+)
+
+type frozenConfig struct {
+	configBeforeFrozen            Config
+	sortMapKeys                   bool
+	indentionStep                 int
+	objectFieldMustBeSimpleString bool
+	onlyTaggedField               bool
+	decoderCache                  sync.Map
+	encoderCache                  sync.Map
+	extensions                    []Extension
+	streamPool                    chan *Stream
+	iteratorPool                  chan *Iterator
+}
+
+func (cfg *frozenConfig) initCache() {
+	cfg.decoderCache = sync.Map{}
+	cfg.encoderCache = sync.Map{}
+}
+
+
+func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect.Type, decoder ValDecoder) {
+	cfg.decoderCache.Store(cacheKey, decoder)
+}
+
+func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect.Type, encoder ValEncoder) {
+	cfg.encoderCache.Store(cacheKey, encoder)
+}
+
+func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect.Type) ValDecoder {
+	decoder, found := cfg.decoderCache.Load(cacheKey)
+	if found {
+		return decoder.(ValDecoder)
+	}
+	return nil
+}
+
+func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
+	encoder, found := cfg.encoderCache.Load(cacheKey)
+	if found {
+		return encoder.(ValEncoder)
+	}
+	return nil
+}

+ 54 - 0
feature_config_without_sync_map.go

@@ -0,0 +1,54 @@
+//+build !go1.9
+
+package jsoniter
+
+import (
+	"reflect"
+	"sync"
+)
+
+type frozenConfig struct {
+	configBeforeFrozen            Config
+	sortMapKeys                   bool
+	indentionStep                 int
+	objectFieldMustBeSimpleString bool
+	onlyTaggedField               bool
+	cacheLock                     *sync.RWMutex
+	decoderCache                  map[reflect.Type]ValDecoder
+	encoderCache                  map[reflect.Type]ValEncoder
+	extensions                    []Extension
+	streamPool                    chan *Stream
+	iteratorPool                  chan *Iterator
+}
+
+func (cfg *frozenConfig) initCache() {
+	cfg.cacheLock = &sync.RWMutex{}
+	cfg.decoderCache = map[reflect.Type]ValDecoder{}
+	cfg.encoderCache = map[reflect.Type]ValEncoder{}
+}
+
+func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect.Type, decoder ValDecoder) {
+	cfg.cacheLock.Lock()
+	cfg.decoderCache[cacheKey] = decoder
+	cfg.cacheLock.Unlock()
+}
+
+func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect.Type, encoder ValEncoder) {
+	cfg.cacheLock.Lock()
+	cfg.encoderCache[cacheKey] = encoder
+	cfg.cacheLock.Unlock()
+}
+
+func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect.Type) ValDecoder {
+	cfg.cacheLock.RLock()
+	decoder, _ := cfg.decoderCache[cacheKey].(ValDecoder)
+	cfg.cacheLock.RUnlock()
+	return decoder
+}
+
+func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
+	cfg.cacheLock.RLock()
+	encoder, _ := cfg.encoderCache[cacheKey].(ValEncoder)
+	cfg.cacheLock.RUnlock()
+	return encoder
+}