1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.util; |
7 | |
8 | import java.util.Collections; |
9 | import java.util.HashMap; |
10 | import java.util.IdentityHashMap; |
11 | import java.util.Map; |
12 | import java.util.concurrent.atomic.AtomicBoolean; |
13 | |
14 | /** |
15 | * A utility class that allows to verify access to a resource is synchronized. |
16 | */ |
17 | public class SynchronizedVerifier { |
18 | |
19 | private static volatile boolean enabled; |
20 | private static final Map<Class<?>, AtomicBoolean> DETECT = |
21 | Collections.synchronizedMap(new HashMap<Class<?>, AtomicBoolean>()); |
22 | private static final Map<Object, Object> CURRENT = |
23 | Collections.synchronizedMap(new IdentityHashMap<Object, Object>()); |
24 | |
25 | /** |
26 | * Enable or disable detection for a given class. |
27 | * |
28 | * @param clazz the class |
29 | * @param value the new value (true means detection is enabled) |
30 | */ |
31 | public static void setDetect(Class<?> clazz, boolean value) { |
32 | if (value) { |
33 | DETECT.put(clazz, new AtomicBoolean()); |
34 | } else { |
35 | AtomicBoolean b = DETECT.remove(clazz); |
36 | if (b == null) { |
37 | throw new AssertionError("Detection was not enabled"); |
38 | } else if (!b.get()) { |
39 | throw new AssertionError("No object of this class was tested"); |
40 | } |
41 | } |
42 | enabled = DETECT.size() > 0; |
43 | } |
44 | |
45 | /** |
46 | * Verify the object is not accessed concurrently. |
47 | * |
48 | * @param o the object |
49 | */ |
50 | public static void check(Object o) { |
51 | if (enabled) { |
52 | detectConcurrentAccess(o); |
53 | } |
54 | } |
55 | |
56 | private static void detectConcurrentAccess(Object o) { |
57 | AtomicBoolean value = DETECT.get(o.getClass()); |
58 | if (value != null) { |
59 | value.set(true); |
60 | if (CURRENT.remove(o) != null) { |
61 | throw new AssertionError("Concurrent access"); |
62 | } |
63 | CURRENT.put(o, o); |
64 | try { |
65 | Thread.sleep(1); |
66 | } catch (InterruptedException e) { |
67 | // ignore |
68 | } |
69 | Object old = CURRENT.remove(o); |
70 | if (old == null) { |
71 | throw new AssertionError("Concurrent access"); |
72 | } |
73 | } |
74 | } |
75 | |
76 | } |