diff options
author | Roel Spilker <r.spilker@gmail.com> | 2014-03-17 21:56:49 +0100 |
---|---|---|
committer | Roel Spilker <r.spilker@gmail.com> | 2014-03-17 21:56:49 +0100 |
commit | 17972d59fa7e2eec6b73ba5da8234f5fa7ac2536 (patch) | |
tree | fbcc5c272480e5915b60ac0992639c083d2507d8 /src/utils/lombok/core | |
parent | 362000933f46ad1d509a659e39279298440a97ec (diff) | |
download | lombok-17972d59fa7e2eec6b73ba5da8234f5fa7ac2536.tar.gz lombok-17972d59fa7e2eec6b73ba5da8234f5fa7ac2536.tar.bz2 lombok-17972d59fa7e2eec6b73ba5da8234f5fa7ac2536.zip |
[#590][refactor] Fix for deadlock in WeakHashMap. Refactored all code that used a
WeakHashMap to fake a field.
Diffstat (limited to 'src/utils/lombok/core')
-rw-r--r-- | src/utils/lombok/core/BooleanFieldAugment.java | 185 | ||||
-rw-r--r-- | src/utils/lombok/core/ReferenceFieldAugment.java | 218 |
2 files changed, 403 insertions, 0 deletions
diff --git a/src/utils/lombok/core/BooleanFieldAugment.java b/src/utils/lombok/core/BooleanFieldAugment.java new file mode 100644 index 00000000..d843e9df --- /dev/null +++ b/src/utils/lombok/core/BooleanFieldAugment.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2014 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.core; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * Augments a instance of a type with a boolean field. + * <p> + * If the type already declares a boolean field, that field is used. Otherwise the field will be augmented. + * + * @param <T> the type to augment. + */ +public abstract class BooleanFieldAugment<T> { + + /** + * Augments a instance of a type with a boolean field. + * <p> + * If the type already declares a boolean instance field, that field might be used. Otherwise the field will be augmented. + * <p> + * This code assumes that for any combination of {@code type} and {@code name} this method is only called once. + * Otherwise, whether state is shared is undefined. + * + * @param type to augment + * @param name of the field + * @throws NullPointerException if {@code type} or {@code name} is {@code null} + */ + public static <T> BooleanFieldAugment<T> augment(Class<T> type, String name) { + checkNotNull(type, "type"); + checkNotNull(name, "name"); + Field booleanField = getBooleanField(type, name); + if (booleanField == null) { + return new MapFieldAugment<T>(); + } + return new ExistingFieldAugment<T>(booleanField); + } + + private BooleanFieldAugment() { + // prevent external instantiation + } + + private static Field getBooleanField(Class<?> type, String name) { + try { + Field result = type.getDeclaredField(name); + if (Modifier.isStatic(result.getModifiers()) || result.getType() != boolean.class) { + return null; + } + result.setAccessible(true); + return result; + } catch (Throwable t) { + return null; + } + } + + /** + * Sets the field to {@code true}. + * @returns the previous value + * @throws NullPointerException if {@code object} is {@code null} + */ + public abstract boolean set(T object); + + /** + * Sets the field to {@code false}. + * @returns the previous value + * @throws NullPointerException if {@code object} is {@code null} + */ + public abstract boolean clear(T object); + + /** + * @eturn {code true} if the field is set, otherwise {@code false}. + * @throws NullPointerException if {@code object} is {@code null} + */ + public abstract boolean get(T object); + + private static class MapFieldAugment<T> extends BooleanFieldAugment<T> { + private static final Object MARKER = new Object(); + + private final Map<T, Object> values = new WeakHashMap<T,Object>(); + + public boolean set(T object) { + checkNotNull(object, "object"); + synchronized (values) { + return values.put(object, MARKER) != null; + } + } + + public boolean clear(T object) { + checkNotNull(object, "object"); + synchronized (values) { + return values.remove(object) != null; + } + } + + public boolean get(T object) { + checkNotNull(object, "object"); + synchronized (values) { + return values.get(object) != null; + } + } + } + + private static class ExistingFieldAugment<T> extends BooleanFieldAugment<T> { + private final Object lock = new Object(); + private final Field booleanField; + + private ExistingFieldAugment(Field booleanField) { + this.booleanField = booleanField; + } + + @Override public boolean set(T object) { + checkNotNull(object, "object"); + try { + synchronized (lock) { + boolean result = booleanField.getBoolean(object); + booleanField.setBoolean(object, true); + return result; + } + } catch (IllegalAccessException e) { + throw sneakyThrow(e); + } + } + + @Override public boolean clear(T object) { + checkNotNull(object, "object"); + try { + synchronized (lock) { + boolean result = booleanField.getBoolean(object); + booleanField.setBoolean(object, false); + return result; + } + } catch (IllegalAccessException e) { + throw sneakyThrow(e); + } + } + + @Override public boolean get(T object) { + checkNotNull(object, "object"); + try { + synchronized (lock) { + return booleanField.getBoolean(object); + } + } catch (IllegalAccessException e) { + throw sneakyThrow(e); + } + } + } + + private static <T> T checkNotNull(T object, String name) { + if (object == null) throw new NullPointerException(name); + return object; + } + + private static RuntimeException sneakyThrow(Throwable t) { + if (t == null) throw new NullPointerException("t"); + BooleanFieldAugment.<RuntimeException>sneakyThrow0(t); + return null; + } + + @SuppressWarnings("unchecked") + private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T { + throw (T)t; + } +}
\ No newline at end of file diff --git a/src/utils/lombok/core/ReferenceFieldAugment.java b/src/utils/lombok/core/ReferenceFieldAugment.java new file mode 100644 index 00000000..214817a7 --- /dev/null +++ b/src/utils/lombok/core/ReferenceFieldAugment.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2014 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.core; + +import java.lang.ref.WeakReference; +import java.util.Map; +import java.util.WeakHashMap; + +public abstract class ReferenceFieldAugment<T, F> { + + /** + * Augments a instance of a type with a reference field. + * <p> + * If the type already declares an instance field with the given name and field type, that field might be used. Otherwise the field will be augmented. + * <p> + * This code assumes that for any combination of {@code type} and {@code name} this method is only called once. + * Otherwise, whether state is shared is undefined. + * + * @param type to augment + * @param fieldType type of the field + * @param name of the field + * @throws NullPointerException if {@code type}, {@code fieldType} or {@code name} is {@code null} + */ + public static <T, F> ReferenceFieldAugment<T, F> augment(Class<T> type, Class<? super F> fieldType, String name) { + return new MapFieldAugment<T, F>(); + } + + /** + * Augments a instance of a type with a weak reference field. + * <p> + * If the type already declares an instance field with the given name and field type, that field might be used. Otherwise the field will be augmented. + * <p> + * This code assumes that for any combination of {@code type} and {@code name} this method is only called once. + * Otherwise, whether state is shared is undefined. + * + * @param type to augment + * @param fieldType type of the field + * @param name of the field + * @throws NullPointerException if {@code type}, {@code fieldType} or {@code name} is {@code null} + */ + public static <T, F> ReferenceFieldAugment<T, F> augmentWeakField(Class<T> type, Class<? super F> fieldType, String name) { + return new MapWeakFieldAugment<T, F>(); + } + + private ReferenceFieldAugment() { + // prevent external instantiation + } + + /** + * @throws NullPointerException if {@code object} is {@code null} + */ + public abstract F get(T object); + + /** + * @throws NullPointerException if {@code object} or {@code expected} is {@code null} + */ + public final void set(T object, F value) { + getAndSet(object, value); + } + + /** + * @return the value of the field <strong>before</strong> the operation. + * @throws NullPointerException if {@code object} or {@code expected} is {@code null} + */ + public abstract F getAndSet(T object, F value); + + /** + * @return the value of the field <strong>before</strong> the operation. + * @throws NullPointerException if {@code object} is {@code null} + */ + public abstract F clear(T object); + + /** + * @return the value of the field <strong>after</strong> the operation. If the value was equal to {@code expected} or already cleared {@code null}, otherwise the current value. + * @throws NullPointerException if {@code object} or {@code expected} is {@code null} + */ + public abstract F compareAndClear(T object, F expected); + + /** + * @return the value of the field <strong>after</strong> the operation. + * @throws NullPointerException if {@code object} or {@code value} is {@code null} + */ + public abstract F setIfAbsent(T object, F value); + + /** + * @return the value of the field <strong>after</strong> the operation. + * @throws NullPointerException if {@code object}, {@code expected} or {@code value} is {@code null} + */ + public abstract F compareAndSet(T object, F expected, F value); + + private static class MapFieldAugment<T, F> extends ReferenceFieldAugment<T, F> { + final Map<T, Object> values = new WeakHashMap<T, Object>(); + + @Override + public F get(T object) { + checkNotNull(object, "object"); + synchronized (values) { + return read(object); + } + } + + @Override + public F getAndSet(T object, F value) { + checkNotNull(object, "object"); + checkNotNull(value, "value"); + synchronized (values) { + F result = read(object); + write(object, value); + return result; + } + } + + @Override + public F clear(T object) { + checkNotNull(object, "object"); + synchronized (values) { + F result = read(object); + values.remove(object); + return result; + } + } + + @Override + public F compareAndClear(T object, F expected) { + checkNotNull(object, "object"); + checkNotNull(expected, "expected"); + synchronized (values) { + F result = read(object); + if (result == null) { + return null; + } + if (!expected.equals(result)) { + return result; + } + values.remove(object); + return null; + } + } + + @Override + public F setIfAbsent(T object, F value) { + checkNotNull(object, "object"); + checkNotNull(value, "value"); + synchronized (values) { + F result = read(object); + if (result != null) { + return result; + } + write(object, value); + return value; + } + } + + @Override + public F compareAndSet(T object, F expected, F value) { + checkNotNull(object, "object"); + checkNotNull(expected, "expected"); + checkNotNull(value, "value"); + synchronized (values) { + F result = read(object); + if (!expected.equals(result)) { + return result; + } + write(object, value); + return value; + } + } + + @SuppressWarnings("unchecked") + F read(T object) { + return (F)values.get(object); + } + + void write(T object, F value) { + values.put(object, value); + } + } + + static class MapWeakFieldAugment<T, F> extends MapFieldAugment<T, F> { + + @SuppressWarnings("unchecked") + F read(T object) { + WeakReference<F> read = (WeakReference<F>)values.get(object); + if (read == null) return null; + F result = read.get(); + if (result == null) values.remove(object); + return result; + } + + void write(T object, F value) { + values.put(object, new WeakReference<F>(value)); + } + } + + private static <T> T checkNotNull(T object, String name) { + if (object == null) throw new NullPointerException(name); + return object; + } +} |