From 28452fcdec4e44348d2af0d91d1e9e38da3a9e0a Mon Sep 17 00:00:00 2001 From: Tao Wen Date: Sun, 28 Jan 2018 17:00:11 +0800 Subject: [PATCH] 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 --- feature_config.go | 57 +----------------------------- feature_config_with_sync_map.go | 51 ++++++++++++++++++++++++++ feature_config_without_sync_map.go | 54 ++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 56 deletions(-) create mode 100644 feature_config_with_sync_map.go create mode 100644 feature_config_without_sync_map.go diff --git a/feature_config.go b/feature_config.go index 0e860df9..2d2fc8cd 100644 --- a/feature_config.go +++ b/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{} diff --git a/feature_config_with_sync_map.go b/feature_config_with_sync_map.go new file mode 100644 index 00000000..e81e1b7f --- /dev/null +++ b/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 +} \ No newline at end of file diff --git a/feature_config_without_sync_map.go b/feature_config_without_sync_map.go new file mode 100644 index 00000000..f01f971e --- /dev/null +++ b/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 +}