fix exceptions when reloading the server (#1689)

This commit is contained in:
Pasqual Koschmieder 2022-06-30 01:08:14 +02:00 committed by GitHub
parent a2bf242097
commit 4f18d37832
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 130 additions and 46 deletions

View File

@ -4,10 +4,14 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
@SuppressWarnings("NullableProblems") @SuppressWarnings("NullableProblems")
final class ListeningList implements List<Object> { final class ListeningList implements List<Object> {
@ -25,17 +29,23 @@ final class ListeningList implements List<Object> {
@Override @Override
public int size() { public int size() {
return this.original.size(); synchronized (this) {
return this.original.size();
}
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return this.original.isEmpty(); synchronized (this) {
return this.original.isEmpty();
}
} }
@Override @Override
public boolean contains(Object o) { public boolean contains(Object o) {
return this.original.contains(o); synchronized (this) {
return this.original.contains(o);
}
} }
@Override @Override
@ -43,81 +53,113 @@ final class ListeningList implements List<Object> {
return this.original.iterator(); return this.original.iterator();
} }
@Override
public void forEach(Consumer<? super Object> action) {
synchronized (this) {
this.original.forEach(action);
}
}
@Override @Override
public Object[] toArray() { public Object[] toArray() {
return this.original.toArray(); synchronized (this) {
return this.original.toArray();
}
} }
@Override @Override
public <T> T[] toArray(T[] a) { public <T> T[] toArray(T[] a) {
//noinspection SuspiciousToArrayCall synchronized (this) {
return this.original.toArray(a); //noinspection SuspiciousToArrayCall
return this.original.toArray(a);
}
} }
@Override @Override
public boolean add(Object o) { public boolean add(Object o) {
this.processInsert(o); synchronized (this) {
return this.original.add(o); this.processInsert(o);
return this.original.add(o);
}
} }
@Override @Override
public void add(int index, Object element) { public void add(int index, Object element) {
this.processInsert(element); synchronized (this) {
this.original.add(index, element); this.processInsert(element);
this.original.add(index, element);
}
} }
@Override @Override
public boolean addAll(Collection<?> c) { public boolean addAll(Collection<?> c) {
c.forEach(this::processInsert); synchronized (this) {
return this.original.addAll(c); c.forEach(this::processInsert);
return this.original.addAll(c);
}
} }
@Override @Override
public boolean addAll(int index, Collection<?> c) { public boolean addAll(int index, Collection<?> c) {
c.forEach(this::processInsert); synchronized (this) {
return this.original.addAll(index, c); c.forEach(this::processInsert);
return this.original.addAll(index, c);
}
} }
@Override @Override
public Object set(int index, Object element) { public Object set(int index, Object element) {
this.processInsert(element); synchronized (this) {
this.processInsert(element);
Object prev = this.original.set(index, element); Object prev = this.original.set(index, element);
this.processRemove(prev); this.processRemove(prev);
return prev; return prev;
}
} }
@Override @Override
public boolean remove(Object o) { public boolean remove(Object o) {
if (this.original.remove(o)) { synchronized (this) {
this.processRemove(o); if (this.original.remove(o)) {
return true; this.processRemove(o);
} return true;
}
return false; return false;
}
} }
@Override @Override
public boolean containsAll(Collection<?> c) { public boolean containsAll(Collection<?> c) {
return this.original.containsAll(c); synchronized (this) {
//noinspection SlowListContainsAll
return this.original.containsAll(c);
}
} }
@Override @Override
public Object remove(int index) { public Object remove(int index) {
Object removed = this.original.remove(index); synchronized (this) {
this.processRemove(removed); Object removed = this.original.remove(index);
return removed; this.processRemove(removed);
return removed;
}
} }
@Override @Override
public int indexOf(Object o) { public int indexOf(Object o) {
return this.original.indexOf(o); synchronized (this) {
return this.original.indexOf(o);
}
} }
@Override @Override
public int lastIndexOf(Object o) { public int lastIndexOf(Object o) {
return this.original.lastIndexOf(o); synchronized (this) {
return this.original.lastIndexOf(o);
}
} }
@Override @Override
@ -132,33 +174,77 @@ final class ListeningList implements List<Object> {
@Override @Override
public List<Object> subList(int fromIndex, int toIndex) { public List<Object> subList(int fromIndex, int toIndex) {
return this.original.subList(fromIndex, toIndex); synchronized (this) {
return this.original.subList(fromIndex, toIndex);
}
} }
@Override @Override
public boolean removeAll(Collection<?> c) { public boolean removeAll(Collection<?> c) {
c.forEach(element -> { synchronized (this) {
if (this.original.contains(element)) { c.forEach(element -> {
this.processRemove(element); if (this.original.contains(element)) {
} this.processRemove(element);
}); }
return this.original.removeAll(c); });
return this.original.removeAll(c);
}
}
@Override
public boolean removeIf(Predicate<? super Object> filter) {
synchronized (this) {
return this.original.removeIf(object -> {
boolean shouldRemove = filter.test(object);
if (shouldRemove) {
this.processRemove(object);
}
return shouldRemove;
});
}
} }
@Override @Override
public boolean retainAll(Collection<?> c) { public boolean retainAll(Collection<?> c) {
return this.original.retainAll(c); synchronized (this) {
return this.original.retainAll(c);
}
}
@Override
public void replaceAll(UnaryOperator<Object> operator) {
synchronized (this) {
this.original.replaceAll(value -> {
Object newValue = operator.apply(value);
if (newValue != value) {
this.processRemove(value);
this.processInsert(newValue);
}
return newValue;
});
}
}
@Override
public void sort(Comparator<? super Object> c) {
synchronized (this) {
this.original.sort(c);
}
} }
@Override @Override
public void clear() { public void clear() {
this.original.forEach(this::processRemove); synchronized (this) {
this.original.clear(); this.original.forEach(this::processRemove);
this.original.clear();
}
} }
@Override @Override
public Object get(int index) { public Object get(int index) {
return this.original.get(index); synchronized (this) {
return this.original.get(index);
}
} }
private void processInsert(Object element) { private void processInsert(Object element) {

View File

@ -26,7 +26,6 @@ import io.netty.channel.ChannelFuture;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -205,7 +204,7 @@ public class NetworkManagerInjector implements ChannelListener {
//noinspection SynchronizationOnLocalVariableOrMethodParameter //noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (value) { synchronized (value) {
// override the list field with our list // override the list field with our list
List<Object> newList = Collections.synchronizedList(new ListeningList(value, this.pipelineInjectorHandler)); List<Object> newList = new ListeningList(value, this.pipelineInjectorHandler);
accessor.set(serverConnection, newList); accessor.set(serverConnection, newList);
} }
} }
@ -215,7 +214,6 @@ public class NetworkManagerInjector implements ChannelListener {
this.injected = true; this.injected = true;
} }
@SuppressWarnings("unchecked")
public void close() { public void close() {
if (this.closed || !this.injected) { if (this.closed || !this.injected) {
return; return;
@ -229,10 +227,10 @@ public class NetworkManagerInjector implements ChannelListener {
for (Pair<Object, FieldAccessor> list : this.overriddenLists) { for (Pair<Object, FieldAccessor> list : this.overriddenLists) {
// get the value of the field we've overridden, if it is no longer a ListeningList someone probably jumped in // get the value of the field we've overridden, if it is no longer a ListeningList someone probably jumped in
// and replaced the field himself - we are out safely as the other person needs to clean the mess... // and replaced the field himself - we are out safely as the other person needs to clean the mess...
List<Object> value = (List<Object>) list.getSecond().get(list.getFirst()); Object currentFieldValue = list.getSecond().get(list.getFirst());
if (value instanceof ListeningList) { if (currentFieldValue instanceof ListeningList) {
// just reset to the list we wrapped originally // just reset to the list we wrapped originally
ListeningList ourList = (ListeningList) value; ListeningList ourList = (ListeningList) currentFieldValue;
List<Object> original = ourList.getOriginal(); List<Object> original = ourList.getOriginal();
//noinspection SynchronizationOnLocalVariableOrMethodParameter //noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (original) { synchronized (original) {