Procházet zdrojové kódy

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 před 7 roky
rodič
revize
28452fcdec

+ 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
+}