1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty5.util.concurrent;
18
19 import java.util.Arrays;
20 import java.util.BitSet;
21 import java.util.concurrent.atomic.AtomicInteger;
22
23
24
25
26
27
28 final class InternalThreadLocalMap {
29
30 private static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap =
31 new ThreadLocal<>();
32 private static final AtomicInteger nextIndex = new AtomicInteger();
33
34 private static final int ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD = 1 << 30;
35
36 private static final int ARRAY_LIST_CAPACITY_MAX_SIZE = Integer.MAX_VALUE - 8;
37 private static final int INDEXED_VARIABLE_TABLE_INITIAL_SIZE = 32;
38
39 static final Object UNSET = new Object();
40
41
42 private Object[] indexedVariables;
43
44 private BitSet cleanerFlags;
45
46 static InternalThreadLocalMap getIfSet() {
47 Thread thread = Thread.currentThread();
48 if (thread instanceof FastThreadLocalThread) {
49 return ((FastThreadLocalThread) thread).threadLocalMap();
50 }
51 return slowThreadLocalMap.get();
52 }
53
54 static InternalThreadLocalMap get() {
55 Thread thread = Thread.currentThread();
56 if (thread instanceof FastThreadLocalThread) {
57 return fastGet((FastThreadLocalThread) thread);
58 } else {
59 return slowGet();
60 }
61 }
62
63 private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
64 InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
65 if (threadLocalMap == null) {
66 thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
67 }
68 return threadLocalMap;
69 }
70
71 private static InternalThreadLocalMap slowGet() {
72 InternalThreadLocalMap ret = slowThreadLocalMap.get();
73 if (ret == null) {
74 ret = new InternalThreadLocalMap();
75 slowThreadLocalMap.set(ret);
76 }
77 return ret;
78 }
79
80 static void remove() {
81 Thread thread = Thread.currentThread();
82 if (thread instanceof FastThreadLocalThread) {
83 ((FastThreadLocalThread) thread).setThreadLocalMap(null);
84 } else {
85 slowThreadLocalMap.remove();
86 }
87 }
88
89 static void destroy() {
90 slowThreadLocalMap.remove();
91 }
92
93 static int nextVariableIndex() {
94 int index = nextIndex.getAndIncrement();
95 if (index >= ARRAY_LIST_CAPACITY_MAX_SIZE || index < 0) {
96 nextIndex.set(ARRAY_LIST_CAPACITY_MAX_SIZE);
97 throw new IllegalStateException("too many thread-local indexed variables");
98 }
99 return index;
100 }
101
102 static int lastVariableIndex() {
103 return nextIndex.get() - 1;
104 }
105
106 private InternalThreadLocalMap() {
107 indexedVariables = newIndexedVariableTable();
108 }
109
110 private static Object[] newIndexedVariableTable() {
111 Object[] array = new Object[INDEXED_VARIABLE_TABLE_INITIAL_SIZE];
112 Arrays.fill(array, UNSET);
113 return array;
114 }
115
116 int size() {
117 int count = 0;
118 for (Object o: indexedVariables) {
119 if (o != UNSET) {
120 count ++;
121 }
122 }
123
124
125
126 return count - 1;
127 }
128
129 Object indexedVariable(int index) {
130 Object[] lookup = indexedVariables;
131 return index < lookup.length? lookup[index] : UNSET;
132 }
133
134
135
136
137 boolean setIndexedVariable(int index, Object value) {
138 Object[] lookup = indexedVariables;
139 if (index < lookup.length) {
140 Object oldValue = lookup[index];
141 lookup[index] = value;
142 return oldValue == UNSET;
143 } else {
144 expandIndexedVariableTableAndSet(index, value);
145 return true;
146 }
147 }
148
149 private void expandIndexedVariableTableAndSet(int index, Object value) {
150 Object[] oldArray = indexedVariables;
151 final int oldCapacity = oldArray.length;
152 int newCapacity;
153 if (index < ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD) {
154 newCapacity = index;
155 newCapacity |= newCapacity >>> 1;
156 newCapacity |= newCapacity >>> 2;
157 newCapacity |= newCapacity >>> 4;
158 newCapacity |= newCapacity >>> 8;
159 newCapacity |= newCapacity >>> 16;
160 newCapacity ++;
161 } else {
162 newCapacity = ARRAY_LIST_CAPACITY_MAX_SIZE;
163 }
164
165 Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
166 Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
167 newArray[index] = value;
168 indexedVariables = newArray;
169 }
170
171 Object removeIndexedVariable(int index) {
172 Object[] lookup = indexedVariables;
173 if (index < lookup.length) {
174 Object v = lookup[index];
175 lookup[index] = UNSET;
176 return v;
177 } else {
178 return UNSET;
179 }
180 }
181
182 boolean isIndexedVariableSet(int index) {
183 Object[] lookup = indexedVariables;
184 return index < lookup.length && lookup[index] != UNSET;
185 }
186
187 boolean isCleanerFlagSet(int index) {
188 return cleanerFlags != null && cleanerFlags.get(index);
189 }
190
191 void setCleanerFlag(int index) {
192 if (cleanerFlags == null) {
193 cleanerFlags = new BitSet();
194 }
195 cleanerFlags.set(index);
196 }
197 }