blob: 845f0fca3fcf5ae734fbf790d3758195aeadb1cc [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.concurrent;
15
16import com.google.devtools.build.lib.concurrent.ThreadSafety.ConditionallyThreadCompatible;
17import com.google.devtools.build.lib.concurrent.ThreadSafety.ConditionallyThreadSafe;
18import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
19import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible;
20import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadHostile;
21import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
22
23import org.junit.Test;
24import org.junit.runner.RunWith;
25import org.junit.runners.JUnit4;
26
27import java.util.Random;
28import java.util.concurrent.atomic.AtomicInteger;
29
30/**
31 * This file just contains some examples of the use of
32 * annotations for different categories of thread safety:
33 * ThreadSafe
34 * ThreadCompatible
35 * ThreadHostile
36 * Immutable ThreadSafe
37 * Immutable ThreadHostile
38 *
39 * It doesn't really test much -- just that this code
40 * using those annotations compiles and runs.
41 *
42 * The main class here is annotated as being both ConditionallyThreadSafe
43 * and ConditionallyThreadCompatible, and accordingly we document here the
44 * conditions under which it is thread-safe and thread-compatible:
45 * - it is thread-safe if you only use the testThreadSafety() method,
46 * the ThreadSafeCounter class, and/or ImmutableThreadSafeCounter class;
47 * - it is thread-compatible if you use only those and/or the
48 * ThreadCompatibleCounter and/or ImmutableThreadCompatibleCounter class;
49 * - it is thread-hostile otherwise.
50 */
51@ConditionallyThreadSafe @ConditionallyThreadCompatible
52@RunWith(JUnit4.class)
53public class ThreadSafetyTest {
54
55 @ThreadSafe
56 public static final class ThreadSafeCounter {
57
58 // A ThreadSafe class can have public mutable fields,
59 // provided they are atomic or volatile.
60
61 public volatile boolean myBool;
62 public AtomicInteger myInt;
63
64 // A ThreadSafe class can have private mutable fields,
65 // provided that access to them is synchronized.
66 private int value;
67 public ThreadSafeCounter(int value) {
68 synchronized (this) { // is this needed?
69 this.value = value;
70 }
71 }
72 public synchronized int getValue() {
73 return value;
74 }
75 public synchronized void increment() {
76 value++;
77 }
78
79 // A ThreadSafe class can have private mutable members
80 // provided that the methods of the class synchronize access
81 // to them.
82 // These members could be static...
83 private static int numFoos = 0;
84 public static synchronized void foo() {
85 numFoos++;
86 }
87 public static synchronized int getNumFoos() {
88 return numFoos;
89 }
90 // ... or non-static.
91 private int numBars = 0;
92 public synchronized void bar() {
93 numBars++;
94 }
95 public synchronized int getNumBars() {
96 return numBars;
97 }
98 }
99
100 @ThreadCompatible
101 public static final class ThreadCompatibleCounter {
102
103 // A ThreadCompatible class can have public mutable fields.
104 public int value;
105 public ThreadCompatibleCounter(int value) {
106 this.value = value;
107 }
108 public int getValue() {
109 return value;
110 }
111 public void increment() {
112 value++;
113 }
114
115 // A ThreadCompatible class can have mutable static members
116 // provided that the methods of the class synchronize access
117 // to them.
118 private static int numFoos = 0;
119 public static synchronized void foo() {
120 numFoos++;
121 }
122 public static synchronized int getNumFoos() {
123 return numFoos;
124 }
125 }
126
127 @ThreadHostile
128 public static final class ThreadHostileCounter {
129
130 // A ThreadHostile class can have public mutable fields.
131 public int value;
132 public ThreadHostileCounter(int value) {
133 this.value = value;
134 }
135 public int getValue() {
136 return value;
137 }
138 public void increment() {
139 value++;
140 }
141
142 // A ThreadHostile class can perform unsynchronized access
143 // to mutable static data.
144 private static int numFoos = 0;
145 public static void foo() {
146 numFoos++;
147 }
148 public static int getNumFoos() {
149 return numFoos;
150 }
151 }
152
153 @Immutable @ThreadSafe
154 public static final class ImmutableThreadSafeCounter {
155
156 // An Immutable ThreadSafe class can have public fields,
157 // provided they are final and immutable.
158 public final int value;
159 public ImmutableThreadSafeCounter(int value) {
160 this.value = value;
161 }
162 public int getValue() {
163 return value;
164 }
165 public ImmutableThreadSafeCounter increment() {
166 return new ImmutableThreadSafeCounter(value + 1);
167 }
168
169 // An Immutable ThreadSafe class can have immutable static members.
170 public static final int NUM_STATIC_CACHE_ENTRIES = 3;
171 private static final ImmutableThreadSafeCounter[] staticCache =
172 new ImmutableThreadSafeCounter[] {
173 new ImmutableThreadSafeCounter(0),
174 new ImmutableThreadSafeCounter(1),
175 new ImmutableThreadSafeCounter(2)
176 };
177 public static ImmutableThreadSafeCounter makeUsingStaticCache(int value) {
178 if (value < NUM_STATIC_CACHE_ENTRIES) {
179 return staticCache[value];
180 } else {
181 return new ImmutableThreadSafeCounter(value);
182 }
183 }
184
185 // An Immutable ThreadSafe class can have private mutable members
186 // provided that the methods of the class synchronize access
187 // to them.
188 // These members could be static...
189 private static int cachedValue = 0;
190 private static ImmutableThreadSafeCounter cachedCounter =
191 new ImmutableThreadSafeCounter(0);
192 public static synchronized ImmutableThreadSafeCounter
193 makeUsingDynamicCache(int value) {
194 if (value != cachedValue) {
195 cachedValue = value;
196 cachedCounter = new ImmutableThreadSafeCounter(value);
197 }
198 return cachedCounter;
199 }
200 // ... or non-static.
201 private ImmutableThreadSafeCounter incrementCache = null;
202 public synchronized ImmutableThreadSafeCounter incrementUsingCache() {
203 if (incrementCache == null) {
204 incrementCache = new ImmutableThreadSafeCounter(value + 1);
205 }
206 return incrementCache;
207 }
208 // Methods of an Immutable class need not be deterministic.
209 private static Random random = new Random();
210 public int choose() {
211 return random.nextInt(value);
212 }
213 }
214
215 @Immutable @ThreadHostile
216 public static final class ImmutableThreadHostileCounter {
217
218 // An Immutable ThreadHostile class can have public fields,
219 // provided they are final and immutable.
220 public final int value;
221 public ImmutableThreadHostileCounter(int value) {
222 this.value = value;
223 }
224 public int getValue() {
225 return value;
226 }
227 public ImmutableThreadHostileCounter increment() {
228 return new ImmutableThreadHostileCounter(value + 1);
229 }
230
231 // An Immutable ThreadHostile class can have private mutable members,
232 // and doesn't need to synchronize access to them.
233 // These members could be static...
234 private static int cachedValue = 0;
235 private static ImmutableThreadHostileCounter cachedCounter =
236 new ImmutableThreadHostileCounter(0);
237 public static ImmutableThreadHostileCounter
238 makeUsingDynamicCache(int value) {
239 if (value != cachedValue) {
240 cachedValue = value;
241 cachedCounter = new ImmutableThreadHostileCounter(value);
242 }
243 return cachedCounter;
244 }
245 // ... or non-static.
246 private ImmutableThreadHostileCounter incrementCache = null;
247 public ImmutableThreadHostileCounter incrementUsingCache() {
248 if (incrementCache == null) {
249 incrementCache = new ImmutableThreadHostileCounter(value + 1);
250 }
251 return incrementCache;
252 }
253 }
254
255 @Test
256 public void threadSafety() throws InterruptedException {
257 final ThreadSafeCounter threadSafeCounterArray[] =
258 new ThreadSafeCounter[] {
259 new ThreadSafeCounter(1),
260 new ThreadSafeCounter(2),
261 new ThreadSafeCounter(3)
262 };
263 final ThreadCompatibleCounter threadCompatibleCounterArray[] =
264 new ThreadCompatibleCounter[] {
265 new ThreadCompatibleCounter(1),
266 new ThreadCompatibleCounter(2),
267 new ThreadCompatibleCounter(3)
268 };
269 final ThreadHostileCounter threadHostileCounter =
270 new ThreadHostileCounter(1);
271
272 class MyThread implements Runnable {
273
274 ThreadCompatibleCounter threadCompatibleCounter =
275 new ThreadCompatibleCounter(1);
276
277 @Override
278 public void run() {
279
280 // ThreadSafe objects can be accessed with without synchronization
281 for (ThreadSafeCounter counter : threadSafeCounterArray) {
282 counter.increment();
283 }
284
285 // ThreadCompatible objects can be accessed with without
286 // synchronization if they are thread-local
287 threadCompatibleCounter.increment();
288
289 // Access to ThreadCompatible objects must be synchronized
290 // if they could be concurrently accessed by other threads
291 for (ThreadCompatibleCounter counter : threadCompatibleCounterArray) {
292 synchronized (counter) {
293 counter.increment();
294 }
295 }
296
297 // Access to ThreadHostile objects must be synchronized.
298 synchronized (this.getClass()) {
299 threadHostileCounter.increment();
300 }
301
302 }
303 }
304
305 Thread thread1 = new Thread(new MyThread());
306 Thread thread2 = new Thread(new MyThread());
307 thread1.start();
308 thread2.start();
309 thread1.join();
310 thread2.join();
311 }
312
313}