mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			174 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
|  * Copyright 2021 ByteDance Inc.
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| package caching
 | |
| 
 | |
| import (
 | |
|     `sync`
 | |
|     `sync/atomic`
 | |
|     `unsafe`
 | |
| 
 | |
|     `github.com/bytedance/sonic/internal/rt`
 | |
| )
 | |
| 
 | |
| /** Program Map **/
 | |
| 
 | |
| const (
 | |
|     _LoadFactor   = 0.5
 | |
|     _InitCapacity = 4096    // must be a power of 2
 | |
| )
 | |
| 
 | |
| type _ProgramMap struct {
 | |
|     n uint64
 | |
|     m uint32
 | |
|     b []_ProgramEntry
 | |
| }
 | |
| 
 | |
| type _ProgramEntry struct {
 | |
|     vt *rt.GoType
 | |
|     fn interface{}
 | |
| }
 | |
| 
 | |
| func newProgramMap() *_ProgramMap {
 | |
|     return &_ProgramMap {
 | |
|         n: 0,
 | |
|         m: _InitCapacity - 1,
 | |
|         b: make([]_ProgramEntry, _InitCapacity),
 | |
|     }
 | |
| }
 | |
| 
 | |
| func (self *_ProgramMap) copy() *_ProgramMap {
 | |
|     fork := &_ProgramMap{
 | |
|         n: self.n,
 | |
|         m: self.m,
 | |
|         b: make([]_ProgramEntry, len(self.b)),
 | |
|     }
 | |
|     for i, f := range self.b {
 | |
|         fork.b[i] = f
 | |
|     }
 | |
|     return fork
 | |
| }
 | |
| 
 | |
| func (self *_ProgramMap) get(vt *rt.GoType) interface{} {
 | |
|     i := self.m + 1
 | |
|     p := vt.Hash & self.m
 | |
| 
 | |
|     /* linear probing */
 | |
|     for ; i > 0; i-- {
 | |
|         if b := self.b[p]; b.vt == vt {
 | |
|             return b.fn
 | |
|         } else if b.vt == nil {
 | |
|             break
 | |
|         } else {
 | |
|             p = (p + 1) & self.m
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* not found */
 | |
|     return nil
 | |
| }
 | |
| 
 | |
| func (self *_ProgramMap) add(vt *rt.GoType, fn interface{}) *_ProgramMap {
 | |
|     p := self.copy()
 | |
|     f := float64(atomic.LoadUint64(&p.n) + 1) / float64(p.m + 1)
 | |
| 
 | |
|     /* check for load factor */
 | |
|     if f > _LoadFactor {
 | |
|         p = p.rehash()
 | |
|     }
 | |
| 
 | |
|     /* insert the value */
 | |
|     p.insert(vt, fn)
 | |
|     return p
 | |
| }
 | |
| 
 | |
| func (self *_ProgramMap) rehash() *_ProgramMap {
 | |
|     c := (self.m + 1) << 1
 | |
|     r := &_ProgramMap{m: c - 1, b: make([]_ProgramEntry, int(c))}
 | |
| 
 | |
|     /* rehash every entry */
 | |
|     for i := uint32(0); i <= self.m; i++ {
 | |
|         if b := self.b[i]; b.vt != nil {
 | |
|             r.insert(b.vt, b.fn)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* rebuild successful */
 | |
|     return r
 | |
| }
 | |
| 
 | |
| func (self *_ProgramMap) insert(vt *rt.GoType, fn interface{}) {
 | |
|     h := vt.Hash
 | |
|     p := h & self.m
 | |
| 
 | |
|     /* linear probing */
 | |
|     for i := uint32(0); i <= self.m; i++ {
 | |
|         if b := &self.b[p]; b.vt != nil {
 | |
|             p += 1
 | |
|             p &= self.m
 | |
|         } else {
 | |
|             b.vt = vt
 | |
|             b.fn = fn
 | |
|             atomic.AddUint64(&self.n, 1)
 | |
|             return
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* should never happens */
 | |
|     panic("no available slots")
 | |
| }
 | |
| 
 | |
| /** RCU Program Cache **/
 | |
| 
 | |
| type ProgramCache struct {
 | |
|     m sync.Mutex
 | |
|     p unsafe.Pointer
 | |
| }
 | |
| 
 | |
| func CreateProgramCache() *ProgramCache {
 | |
|     return &ProgramCache {
 | |
|         m: sync.Mutex{},
 | |
|         p: unsafe.Pointer(newProgramMap()),
 | |
|     }
 | |
| }
 | |
| 
 | |
| func (self *ProgramCache) Get(vt *rt.GoType) interface{} {
 | |
|     return (*_ProgramMap)(atomic.LoadPointer(&self.p)).get(vt)
 | |
| }
 | |
| 
 | |
| func (self *ProgramCache) Compute(vt *rt.GoType, compute func(*rt.GoType, ... interface{}) (interface{}, error), ex ...interface{}) (interface{}, error) {
 | |
|     var err error
 | |
|     var val interface{}
 | |
| 
 | |
|     /* use defer to prevent inlining of this function */
 | |
|     self.m.Lock()
 | |
|     defer self.m.Unlock()
 | |
| 
 | |
|     /* double check with write lock held */
 | |
|     if val = self.Get(vt); val != nil {
 | |
|         return val, nil
 | |
|     }
 | |
| 
 | |
|     /* compute the value */
 | |
|     if val, err = compute(vt, ex...); err != nil {
 | |
|         return nil, err
 | |
|     }
 | |
| 
 | |
|     /* update the RCU cache */
 | |
|     atomic.StorePointer(&self.p, unsafe.Pointer((*_ProgramMap)(atomic.LoadPointer(&self.p)).add(vt, val)))
 | |
|     return val, nil
 | |
| }
 |