1//  Copyright (c) 2017 Couchbase, Inc.
2//
3//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
4//  except in compliance with the License. You may obtain a copy of the License at
5//    http://www.apache.org/licenses/LICENSE-2.0
6//  Unless required by applicable law or agreed to in writing, software distributed under the
7//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
8//  either express or implied. See the License for the specific language governing permissions
9//  and limitations under the License.
10
11package util
12
13// We implement here sync types that in sync don't do exactly what we want.
14// Our implementation tends to be leaner too.
15
16import (
17	"sync"
18	"unsafe"
19
20	atomic "github.com/couchbase/go-couchbase/platform"
21)
22
23// Once is an object that will perform exactly one action.
24type Once struct {
25	done uint32
26}
27
28// Do calls the function f if and only if Do is being called for the
29// first time for this instance of Once. In other words, given
30// 	var once Once
31// if once.Do(f) is called multiple times, only the first call will invoke f,
32// even if f has a different value in each invocation. A new instance of
33// Once is required for each function to execute.
34//
35// Do is intended for initialization that must be run exactly once. Since f
36// is niladic, it may be necessary to use a function literal to capture the
37// arguments to a function to be invoked by Do:
38// 	config.once.Do(func() { config.init(filename) })
39//
40// Because no call to Do returns until the one call to f returns, if f causes
41// Do to be called, it will deadlock.
42//
43// If f panics, Do considers it to have returned; future calls of Do return
44// without calling f.
45//
46// Our Once type can be reset
47func (o *Once) Do(f func()) {
48	if atomic.LoadUint32(&o.done) > 0 {
49		return
50	}
51
52	// Slow-path.
53	if atomic.AddUint32(&o.done, 1) == 1 {
54		f()
55	}
56}
57
58func (o *Once) Reset() {
59	atomic.StoreUint32(&o.done, 0)
60}
61
62const _POOL_BUCKETS = 16
63const _POOL_SIZE = 1024
64
65type FastPool struct {
66	getNext   uint32
67	putNext   uint32
68	useCount  int32
69	freeCount int32
70	f         func() interface{}
71	pool      [_POOL_BUCKETS]poolList
72	free      [_POOL_BUCKETS]poolList
73}
74
75type poolList struct {
76	head *poolEntry
77	tail *poolEntry
78	sync.Mutex
79}
80
81type poolEntry struct {
82	entry interface{}
83	next  *poolEntry
84}
85
86func NewFastPool(p *FastPool, f func() interface{}) {
87	*p = FastPool{}
88	p.f = f
89}
90
91func (p *FastPool) Get() interface{} {
92	if atomic.LoadInt32(&p.useCount) == 0 {
93		return p.f()
94	}
95	l := atomic.AddUint32(&p.getNext, 1) % _POOL_BUCKETS
96	e := p.pool[l].Get()
97	if e == nil {
98		return p.f()
99	}
100	atomic.AddInt32(&p.useCount, -1)
101	rv := e.entry
102	e.entry = nil
103	if atomic.LoadInt32(&p.freeCount) < _POOL_SIZE {
104		atomic.AddInt32(&p.freeCount, 1)
105		p.free[l].Put(e)
106	}
107	return rv
108}
109
110func (p *FastPool) Put(s interface{}) {
111	if atomic.LoadInt32(&p.useCount) >= _POOL_SIZE {
112		return
113	}
114	l := atomic.AddUint32(&p.putNext, 1) % _POOL_BUCKETS
115	e := p.free[l].Get()
116	if e == nil {
117		e = &poolEntry{}
118	} else {
119		atomic.AddInt32(&p.freeCount, -1)
120	}
121	e.entry = s
122	p.pool[l].Put(e)
123	atomic.AddInt32(&p.useCount, 1)
124}
125
126func (l *poolList) Get() *poolEntry {
127	if l.head == nil {
128		return nil
129	}
130
131	l.Lock()
132	if l.head == nil {
133		l.Unlock()
134		return nil
135	}
136	rv := l.head
137	l.head = rv.next
138	l.Unlock()
139	rv.next = nil
140	return rv
141}
142
143func (l *poolList) Put(e *poolEntry) {
144	l.Lock()
145	if l.head == nil {
146		l.head = e
147	} else {
148		l.tail.next = e
149	}
150	l.tail = e
151	l.Unlock()
152}
153
154type LocklessPool struct {
155	getNext uint32
156	putNext uint32
157	f       func() unsafe.Pointer
158	pool    [_POOL_SIZE]unsafe.Pointer
159}
160
161func NewLocklessPool(p *LocklessPool, f func() unsafe.Pointer) {
162	*p = LocklessPool{}
163	p.f = f
164}
165
166func (p *LocklessPool) Get() unsafe.Pointer {
167	l := atomic.LoadUint32(&p.getNext) % _POOL_BUCKETS
168	e := atomic.SwapPointer(&p.pool[l], nil)
169
170	// niet
171	if e == nil {
172		return p.f()
173	} else {
174
175		// move to the next slot
176		atomic.AddUint32(&p.getNext, 1)
177		return e
178	}
179
180	return e
181}
182
183func (p *LocklessPool) Put(s unsafe.Pointer) {
184	l := (atomic.AddUint32(&p.putNext, 1) - 1) % _POOL_BUCKETS
185	atomic.StorePointer(&p.pool[l], s)
186}
187