/* * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Version 1.0, and under the Eclipse Public License, Version 1.0 * (http://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.util; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; /** * A utility class that allows to verify access to a resource is synchronized. */ public class SynchronizedVerifier { private static volatile boolean enabled; private static final Map<Class<?>, AtomicBoolean> DETECT = Collections.synchronizedMap(new HashMap<Class<?>, AtomicBoolean>()); private static final Map<Object, Object> CURRENT = Collections.synchronizedMap(new IdentityHashMap<Object, Object>()); /** * Enable or disable detection for a given class. * * @param clazz the class * @param value the new value (true means detection is enabled) */ public static void setDetect(Class<?> clazz, boolean value) { if (value) { DETECT.put(clazz, new AtomicBoolean()); } else { AtomicBoolean b = DETECT.remove(clazz); if (b == null) { throw new AssertionError("Detection was not enabled"); } else if (!b.get()) { throw new AssertionError("No object of this class was tested"); } } enabled = DETECT.size() > 0; } /** * Verify the object is not accessed concurrently. * * @param o the object */ public static void check(Object o) { if (enabled) { detectConcurrentAccess(o); } } private static void detectConcurrentAccess(Object o) { AtomicBoolean value = DETECT.get(o.getClass()); if (value != null) { value.set(true); if (CURRENT.remove(o) != null) { throw new AssertionError("Concurrent access"); } CURRENT.put(o, o); try { Thread.sleep(1); } catch (InterruptedException e) { // ignore } Object old = CURRENT.remove(o); if (old == null) { throw new AssertionError("Concurrent access"); } } } }